# <img src="website/static/img/logo.png" width="27" /> Contributing to Functionless <img src="website/static/img/logo.png" width="27" />

Thank you for your interest and contribution in Functionless.

## Getting Started

Requirements:

- Node 14+
- Yarn
- Docker
  - Used in the tests. Docker daemon must be running.
- Localstack
  - Used in the tests.

Fork the repo and then run:

```console
$ git clone https://github.com/{your-account}/functionless.git
$ cd functionless
$ yarn install
```

We do all of our work in [`VSCode`](https://code.visualstudio.com/) and include configuration files to improve the developer experience. There is no strict dependency. Suggested plugins: vscode-jest, prettier-vscode, streetsidesoftware.code-spell-checker.

### Layout

- `/` and `/src` - The Functionless Package - containing compiler (`src/compiler.ts`), transpilers (`src/vtl.ts`), and publicly exposed resources (`Function`, `StepFunction`, etc). Generates the [functionless](https://www.npmjs.com/package/functionless) NPM repo.
- `/test` - Tests for the main repo, see [testing](#testing)
- `/website` - Functionless.org code
- `/test-app` - A series of test CDK applications which consume `functionless` via npm. We use this to test compilation of various "real world" scenarios and CDK synthesis.

Functionless leverages the [typescript compiler api](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API). `compile.ts` runs over the ts of a consuming application, validating, transforming, and extracting necessary information.

> Note: Functionless does not compile itself, at the moment, functions like `reflect` cannot be used to transform the core functionless source. `/test`s are compiled.

The compiler outputs the functionless AST (`/src/expression.ts`, `/src/declaration.ts`, `/src/statement.ts`) into the transpiled javascript which is used by the resources (`/src/function.ts`, `/src/step-function.ts`, etc) during **cdk synthesis** to infer infrastructure (CDK Constructs) and transpile typescript/Functionless AST to cloud native languages (ASL, Velocity).

## Building Functionless

```console
$ yarn install
$ yarn watch
```

### Guidelines

- Every change or bug fix MUST come with one or more tests that would break before the change.
- Errors MUST be documented as [Error Codes](#error-codes) with detailed descriptions.
- Errors SHOULD be discovered via `/src/validate.ts`. `/src/validate.ts` runs with the `functionless` cli and the [`@functionless/language-service`](https://github.com/functionless/functionless-language-service) in the IDE to give early errors to consumers.
- No/Minimal Magic - Core Functionless resources should improve the developer experience without introducing opinionated implementation. The goal is to translate typescript behavior into AWS services. The infrastructure created should be clear and logical. For example, `Step Functions` cannot perform arithmetic natively, we will not automatically create/call a lambda to do it. We will validate for arithmetic, throw an error, and show how a `Function` can be used as a workaround. We may introduce higher level constructs or integrations that accomplish the "magic" behavior.

### Project Configuration Changes

Functionless uses [`projen`](https://projen.io/), desired state project definitions. Most configuration files in the main `Functionless` package are generated by `projen` and should not be updated manually (`package.json`, `prettierrc.json`, `tsconfig.json`, etc).

To make changes, update the `projen.js` file and run `yarn default`.

The sub-packages (ex: website, test-app) do not currently use projen ([TODO: mono repo](https://github.com/functionless/functionless/issues/258)), their package configuration can be updated manually.

### Testing

Required:

- Docker
  - With the docker daemon running.
- [Localstack](https://docs.localstack.cloud/get-started/#localstack-cli)

Commands:

- `yarn test` - boots up localstack, runs all tests, stops localstack
- `yarn test:fast` - runs all non-localstack tests
- `yarn test:update` - runs all tests and updates snapshots
- `yarn test:watch` - watches for changes to the source and re-runs the tests.

Suggested: VSCode jest plugin - use the VSCode jest plugin to run individual tests and debug.

When using any method that does not invoke `yarn test`, `./script/localstack` may be used to start `localstack` (and boostrap CDK) manually.

### Testing compiler changes

The compiler transforms the consumer's typescript. This output can be seen by looking at the Javascript files output by Typescript. By default, `jest` caches this output, which means your compiler changes may not apply to the next test run. If making changes to the compiler, you'll want to either make changes to your test source to force the cache to refresh, explicitly clear the jest cache `npx jest --clearCache`, or run without the cache `npx jest --cache false`.

Compiler code can be debugged, however, ensure that `jest` actually runs the compiler by using one of the cache clearing mechanisms above. When testing the compiler, all of the uncached typescript files will be processed, making it difficult to debug specific tests. One strategy is to isolate your test in it's own file temporarily and continually make small changes (like updating the name), to break the cache.

Another option to test compiler changes is using the includes VSCode Launch Configuration `compile test-app`. This will explicitly compile the `/test-app` and supports the debugger.

### Test App

```console
$ cd test-app
test-app$ yarn install
test-app$ yarn watch
```

### Website

The doc site contains marketing, documentation, and blogs on `Functionless.org`. It is built using [Docusaurus v2](https://docusaurus.io/).

- `/website/docs/api/*` - Auto generated by [typedocs](https://www.npmjs.com/package/docusaurus-plugin-typedoc). Generation happens on `yarn start` or via netlify when a PR is pushed.
- `/website/docs/error-codes.md` - Auto generated by [typedocs](https://www.npmjs.com/package/docusaurus-plugin-typedoc) and the `/scripts/compile-error-code-page.js` script. Generation happens on `yarn start` or via netlify when a PR is pushed.
- `/website/docs/concepts` - Hand written documentation for functionless resources. Update as needed.

To watch during development:

```console
$ cd website
website$ yarn start
```

To simulate the netlify build, run (at the root directory)

```console
$ yarn build:website
```

## Additions

### Error Codes

Functionless maintains well documented and linked error codes for compiler and synth errors.

- Compiler Errors - Errors in the source discovered during the compile/transform phase.
  - Note: the compiler should never throw all of the way up the stack. Use the `errorBoundary` method to catch errors and turn them into `Err` AST nodes. `Err` nodes should be checked for when transpiling where they can be re-thrown.
  - The validator (`/src/validate.ts`) and language service (`@functionless/language-service`) should be used to expose compile time errors to the consumer.
- Synth Errors - Errors encountered while generating the infrastructure in CDK Synth.

#### Error Code Strategy

https://github.com/functionless/functionless/issues/182#issuecomment-1155683168

TLDR; keep it simple and follow Typescript/React

1.  Error Codes - Start at 10000
2.  Error Message/Title
3.  Type - Error, Warning, Info, Deprecated

All errors should be functionless(code) - If the error pertains to a specific service, the message should include that. If an error code is no longer relevant, we do not re-use the code, change the type to deprecated and stop returning the error. At major version bumps, we will clean up the deprecated errors and re-evaluate.

#### Add a new Error Code

1. Add a new exported constant in the `src/error_codes.ts` file.

````ts
/**
 * Error thrown when doing something bad.
 * ```ts
 * some code showing valid and invalid cases
 * ```
 *
 * ### More context
 *
 * This is why this error happens, tickets that may resolve the issue, context links, aws/cloud/cdk limitations.
 *
 * ### Workaround
 *
 * As an alternative, do this
 *
 * ```ts
 * // something that achieves the behavior or close to it
 * ```
 **/
export const My_New_Error: ErrorCode {
	type: ErrorType.ERROR,
	title: "my new error",
	code: 19999
}
````

**type** -

- <span style="background-color: red; padding: 4px;">ERROR</span> - Should fail validation, cli. An error signifies that consumer logic will fail or during compile, synth, or runtime, generally due to an explicit error from Functionless.
- <span style="background-color: yellow; color: black; padding: 4px;">WARN</span> - Should show warning during validation, cli. A warning signifies that consumer logic MAY fail or act in undefined ways during compile, synth, or runtime
- <span style="background-color: blue; padding: 4px;">INFO</span> - Show info during validate. Will not fail during compile, synth, or runtime.
- <span style="background-color: grey; padding: 4px;">DEPRECATED</span> - Errors or warnings that are no longer applicable. (We unblocked the use case, for example).

**Title** - A short, single sentence with no punctuation describing what the error is. The title of the error on the [Error Code page](https://functionless.org/docs/error-codes). The anchor created on the url. The message text printed by default with a `SynthError`.

**Code** - Unique, numeric code, starting at 10000. Will be displayed in the IDE and website to uniquely identify the error even if the message text changes.

**TSDocs** - Above the ErrorCode, TS docs should be used to describe the error in detail. This can contain markdown, which will be turned into markdown in the docs. Include code examples of what not to do is possible, go into detail on the issue, and explain any workarounds or escape hatching available to bypass the issue.

2. [Optional] - Add validation

Any ErrorCode which can be determined statically should have static validation added to `validate.ts`. `validate.ts` uses the Typescript AST to find errors which would be thrown later. `validate.ts` is used by the `functionless` api (`npx functionless`) and the [`@functionless/language-service`](https://github.com/functionless/functionless-language-service).

> If the error code cannot be validated, add the new error code to `skipErrorCodes` in `validate.test.ts`. You must explain why validation cannot be added in the PR and comments.

3. [Optional] - Add a validation test

If step 2 created validation, add an error code to the corresponding `test/test-files` file(s) and run the tests to recreate the snapshot. The snapshot should show new errors for each test case added.

> In the rare case that validation is added, but cannot be re-created in the `test/test-files`, add the new error code to `skipErrorCodes` in `validate.test.ts`. You must explain why validation cannot be added in the PR and comments.

## PR

Create a PR from your fork to `functionless` `main`.

Review [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for details on how to submit a pull request.

> Title: `feat/fix/chore(feature): lower case concise description`
> Title Example: `feat(lambda): support event sources`

Feature names are optional and should come from the [repo labels](https://github.com/functionless/functionless/labels).

Breaking Changes:

Breaking changes will force a version bump (major version bump after version 1.0).

To declare a breaking change add:

> BREAKING_CHANGE: ...

To the bottom of your PR description. When we merge the PR into `main`, the BREAKING_CHANGE will be retained in the squash commit message.

> Note: PRs should not use force pushes, always push new commits to the PR branch, including periodic merges from live as `main` is updated.

## Troubleshooting

### Function Not Compiled

[Error 10001 - Function not compiled]https://functionless.org/docs/error-codes#function-not-compiled-by-functionless-plugin

The SWC plugin isn't running on your code. See the [Getting Started](./website/docs/getting-started.md#add-to-an-existing-cdk-project) documentation for steps on how to properly configure SWC.
