FormidableLabs / FormidableLabs/serverless-jetpack

Feature: Automagically include other handlers that reuse package in `package.artifact` in trace mode.

Open
#108 0 comments 0 reactions 0 assignees View on GitHub
enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.