├── .github
└── workflows
│ ├── ci.yml
│ └── codeql-analysis.yml
├── .gitignore
├── CONTRIBUTING.md
├── README.md
├── bin
├── auto-cdk
└── auto-cdk.ts
├── cli
├── build.ts
└── dev.ts
├── docs
├── CLI.md
└── examples
│ ├── api-with-existing-cdk-app
│ ├── api
│ │ └── index.ts
│ ├── app.ts
│ ├── cdk.json
│ └── package.json
│ ├── api-with-greedy-proxy
│ ├── README.md
│ ├── api
│ │ └── {proxy+}
│ │ │ └── index.ts
│ └── package.json
│ ├── api-with-parametized-path
│ ├── api
│ │ └── {id}
│ │ │ └── index.ts
│ └── package.json
│ └── api-with-single-handler-all-methods
│ ├── .gitignore
│ ├── README.md
│ ├── api
│ ├── index.ts
│ ├── posts
│ │ └── index.ts
│ └── users
│ │ └── index.ts
│ ├── package.json
│ └── sample-template.json
├── lib
├── autocdk.ts
├── build
│ ├── index.ts
│ └── webpack
│ │ ├── compiler.ts
│ │ ├── config.ts
│ │ └── entrypoints.ts
├── config.ts
├── index.ts
├── resources.ts
├── routes.ts
└── utils.ts
├── package.json
├── test
├── autocdk.test.ts
├── build
│ └── webpack
│ │ └── entries.test.ts
├── mock
│ └── api
│ │ ├── another
│ │ └── test.ts
│ │ ├── index.ts
│ │ └── {id}
│ │ ├── index.ts
│ │ └── settings.ts
├── routes.test.ts
└── utils.test.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CICD
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Setup Node
13 | uses: actions/setup-node@v1
14 | with:
15 | node-version: 12.x
16 | registry-url: 'https://registry.npmjs.org'
17 | - run: npm i -g yarn
18 | - run: yarn
19 | - run: yarn test
20 | - run: yarn lint
21 | - run: yarn build
22 | - run: yarn publish
23 | env:
24 | NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
25 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "Code scanning - action"
2 |
3 | on:
4 | push:
5 | branches: [master, ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [master]
9 | schedule:
10 | - cron: '0 20 * * 3'
11 |
12 | jobs:
13 | CodeQL-Build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v2
20 | with:
21 | # We must fetch at least the immediate parents so that if this is
22 | # a pull request then we can checkout the head.
23 | fetch-depth: 2
24 |
25 | # If this run was triggered by a pull request event, then checkout
26 | # the head of the pull request instead of the merge commit.
27 | - run: git checkout HEAD^2
28 | if: ${{ github.event_name == 'pull_request' }}
29 |
30 | # Initializes the CodeQL tools for scanning.
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v1
33 | # Override language selection by uncommenting this and choosing your languages
34 | # with:
35 | # languages: go, javascript, csharp, python, cpp, java
36 |
37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
38 | # If this step fails, then you should remove it and run the build manually (see below)
39 | - name: Autobuild
40 | uses: github/codeql-action/autobuild@v1
41 |
42 | # ℹ️ Command-line programs to run using the OS shell.
43 | # 📚 https://git.io/JvXDl
44 |
45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
46 | # and modify them (or add more) to build your code if your project
47 | # uses a compiled language
48 |
49 | #- run: |
50 | # make bootstrap
51 | # make release
52 |
53 | - name: Perform CodeQL Analysis
54 | uses: github/codeql-action/analyze@v1
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .DS_Store
3 | */**/*.js
4 | node_modules
5 | */**/*.d.ts
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thank you for your interest in contributing!
4 |
5 | The below document describes how to build and test the project and submit your contributions.
6 |
7 | * [Getting Started](#getting-started)
8 | * [Pull Requests](#pull-requests)
9 | * [Tools](#tools)
10 | * [Troubleshooting](#troubleshooting)
11 |
12 | ## Getting Started
13 |
14 | The basic commands to get the repository cloned and built locally follow:
15 |
16 | ```bash
17 | $ git clone https://github.com/wulfmann/auto-cdk.git
18 | $ cd auto-cdk
19 | $ yarn install
20 | $ yarn build
21 | ```
22 |
23 | ### Linking
24 |
25 | Since a lot of the functionality depends on the filesystem structure, it's usually easier to [link the project](https://classic.yarnpkg.com/en/docs/cli/link/)locally so that you can iterate while using it in a spearate place.
26 |
27 | You can do this by running:
28 |
29 | ```bash
30 | $ yarn link
31 | ```
32 |
33 | Now you can create a separate node project somewhere else by running the following in a new directory and following the prompts:
34 |
35 | ```bash
36 | $ yarn init
37 | $ yarn add auto-cdk
38 | $ yarn link auto-cdk
39 | ```
40 |
41 | Now changes you make to `auto-cdk` will reflect in the new project. It is recommended that you run `auto-cdk` in [watch mode](#watch-mode) while developing.
42 |
43 | To unlink later you can run:
44 |
45 | ```bash
46 | $ yarn unlink auto-cdk
47 | ```
48 |
49 | ### Watching
50 |
51 | You can enable live-compiling by running:
52 |
53 | ```bash
54 | $ yarn watch
55 | ```
56 |
57 | ## Pull Requests
58 |
59 | ### Open Issue
60 |
61 | ### Add your Changss
62 |
63 | ### Tests
64 |
65 | ### Commit
66 |
67 | ### Pull Request
68 |
69 | ### Merge
70 |
71 | ## Tools
72 |
73 | This section describes some of the tools this project uses for building, testing and maintaining code quality.
74 |
75 | ### Linting
76 |
77 | This project uses [TSLint](https://palantir.github.io/tslint/) for code linting.
78 |
79 | To check if any files have bad formatting run:
80 |
81 | ```bash
82 | $ yarn lint
83 | ```
84 |
85 | To auto-fix linting issues (ones that can anyways) run:
86 |
87 | ```bash
88 | $ yarn lint --fix
89 | ```
90 |
91 | ### Formatting
92 |
93 | This project uses [Prettier](https://prettier.io) for code formatting.
94 |
95 | To check if any files have bad formatting run:
96 |
97 | ```bash
98 | $ yarn format --check
99 | ```
100 |
101 | To allow the formatter to modify files run:
102 |
103 | ```bash
104 | $ yarn format --write
105 | ```
106 |
107 | ### Testing
108 |
109 | This project uses [Jest](https://jestjs.io) for testing.
110 |
111 | To run the tests, run the following:
112 |
113 | ```bash
114 | $ yarn test
115 | ```
116 |
117 | ## Troubleshooting
118 |
119 | Ensure that you can reproduce your issue after doing a complete rebuild:
120 |
121 | ```bash
122 | $ git clean -fqdx .
123 | $ yarn build
124 | ```
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Auto CDK
2 |
3 | `auto-cdk` lets you generate an api gateway with lambda integrations based on the filesystem. It makes use of [AWS CDK](https://aws.amazon.com/cdk/) to generate cloudformation stacks, and [Webpack](https://webpack.js.org) for bundling and code-splitting.
4 |
5 | **Caveats**
6 |
7 | Currently this project only aims to build and package node/typescript-based integrations. It is on the roadmap to support more, but will not be available until a later version.
8 |
9 | ## Quickstart
10 |
11 | ```bash
12 | yarn add auto-cdk
13 | ```
14 |
15 | Create an `api` directory and add a file to it that exports a function named `handler`:
16 |
17 | ```bash
18 | $ mkdir api && touch api/index.ts
19 | ```
20 |
21 | ```js
22 | // api/index.ts
23 |
24 | exports.handler = (event, ctx) => {
25 | return {
26 | statusCode: 200,
27 | body: 'hello world',
28 | headers: {
29 | 'Content-Type': 'text/html'
30 | }
31 | }
32 | }
33 | ```
34 |
35 | Run:
36 |
37 | ```bash
38 | $ yarn dev
39 | ```
40 |
41 | You should now have webpack auto-compiling when your source changes, and a cdk stack that has been generated in `cdk.out`.
42 |
43 | ### Bonus
44 |
45 | If you install [AWS sam-cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html), you can run the api on localhost with the following:
46 |
47 | ```bash
48 | $ sam local start-api cdk.out/*.template.json
49 | ```
50 |
51 | View more examples [here](/docs/examples).
52 |
53 | ## Features
54 |
55 | * Automatic CDK Stack Generation
56 | * Code-Splitting and Bundling with Webpack
57 | * Out of the box typescript support
58 |
59 | ## Contributing
60 |
61 | If you would like to make a contribution or learn more about running this project locally, please review the [Contributing Documentation](/CONTRIBUTING.md).
--------------------------------------------------------------------------------
/bin/auto-cdk:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('./auto-cdk.js')
3 |
--------------------------------------------------------------------------------
/bin/auto-cdk.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import * as arg from 'arg';
3 |
4 | const defaultCommand = 'dev';
5 |
6 | export type Command = (argv?: string[]) => void;
7 |
8 | export type CommandMap = {
9 | [command: string]: () => Promise
10 | }
11 |
12 | const commands: CommandMap = {
13 | build: async () => await import('../cli/build').then((i) => i.build),
14 | dev: async () => await import('../cli/dev').then((i) => i.dev)
15 | };
16 |
17 | const argMap = {
18 | // Types
19 | '--help': Boolean,
20 | '--version': Boolean,
21 | '--verbose': arg.COUNT,
22 |
23 | // Aliases
24 | '-v': '--verbose'
25 | };
26 |
27 | const args = arg(argMap);
28 |
29 | if (args['--version']) {
30 | console.log(`auto-cdk v`);
31 | process.exit(0);
32 | }
33 |
34 | const foundCommand = Boolean(commands[args._[0]]);
35 | const command = foundCommand ? args._[0] : defaultCommand;
36 | const forwardedArgs = foundCommand ? args._.slice(1) : args._;
37 |
38 | if (args['--help']) {
39 | forwardedArgs.push('--help')
40 | }
41 |
42 | commands[command]().then((exec) => exec(forwardedArgs));
--------------------------------------------------------------------------------
/cli/build.ts:
--------------------------------------------------------------------------------
1 | import { basename } from 'path';
2 | import { AutoCdk } from '../lib';
3 | import { builder } from '../lib/build';
4 | import { Environment } from '../lib/config';
5 |
6 | export const build = async (args: any): Promise => {
7 | try {
8 | const appName = basename(process.cwd());
9 | const app = new AutoCdk(appName, Environment.PRODUCTION);
10 | await builder(app.config);
11 | await app.constructResources();
12 | app.synth();
13 | } catch (e) {
14 | console.error(e);
15 | return Promise.reject();
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/cli/dev.ts:
--------------------------------------------------------------------------------
1 | import { basename } from 'path';
2 | import { AutoCdk } from '../lib';
3 | import { builder } from '../lib/build';
4 | import { Environment } from '../lib/config';
5 |
6 | export const dev = async (args: any): Promise => {
7 | try {
8 | const appName = basename(process.cwd());
9 | const app = new AutoCdk(appName, Environment.DEVELOPMENT);
10 | await builder(app.config);
11 | await app.constructResources();
12 | app.synth();
13 | } catch (e) {
14 | console.error(e);
15 | return Promise.reject();
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/docs/CLI.md:
--------------------------------------------------------------------------------
1 | # CLI
2 |
3 | This document serves as a reference for the `auto-cdk` cli.
4 |
5 | ## Usage
6 |
7 | ```text
8 | auto-cdk [subcommand] [options]
9 | ```
10 |
11 | ## Global Options
12 |
13 | |----|-----------|--------|-------|
14 | |name|description|required|default|
15 | |--version|Displays the version of `auto-cdk`|false|none|
16 | |--debug|Runs `auto-cdk` with debug logging|false|none|
17 | |--help|Displays the help text|false|none|
18 |
19 | ## Subcommands
20 |
21 | ### build
22 |
23 | |----|-----------|--------|-------|
24 | |name|description|required|default|
25 |
26 | ### dev
27 |
28 | |----|-----------|--------|-------|
29 | |name|description|required|default|
30 |
31 | ## Configuration
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/examples/api-with-existing-cdk-app/api/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-existing-cdk-app/api/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-existing-cdk-app/app.ts:
--------------------------------------------------------------------------------
1 | import * as cdk from 'aws-cdk';
2 | import { AutoCdk } from 'auto-cdk';
3 |
4 | const app = new cdk.App();
5 | const stack = new cdk.Stack();
6 |
7 | new AutoCdk('my-api', {
8 | app,
9 | stack
10 | }).build():
11 |
--------------------------------------------------------------------------------
/docs/examples/api-with-existing-cdk-app/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "npx ts-node app.ts"
3 | }
--------------------------------------------------------------------------------
/docs/examples/api-with-existing-cdk-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-with-existing-cdk-app",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "dev": "auto-cdk dev",
7 | "build": "auto-cdk build"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "auto-cdk": "^0.1.1",
12 | "aws-cdk": ""
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/examples/api-with-greedy-proxy/README.md:
--------------------------------------------------------------------------------
1 | # API with a Greedy Proxy
2 |
3 | You can add a [greedy resource](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html) by adding a directory named `{proxy+}` to your api structure. You can learn more about proxy resources in the [AWS Documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html).
4 |
5 |
--------------------------------------------------------------------------------
/docs/examples/api-with-greedy-proxy/api/{proxy+}/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-greedy-proxy/api/{proxy+}/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-greedy-proxy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-with-greedy-proxy",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "dev": "auto-cdk dev",
7 | "build": "auto-cdk build"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "auto-cdk": "^0.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/examples/api-with-parametized-path/api/{id}/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-parametized-path/api/{id}/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-parametized-path/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-with-parametized-path",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "dev": "auto-cdk dev",
7 | "build": "auto-cdk build"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "auto-cdk": "^0.1.1"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/.gitignore:
--------------------------------------------------------------------------------
1 | cdk.out/
2 | dist/
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/README.md:
--------------------------------------------------------------------------------
1 | # Example API with auto-cdk
2 |
3 | This example has a simple api in the `api` directory and a dependency on `auto-cdk`.
4 |
5 | You can start the webpack compiler in watch mode by running: `yarn dev`.
6 |
7 | You can build for production with: `yarn build`.
8 |
9 | If you have [sam-cli](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) you can run `yarn dev & yarn sam` to start both in watch mode and get live reload for the api running on localhost.
10 |
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/api/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-single-handler-all-methods/api/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/api/posts/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-single-handler-all-methods/api/posts/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/api/users/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/docs/examples/api-with-single-handler-all-methods/api/users/index.ts
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-with-single-handler-all-methods",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "dev": "auto-cdk dev",
7 | "build": "auto-cdk build",
8 | "sam": "sam local start-api -t cdk.out/api.template.json"
9 | },
10 | "license": "MIT",
11 | "dependencies": {
12 | "auto-cdk": "^0.1.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/examples/api-with-single-handler-all-methods/sample-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "Resources": {
3 | "apiC8550315": {
4 | "Type": "AWS::ApiGateway::RestApi",
5 | "Properties": {
6 | "Name": "api"
7 | }
8 | },
9 | "apiCloudWatchRoleAC81D93E": {
10 | "Type": "AWS::IAM::Role",
11 | "Properties": {
12 | "AssumeRolePolicyDocument": {
13 | "Statement": [
14 | {
15 | "Action": "sts:AssumeRole",
16 | "Effect": "Allow",
17 | "Principal": {
18 | "Service": "apigateway.amazonaws.com"
19 | }
20 | }
21 | ],
22 | "Version": "2012-10-17"
23 | },
24 | "ManagedPolicyArns": [
25 | {
26 | "Fn::Join": [
27 | "",
28 | [
29 | "arn:",
30 | {
31 | "Ref": "AWS::Partition"
32 | },
33 | ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
34 | ]
35 | ]
36 | }
37 | ]
38 | }
39 | },
40 | "apiAccount57E28B43": {
41 | "Type": "AWS::ApiGateway::Account",
42 | "Properties": {
43 | "CloudWatchRoleArn": {
44 | "Fn::GetAtt": [
45 | "apiCloudWatchRoleAC81D93E",
46 | "Arn"
47 | ]
48 | }
49 | },
50 | "DependsOn": [
51 | "apiC8550315"
52 | ]
53 | },
54 | "apiDeployment149F129457e6bb80d0bc1859bf31179be6efb638": {
55 | "Type": "AWS::ApiGateway::Deployment",
56 | "Properties": {
57 | "RestApiId": {
58 | "Ref": "apiC8550315"
59 | },
60 | "Description": "Automatically created by the RestApi construct"
61 | },
62 | "DependsOn": [
63 | "apiANY4728F8A3",
64 | "apipostsANY8A1EF778",
65 | "apiposts2859138C",
66 | "apiA9E0BAD5",
67 | "apiusersANY4A3D9359",
68 | "apiusers90C8981D"
69 | ]
70 | },
71 | "apiDeploymentStageprod896C8101": {
72 | "Type": "AWS::ApiGateway::Stage",
73 | "Properties": {
74 | "RestApiId": {
75 | "Ref": "apiC8550315"
76 | },
77 | "DeploymentId": {
78 | "Ref": "apiDeployment149F129457e6bb80d0bc1859bf31179be6efb638"
79 | },
80 | "StageName": "prod"
81 | }
82 | },
83 | "apiA9E0BAD5": {
84 | "Type": "AWS::ApiGateway::Resource",
85 | "Properties": {
86 | "ParentId": {
87 | "Fn::GetAtt": [
88 | "apiC8550315",
89 | "RootResourceId"
90 | ]
91 | },
92 | "PathPart": "api",
93 | "RestApiId": {
94 | "Ref": "apiC8550315"
95 | }
96 | }
97 | },
98 | "apiposts2859138C": {
99 | "Type": "AWS::ApiGateway::Resource",
100 | "Properties": {
101 | "ParentId": {
102 | "Ref": "apiA9E0BAD5"
103 | },
104 | "PathPart": "posts",
105 | "RestApiId": {
106 | "Ref": "apiC8550315"
107 | }
108 | }
109 | },
110 | "apipostsANYApiPermissionapi4F59AA66ANYapiposts5FF718E9": {
111 | "Type": "AWS::Lambda::Permission",
112 | "Properties": {
113 | "Action": "lambda:InvokeFunction",
114 | "FunctionName": {
115 | "Fn::GetAtt": [
116 | "apiapipostsANYFunctionFunction80280F8D",
117 | "Arn"
118 | ]
119 | },
120 | "Principal": "apigateway.amazonaws.com",
121 | "SourceArn": {
122 | "Fn::Join": [
123 | "",
124 | [
125 | "arn:",
126 | {
127 | "Ref": "AWS::Partition"
128 | },
129 | ":execute-api:",
130 | {
131 | "Ref": "AWS::Region"
132 | },
133 | ":",
134 | {
135 | "Ref": "AWS::AccountId"
136 | },
137 | ":",
138 | {
139 | "Ref": "apiC8550315"
140 | },
141 | "/",
142 | {
143 | "Ref": "apiDeploymentStageprod896C8101"
144 | },
145 | "/*/api/posts"
146 | ]
147 | ]
148 | }
149 | }
150 | },
151 | "apipostsANYApiPermissionTestapi4F59AA66ANYapipostsEDECFA79": {
152 | "Type": "AWS::Lambda::Permission",
153 | "Properties": {
154 | "Action": "lambda:InvokeFunction",
155 | "FunctionName": {
156 | "Fn::GetAtt": [
157 | "apiapipostsANYFunctionFunction80280F8D",
158 | "Arn"
159 | ]
160 | },
161 | "Principal": "apigateway.amazonaws.com",
162 | "SourceArn": {
163 | "Fn::Join": [
164 | "",
165 | [
166 | "arn:",
167 | {
168 | "Ref": "AWS::Partition"
169 | },
170 | ":execute-api:",
171 | {
172 | "Ref": "AWS::Region"
173 | },
174 | ":",
175 | {
176 | "Ref": "AWS::AccountId"
177 | },
178 | ":",
179 | {
180 | "Ref": "apiC8550315"
181 | },
182 | "/test-invoke-stage/*/api/posts"
183 | ]
184 | ]
185 | }
186 | }
187 | },
188 | "apipostsANY8A1EF778": {
189 | "Type": "AWS::ApiGateway::Method",
190 | "Properties": {
191 | "HttpMethod": "ANY",
192 | "ResourceId": {
193 | "Ref": "apiposts2859138C"
194 | },
195 | "RestApiId": {
196 | "Ref": "apiC8550315"
197 | },
198 | "AuthorizationType": "NONE",
199 | "Integration": {
200 | "IntegrationHttpMethod": "POST",
201 | "Type": "AWS_PROXY",
202 | "Uri": {
203 | "Fn::Join": [
204 | "",
205 | [
206 | "arn:",
207 | {
208 | "Ref": "AWS::Partition"
209 | },
210 | ":apigateway:",
211 | {
212 | "Ref": "AWS::Region"
213 | },
214 | ":lambda:path/2015-03-31/functions/",
215 | {
216 | "Fn::GetAtt": [
217 | "apiapipostsANYFunctionFunction80280F8D",
218 | "Arn"
219 | ]
220 | },
221 | "/invocations"
222 | ]
223 | ]
224 | }
225 | }
226 | }
227 | },
228 | "apiusers90C8981D": {
229 | "Type": "AWS::ApiGateway::Resource",
230 | "Properties": {
231 | "ParentId": {
232 | "Ref": "apiA9E0BAD5"
233 | },
234 | "PathPart": "users",
235 | "RestApiId": {
236 | "Ref": "apiC8550315"
237 | }
238 | }
239 | },
240 | "apiusersANYApiPermissionapi4F59AA66ANYapiusersBB57DBD7": {
241 | "Type": "AWS::Lambda::Permission",
242 | "Properties": {
243 | "Action": "lambda:InvokeFunction",
244 | "FunctionName": {
245 | "Fn::GetAtt": [
246 | "apiapiusersANYFunctionFunction27017680",
247 | "Arn"
248 | ]
249 | },
250 | "Principal": "apigateway.amazonaws.com",
251 | "SourceArn": {
252 | "Fn::Join": [
253 | "",
254 | [
255 | "arn:",
256 | {
257 | "Ref": "AWS::Partition"
258 | },
259 | ":execute-api:",
260 | {
261 | "Ref": "AWS::Region"
262 | },
263 | ":",
264 | {
265 | "Ref": "AWS::AccountId"
266 | },
267 | ":",
268 | {
269 | "Ref": "apiC8550315"
270 | },
271 | "/",
272 | {
273 | "Ref": "apiDeploymentStageprod896C8101"
274 | },
275 | "/*/api/users"
276 | ]
277 | ]
278 | }
279 | }
280 | },
281 | "apiusersANYApiPermissionTestapi4F59AA66ANYapiusers915EE57D": {
282 | "Type": "AWS::Lambda::Permission",
283 | "Properties": {
284 | "Action": "lambda:InvokeFunction",
285 | "FunctionName": {
286 | "Fn::GetAtt": [
287 | "apiapiusersANYFunctionFunction27017680",
288 | "Arn"
289 | ]
290 | },
291 | "Principal": "apigateway.amazonaws.com",
292 | "SourceArn": {
293 | "Fn::Join": [
294 | "",
295 | [
296 | "arn:",
297 | {
298 | "Ref": "AWS::Partition"
299 | },
300 | ":execute-api:",
301 | {
302 | "Ref": "AWS::Region"
303 | },
304 | ":",
305 | {
306 | "Ref": "AWS::AccountId"
307 | },
308 | ":",
309 | {
310 | "Ref": "apiC8550315"
311 | },
312 | "/test-invoke-stage/*/api/users"
313 | ]
314 | ]
315 | }
316 | }
317 | },
318 | "apiusersANY4A3D9359": {
319 | "Type": "AWS::ApiGateway::Method",
320 | "Properties": {
321 | "HttpMethod": "ANY",
322 | "ResourceId": {
323 | "Ref": "apiusers90C8981D"
324 | },
325 | "RestApiId": {
326 | "Ref": "apiC8550315"
327 | },
328 | "AuthorizationType": "NONE",
329 | "Integration": {
330 | "IntegrationHttpMethod": "POST",
331 | "Type": "AWS_PROXY",
332 | "Uri": {
333 | "Fn::Join": [
334 | "",
335 | [
336 | "arn:",
337 | {
338 | "Ref": "AWS::Partition"
339 | },
340 | ":apigateway:",
341 | {
342 | "Ref": "AWS::Region"
343 | },
344 | ":lambda:path/2015-03-31/functions/",
345 | {
346 | "Fn::GetAtt": [
347 | "apiapiusersANYFunctionFunction27017680",
348 | "Arn"
349 | ]
350 | },
351 | "/invocations"
352 | ]
353 | ]
354 | }
355 | }
356 | }
357 | },
358 | "apiANYApiPermissionapi4F59AA66ANYapiFA540F0D": {
359 | "Type": "AWS::Lambda::Permission",
360 | "Properties": {
361 | "Action": "lambda:InvokeFunction",
362 | "FunctionName": {
363 | "Fn::GetAtt": [
364 | "apiapiANYFunctionFunctionD0B038B8",
365 | "Arn"
366 | ]
367 | },
368 | "Principal": "apigateway.amazonaws.com",
369 | "SourceArn": {
370 | "Fn::Join": [
371 | "",
372 | [
373 | "arn:",
374 | {
375 | "Ref": "AWS::Partition"
376 | },
377 | ":execute-api:",
378 | {
379 | "Ref": "AWS::Region"
380 | },
381 | ":",
382 | {
383 | "Ref": "AWS::AccountId"
384 | },
385 | ":",
386 | {
387 | "Ref": "apiC8550315"
388 | },
389 | "/",
390 | {
391 | "Ref": "apiDeploymentStageprod896C8101"
392 | },
393 | "/*/api"
394 | ]
395 | ]
396 | }
397 | }
398 | },
399 | "apiANYApiPermissionTestapi4F59AA66ANYapi8313EDBB": {
400 | "Type": "AWS::Lambda::Permission",
401 | "Properties": {
402 | "Action": "lambda:InvokeFunction",
403 | "FunctionName": {
404 | "Fn::GetAtt": [
405 | "apiapiANYFunctionFunctionD0B038B8",
406 | "Arn"
407 | ]
408 | },
409 | "Principal": "apigateway.amazonaws.com",
410 | "SourceArn": {
411 | "Fn::Join": [
412 | "",
413 | [
414 | "arn:",
415 | {
416 | "Ref": "AWS::Partition"
417 | },
418 | ":execute-api:",
419 | {
420 | "Ref": "AWS::Region"
421 | },
422 | ":",
423 | {
424 | "Ref": "AWS::AccountId"
425 | },
426 | ":",
427 | {
428 | "Ref": "apiC8550315"
429 | },
430 | "/test-invoke-stage/*/api"
431 | ]
432 | ]
433 | }
434 | }
435 | },
436 | "apiANY4728F8A3": {
437 | "Type": "AWS::ApiGateway::Method",
438 | "Properties": {
439 | "HttpMethod": "ANY",
440 | "ResourceId": {
441 | "Ref": "apiA9E0BAD5"
442 | },
443 | "RestApiId": {
444 | "Ref": "apiC8550315"
445 | },
446 | "AuthorizationType": "NONE",
447 | "Integration": {
448 | "IntegrationHttpMethod": "POST",
449 | "Type": "AWS_PROXY",
450 | "Uri": {
451 | "Fn::Join": [
452 | "",
453 | [
454 | "arn:",
455 | {
456 | "Ref": "AWS::Partition"
457 | },
458 | ":apigateway:",
459 | {
460 | "Ref": "AWS::Region"
461 | },
462 | ":lambda:path/2015-03-31/functions/",
463 | {
464 | "Fn::GetAtt": [
465 | "apiapiANYFunctionFunctionD0B038B8",
466 | "Arn"
467 | ]
468 | },
469 | "/invocations"
470 | ]
471 | ]
472 | }
473 | }
474 | }
475 | },
476 | "apiapipostsANYFunctionFunctionServiceRoleB4A84966": {
477 | "Type": "AWS::IAM::Role",
478 | "Properties": {
479 | "AssumeRolePolicyDocument": {
480 | "Statement": [
481 | {
482 | "Action": "sts:AssumeRole",
483 | "Effect": "Allow",
484 | "Principal": {
485 | "Service": "lambda.amazonaws.com"
486 | }
487 | }
488 | ],
489 | "Version": "2012-10-17"
490 | },
491 | "ManagedPolicyArns": [
492 | {
493 | "Fn::Join": [
494 | "",
495 | [
496 | "arn:",
497 | {
498 | "Ref": "AWS::Partition"
499 | },
500 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
501 | ]
502 | ]
503 | }
504 | ]
505 | }
506 | },
507 | "apiapipostsANYFunctionFunction80280F8D": {
508 | "Type": "AWS::Lambda::Function",
509 | "Properties": {
510 | "Code": {
511 | "S3Bucket": {
512 | "Ref": "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dS3Bucket54C64172"
513 | },
514 | "S3Key": {
515 | "Fn::Join": [
516 | "",
517 | [
518 | {
519 | "Fn::Select": [
520 | 0,
521 | {
522 | "Fn::Split": [
523 | "||",
524 | {
525 | "Ref": "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dS3VersionKeyE0C74E6B"
526 | }
527 | ]
528 | }
529 | ]
530 | },
531 | {
532 | "Fn::Select": [
533 | 1,
534 | {
535 | "Fn::Split": [
536 | "||",
537 | {
538 | "Ref": "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dS3VersionKeyE0C74E6B"
539 | }
540 | ]
541 | }
542 | ]
543 | }
544 | ]
545 | ]
546 | }
547 | },
548 | "Handler": "index.handler",
549 | "Role": {
550 | "Fn::GetAtt": [
551 | "apiapipostsANYFunctionFunctionServiceRoleB4A84966",
552 | "Arn"
553 | ]
554 | },
555 | "Runtime": "nodejs12.x"
556 | },
557 | "DependsOn": [
558 | "apiapipostsANYFunctionFunctionServiceRoleB4A84966"
559 | ]
560 | },
561 | "apiapiusersANYFunctionFunctionServiceRoleD705655D": {
562 | "Type": "AWS::IAM::Role",
563 | "Properties": {
564 | "AssumeRolePolicyDocument": {
565 | "Statement": [
566 | {
567 | "Action": "sts:AssumeRole",
568 | "Effect": "Allow",
569 | "Principal": {
570 | "Service": "lambda.amazonaws.com"
571 | }
572 | }
573 | ],
574 | "Version": "2012-10-17"
575 | },
576 | "ManagedPolicyArns": [
577 | {
578 | "Fn::Join": [
579 | "",
580 | [
581 | "arn:",
582 | {
583 | "Ref": "AWS::Partition"
584 | },
585 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
586 | ]
587 | ]
588 | }
589 | ]
590 | }
591 | },
592 | "apiapiusersANYFunctionFunction27017680": {
593 | "Type": "AWS::Lambda::Function",
594 | "Properties": {
595 | "Code": {
596 | "S3Bucket": {
597 | "Ref": "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4S3Bucket6D64E14D"
598 | },
599 | "S3Key": {
600 | "Fn::Join": [
601 | "",
602 | [
603 | {
604 | "Fn::Select": [
605 | 0,
606 | {
607 | "Fn::Split": [
608 | "||",
609 | {
610 | "Ref": "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4S3VersionKey84C79F22"
611 | }
612 | ]
613 | }
614 | ]
615 | },
616 | {
617 | "Fn::Select": [
618 | 1,
619 | {
620 | "Fn::Split": [
621 | "||",
622 | {
623 | "Ref": "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4S3VersionKey84C79F22"
624 | }
625 | ]
626 | }
627 | ]
628 | }
629 | ]
630 | ]
631 | }
632 | },
633 | "Handler": "index.handler",
634 | "Role": {
635 | "Fn::GetAtt": [
636 | "apiapiusersANYFunctionFunctionServiceRoleD705655D",
637 | "Arn"
638 | ]
639 | },
640 | "Runtime": "nodejs12.x"
641 | },
642 | "DependsOn": [
643 | "apiapiusersANYFunctionFunctionServiceRoleD705655D"
644 | ]
645 | },
646 | "apiapiANYFunctionFunctionServiceRoleA46995B7": {
647 | "Type": "AWS::IAM::Role",
648 | "Properties": {
649 | "AssumeRolePolicyDocument": {
650 | "Statement": [
651 | {
652 | "Action": "sts:AssumeRole",
653 | "Effect": "Allow",
654 | "Principal": {
655 | "Service": "lambda.amazonaws.com"
656 | }
657 | }
658 | ],
659 | "Version": "2012-10-17"
660 | },
661 | "ManagedPolicyArns": [
662 | {
663 | "Fn::Join": [
664 | "",
665 | [
666 | "arn:",
667 | {
668 | "Ref": "AWS::Partition"
669 | },
670 | ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
671 | ]
672 | ]
673 | }
674 | ]
675 | }
676 | },
677 | "apiapiANYFunctionFunctionD0B038B8": {
678 | "Type": "AWS::Lambda::Function",
679 | "Properties": {
680 | "Code": {
681 | "S3Bucket": {
682 | "Ref": "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bS3Bucket5491774E"
683 | },
684 | "S3Key": {
685 | "Fn::Join": [
686 | "",
687 | [
688 | {
689 | "Fn::Select": [
690 | 0,
691 | {
692 | "Fn::Split": [
693 | "||",
694 | {
695 | "Ref": "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bS3VersionKey85267BD1"
696 | }
697 | ]
698 | }
699 | ]
700 | },
701 | {
702 | "Fn::Select": [
703 | 1,
704 | {
705 | "Fn::Split": [
706 | "||",
707 | {
708 | "Ref": "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bS3VersionKey85267BD1"
709 | }
710 | ]
711 | }
712 | ]
713 | }
714 | ]
715 | ]
716 | }
717 | },
718 | "Handler": "index.handler",
719 | "Role": {
720 | "Fn::GetAtt": [
721 | "apiapiANYFunctionFunctionServiceRoleA46995B7",
722 | "Arn"
723 | ]
724 | },
725 | "Runtime": "nodejs12.x"
726 | },
727 | "DependsOn": [
728 | "apiapiANYFunctionFunctionServiceRoleA46995B7"
729 | ]
730 | }
731 | },
732 | "Outputs": {
733 | "apiEndpoint9349E63C": {
734 | "Value": {
735 | "Fn::Join": [
736 | "",
737 | [
738 | "https://",
739 | {
740 | "Ref": "apiC8550315"
741 | },
742 | ".execute-api.",
743 | {
744 | "Ref": "AWS::Region"
745 | },
746 | ".",
747 | {
748 | "Ref": "AWS::URLSuffix"
749 | },
750 | "/",
751 | {
752 | "Ref": "apiDeploymentStageprod896C8101"
753 | },
754 | "/"
755 | ]
756 | ]
757 | }
758 | }
759 | },
760 | "Parameters": {
761 | "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dS3Bucket54C64172": {
762 | "Type": "String",
763 | "Description": "S3 bucket for asset \"37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7d\""
764 | },
765 | "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dS3VersionKeyE0C74E6B": {
766 | "Type": "String",
767 | "Description": "S3 key for asset version \"37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7d\""
768 | },
769 | "AssetParameters37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7dArtifactHash12862E4F": {
770 | "Type": "String",
771 | "Description": "Artifact hash for asset \"37f0f4416bf3508e811a5895e5da4c0d45caaf69176a33c055e5f0a63c3ced7d\""
772 | },
773 | "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4S3Bucket6D64E14D": {
774 | "Type": "String",
775 | "Description": "S3 bucket for asset \"c0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4\""
776 | },
777 | "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4S3VersionKey84C79F22": {
778 | "Type": "String",
779 | "Description": "S3 key for asset version \"c0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4\""
780 | },
781 | "AssetParametersc0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4ArtifactHashDB848C20": {
782 | "Type": "String",
783 | "Description": "Artifact hash for asset \"c0642791fd57051c310f540e9c1eef09e0883c4e8bfd3c4ce4b749cd8da0a4b4\""
784 | },
785 | "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bS3Bucket5491774E": {
786 | "Type": "String",
787 | "Description": "S3 bucket for asset \"2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5b\""
788 | },
789 | "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bS3VersionKey85267BD1": {
790 | "Type": "String",
791 | "Description": "S3 key for asset version \"2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5b\""
792 | },
793 | "AssetParameters2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5bArtifactHash3DA1EC83": {
794 | "Type": "String",
795 | "Description": "Artifact hash for asset \"2288a22aa274ce0f1db5773622c8a26b66df89c50881a31343112a309bba2d5b\""
796 | }
797 | }
798 | }
--------------------------------------------------------------------------------
/lib/autocdk.ts:
--------------------------------------------------------------------------------
1 | import * as cdk from '@aws-cdk/core';
2 | import * as ag from '@aws-cdk/aws-apigateway';
3 | import * as lambda from '@aws-cdk/aws-lambda';
4 | import { constructRouteMap, IRouteMap } from './routes';
5 | import { constructResourceMap, IResourceMap, IResource, ResourceType } from './resources';
6 | import { Config, Environment, ConfigProps } from './config';
7 | import { isNotFound } from './utils';
8 |
9 | const INDEX = 'index';
10 |
11 | export type ResourceLike = ag.Resource | ag.IResource;
12 | export type MethodLike = ag.Method;
13 |
14 | export interface AutoCdkProps{
15 | app?: cdk.App;
16 | stack?: cdk.Stack;
17 | api?: ag.RestApi;
18 | config?: ConfigProps;
19 | }
20 |
21 | export interface CreateIntegrationProps {
22 | runtime?: lambda.Runtime;
23 | handler?: string;
24 | }
25 |
26 | export class AutoCdk {
27 | public readonly app: cdk.App;
28 | public readonly stack: cdk.Stack;
29 | public readonly api: ag.RestApi;
30 | public readonly config: Config;
31 | public routeMap: IRouteMap;
32 | private readonly id: string;
33 |
34 | constructor(id: string, env: Environment, props?: AutoCdkProps) {
35 | this.id = id;
36 | this.config = new Config({ env, ...props?.config });
37 |
38 | if (this.config.debug) {
39 | console.log(`Using: ${this.config.workingDirectory} as working directory`);
40 | }
41 |
42 | this.app = props?.app || new cdk.App({
43 | outdir: `${this.config.workingDirectory}/cdk.out`
44 | });
45 |
46 | this.stack = props?.stack || new cdk.Stack(this.app, id);
47 | this.api = props?.api || new ag.RestApi(this.stack, id);
48 | }
49 |
50 | public synth() {
51 | return this.app.synth();
52 | }
53 |
54 | public async constructRoutes(): Promise {
55 | if (this.config.debug) {
56 | console.log('Constructing Routes')
57 | }
58 | const routeMap = await constructRouteMap(this.config.rootDirectory, this.config);
59 | this.routeMap = routeMap;
60 | return this.routeMap;
61 | }
62 |
63 | public async constructResources(): Promise {
64 | if (this.config.debug) {
65 | console.log('Constructing Resources')
66 | }
67 | const routes = await this.constructRoutes();
68 | const resourceMap = constructResourceMap(routes, this.config.rootDirectory, this.config);
69 | this.createResourcesFromMap(this.api.root, resourceMap);
70 | }
71 |
72 | private createResourcesFromMap(parent: ResourceLike, resourceMap: IResourceMap) {
73 | if (resourceMap.type === ResourceType.RESOURCE) {
74 | if (resourceMap.children) {
75 | const resource = this.createResource(parent, resourceMap.name);
76 | const children = resourceMap.children;
77 | Object.keys(children).forEach(child => {
78 | this.createResourcesFromMap(resource, children[child]);
79 | })
80 | } else {
81 | if (this.config.createEmptyResources) {
82 | this.createResource(parent, resourceMap.name);
83 | }
84 | }
85 | } else {
86 | if (resourceMap.name === INDEX) {
87 | this.createMethod(parent, resourceMap);
88 | } else {
89 | const resource = this.createResource(parent, resourceMap.name);
90 | this.createMethod(resource, resourceMap);
91 | }
92 | }
93 | }
94 |
95 | private createResource(parent: ResourceLike, path: string, options?: ag.ResourceOptions) {
96 | if (this.config.debug) {
97 | console.log(`Creating Resource: ${path}`);
98 | }
99 |
100 | return parent.addResource(path, options);
101 | }
102 |
103 | private createMethod(parent: ResourceLike, resource: IResourceMap, options?: ag.MethodOptions) {
104 | const method = 'ANY';
105 |
106 | if (this.config.debug) {
107 | console.log(`Attaching Method: ${method}, to Resource: ${parent.path}`);
108 | }
109 |
110 | const logicalId = `${this.id}${parent.path}${method}Function`;
111 | const integration = this.createIntegration(resource, logicalId);
112 | return parent.addMethod(method, integration, options);
113 | }
114 |
115 | private createIntegration(resource: IResourceMap, id: string, props?: CreateIntegrationProps) {
116 | const normalizedAssetDir = this.config.assetDir.endsWith('/') ? this.config.assetDir : `${this.config.assetDir}/`;
117 | const assetPath = `${normalizedAssetDir}${resource.assetPath}`;
118 | const lambdaProps = {
119 | runtime: props?.runtime || this.config.defaultRuntime,
120 | handler: props?.handler || this.config.defaultHandler,
121 | code: lambda.Code.fromAsset(assetPath)
122 | };
123 |
124 | try {
125 | const fn = this.createLambda(`${id}Function`, lambdaProps);
126 | return new ag.LambdaIntegration(fn);
127 | } catch (e) {
128 | if (isNotFound(e)) {
129 | console.error(`Unable to add lambda integration. ${assetPath} was not found. This most likely means webpack did not run before this process.`);
130 | process.exit();
131 | } else {
132 | throw e;
133 | }
134 | }
135 | }
136 |
137 | private createLambda(id: string, props: lambda.FunctionProps) {
138 | return new lambda.Function(this.stack, id, props);
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/lib/build/index.ts:
--------------------------------------------------------------------------------
1 | import { constructRouteMap } from '../routes';
2 | import { createEntrypoints } from './webpack/entrypoints';
3 | import { createConfig } from './webpack/config';
4 | import { compile } from './webpack/compiler';
5 | import { Config } from '../config';
6 |
7 | export async function builder(config: Config): Promise {
8 | const routes = await constructRouteMap(config.rootDirectory, config);
9 | const entrypoints = createEntrypoints(routes, config.rootDirectory, config);
10 | const configs = await createConfig(config, entrypoints, config.env);
11 | return await compile(configs, config.env);
12 | }
13 |
--------------------------------------------------------------------------------
/lib/build/webpack/compiler.ts:
--------------------------------------------------------------------------------
1 | import * as webpack from 'webpack';
2 | import { Configuration, Stats } from 'webpack';
3 | import { Environment } from '../../config';
4 |
5 | const compilerHandler = (err: Error, stats: Stats): void => {
6 | if (err) {
7 | const reason = err?.toString()
8 | if (reason) {
9 | throw new Error(reason)
10 | } else {
11 | throw new Error(`There was an error when running the webpack compiler: ${err}`);
12 | }
13 | }
14 |
15 | const info = stats.toJson();
16 |
17 | if (stats.hasErrors()) {
18 | console.error(info.errors);
19 | }
20 |
21 | if (stats.hasWarnings()) {
22 | console.warn(info.warnings);
23 | }
24 |
25 | console.log(stats.toString({
26 | chunks: false, // Makes the build much quieter
27 | colors: true // Shows colors in the console
28 | }));
29 | }
30 |
31 | export const compile = (config: Configuration, env: Environment): Promise => {
32 | console.log('Beginning Compile')
33 | return new Promise(async (resolve, reject) => {
34 | try {
35 | const compiler = webpack(config);
36 | if (env === Environment.DEVELOPMENT) {
37 | compiler.watch({ ignored: /node_modules/ }, compilerHandler);
38 | await compiler.hooks.afterEmit.tapAsync('developmentCompiler', () => {
39 | resolve()
40 | });
41 | } else {
42 | compiler.run(compilerHandler);
43 | await compiler.hooks.afterEmit.tapAsync('productionCompiler', () => {
44 | resolve()
45 | });
46 | }
47 | } catch (e) {
48 | reject(e);
49 | }
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/lib/build/webpack/config.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'path';
2 | import { Config } from '../../config';
3 | import { Configuration, ProgressPlugin } from 'webpack';
4 | import { Entrypoint } from './entrypoints';
5 | import { CleanWebpackPlugin } from 'clean-webpack-plugin';
6 |
7 | /**
8 | * Generates the configuration that webpack will use
9 | */
10 | export const createDevelopmentConfig = async (config: Config, entry: Entrypoint): Promise => {
11 | return {
12 | mode: 'development',
13 | entry,
14 | output: {
15 | path: join(config.workingDirectory, 'dist'),
16 | filename: '[name]/index.js'
17 | },
18 | watch: true,
19 | plugins: [
20 | new ProgressPlugin(),
21 | new CleanWebpackPlugin({
22 | cleanStaleWebpackAssets: false
23 | }),
24 | ]
25 | };
26 | }
27 |
28 |
29 | /**
30 | * Generates the configuration that webpack will use
31 | */
32 | export const createProductionConfig = async (config: Config, entry: Entrypoint): Promise => {
33 | return {
34 | mode: 'production',
35 | entry,
36 | output: {
37 | path: join(config.workingDirectory, 'dist'),
38 | filename: '[name]/index.js'
39 | },
40 | plugins: [
41 | new ProgressPlugin(),
42 | new CleanWebpackPlugin(),
43 | ]
44 | };
45 | }
46 |
47 | export const createConfig = async(config: Config, entry: Entrypoint, env: string): Promise => {
48 | if (env === 'development') {
49 | return createDevelopmentConfig(config, entry);
50 | } else if (env === 'production') {
51 | return createProductionConfig(config, entry);
52 | } else {
53 | return Promise.reject(`expected ${env} to be one of development or production. Not sure how to handle this environment`)
54 | }
55 | }
--------------------------------------------------------------------------------
/lib/build/webpack/entrypoints.ts:
--------------------------------------------------------------------------------
1 | import { parse } from 'path';
2 | import { IRouteMap } from '../../routes';
3 | import { Config } from '../../config';
4 |
5 | export interface Entrypoint {
6 | [key: string]: string;
7 | }
8 |
9 | /**
10 | * Converts a IRouteMap to a map of webpack entrypoints
11 | */
12 | export const createEntrypoints = (routeMap: IRouteMap, targetDirectory: string, config: Config): Entrypoint => {
13 | let result: Entrypoint = {};
14 | if (routeMap.children) {
15 | for (const key in routeMap.children) {
16 | Object.assign(result, createEntrypoints(routeMap.children[key], targetDirectory, config));
17 | }
18 | } else {
19 | const parsed = parse(routeMap.path);
20 | let adjustedDir = parsed.dir;
21 | if (config.includeRoot) {
22 | adjustedDir = parsed.dir.startsWith(targetDirectory) ? parsed.dir.slice(targetDirectory.length): parsed.dir;
23 | }
24 | const key = `${adjustedDir}/${parsed.name}`
25 | result[key] = routeMap.relativePath;
26 | }
27 | return result
28 | }
29 |
--------------------------------------------------------------------------------
/lib/config.ts:
--------------------------------------------------------------------------------
1 | import { Runtime } from "@aws-cdk/aws-lambda";
2 |
3 | export enum Environment {
4 | PRODUCTION='production',
5 | DEVELOPMENT='development'
6 | }
7 |
8 | export interface ConfigProps {
9 | /**
10 | * Create resources on the api gateway even when the directory is empty
11 | * @default true
12 | */
13 | createEmptyResources?: boolean;
14 |
15 | /**
16 | * The working directory
17 | * @default process.cwd()
18 | */
19 | workingDirectory?: string;
20 |
21 | /**
22 | * The directory that contains the root of the api tree
23 | * @default api
24 | */
25 | rootDirectory?: string;
26 |
27 | /**
28 | * The default lambda runtime
29 | * @default Runtime.NODEJS_12_X
30 | */
31 | defaultRuntime?: Runtime;
32 |
33 | /**
34 | * The default lambda handler
35 | * @default Runtime.NODEJS_12_X
36 | */
37 | defaultHandler?: string;
38 |
39 | /**
40 | * The directory where the lambda assets will be packaged
41 | * @default dist
42 | */
43 | assetDir?: string;
44 |
45 | /**
46 | * Enable debug mode (verbose logging)
47 | * @default false
48 | */
49 | debug?: boolean;
50 |
51 | /**
52 | * Include the root directory in the api paths
53 | * @default false
54 | */
55 | includeRoot?: boolean;
56 |
57 | /**
58 | * The environment
59 | * @default development
60 | */
61 | env?: Environment;
62 | }
63 |
64 | export class Config {
65 | public readonly createEmptyResources: boolean;
66 | public readonly workingDirectory: string;
67 | public readonly rootDirectory: string;
68 | public readonly defaultRuntime: Runtime;
69 | public readonly defaultHandler: string;
70 | public readonly assetDir: string;
71 | public readonly debug: boolean;
72 | public readonly includeRoot: boolean;
73 | public readonly env: Environment;
74 |
75 | constructor(props?: ConfigProps) {
76 | this.createEmptyResources = props?.createEmptyResources || true;
77 | this.workingDirectory = props?.workingDirectory || process.cwd();
78 | this.rootDirectory = props?.rootDirectory || 'api';
79 | this.defaultRuntime = props?.defaultRuntime || Runtime.NODEJS_12_X;
80 | this.defaultHandler = props?.defaultHandler || 'index.handler';
81 | this.assetDir = props?.assetDir || 'dist';
82 | this.debug = props?.debug || false;
83 | this.includeRoot = props?.includeRoot || false;
84 | this.env = props?.env || Environment.DEVELOPMENT;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/index.ts:
--------------------------------------------------------------------------------
1 | export { AutoCdk, AutoCdkProps } from './autocdk';
2 | export { IRoute, IRouteMap, RouteType } from './routes';
3 | export { IResource, IResourceMap, ResourceType } from './resources';
4 |
5 |
--------------------------------------------------------------------------------
/lib/resources.ts:
--------------------------------------------------------------------------------
1 | import { parse } from 'path';
2 | import { IRouteMap, RouteType } from './routes';
3 | import { Config } from './config';
4 |
5 | export enum ResourceType {
6 | RESOURCE = 'RESOURCE',
7 | METHOD = 'METHOD'
8 | }
9 |
10 | export interface IResourceMap {
11 | name: string;
12 | path: string;
13 | assetPath: string;
14 | type: ResourceType;
15 | children?: IResource;
16 | }
17 |
18 | export interface IResource {
19 | [key: string]: IResourceMap;
20 | }
21 |
22 | export const constructResourceMap = (route: IRouteMap, targetDirectory: string, config: Config): IResourceMap => {
23 | if (config.debug) {
24 | console.log(`Constructing ResourceMap for: ${route.name}`);
25 | }
26 |
27 | const parsed = parse(route.path);
28 |
29 | let adjustedDir = parsed.dir;
30 | if (config.includeRoot) {
31 | adjustedDir = parsed.dir.startsWith(targetDirectory) ? parsed.dir.slice(targetDirectory.length): parsed.dir;
32 | }
33 |
34 | const assetPath = `${adjustedDir}/${parsed.name}`;
35 |
36 | if (route.type === RouteType.DIRECTORY) {
37 | const item: IResourceMap = {
38 | name: parsed.name,
39 | path: route.path,
40 | assetPath: assetPath,
41 | type: ResourceType.RESOURCE
42 | }
43 | if (route.children) {
44 | const children = route.children;
45 | item.children = Object.keys(children)
46 | .reduce((acc: IResource, key) => {
47 | acc[key] = constructResourceMap(children[key], targetDirectory, config);
48 | return acc
49 | }, {})
50 | }
51 | return item
52 | } else {
53 | return {
54 | name: parsed.name,
55 | path: route.path,
56 | assetPath: assetPath,
57 | type: ResourceType.METHOD
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/routes.ts:
--------------------------------------------------------------------------------
1 | import { directoryTree, isNotFound } from './utils';
2 | import { Config } from './config';
3 |
4 | export enum RouteType {
5 | DIRECTORY='DIRECTORY',
6 | FILE='FILE'
7 | }
8 |
9 | export interface IRouteMap {
10 | children?: IRoute;
11 | type: RouteType;
12 | name: string;
13 | path: string;
14 | relativePath: string;
15 | }
16 |
17 | export interface IRoute {
18 | [key: string]: IRouteMap;
19 | }
20 |
21 | export const constructRouteMap = async (dir: string, config: Config): Promise => {
22 | if (config.debug) {
23 | console.log(`Constructing RouteMap for: ${dir}`);
24 | }
25 | try {
26 | return await directoryTree(dir);
27 | }catch (e) {
28 | if (isNotFound(e)) {
29 | return Promise.reject(`${dir} does not exist in ${config.workingDirectory}`);
30 | } else {
31 | return Promise.reject(e);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs';
2 | import { basename, join } from 'path';
3 | import { RouteType, IRouteMap } from './routes';
4 |
5 | export async function directoryTree(dir: string): Promise {
6 | const root = basename(dir);
7 |
8 | const item: IRouteMap = {
9 | type: RouteType.DIRECTORY,
10 | name: root,
11 | path: dir,
12 | relativePath: `./${dir}`
13 | }
14 |
15 | for await (const path of await fs.opendir(dir)) {
16 | if (!(item.children)) {
17 | item.children = {};
18 | }
19 | const name = path.name;
20 | const fullPath = join(dir, name);
21 | if (path.isDirectory()) {
22 | item.children[name] = await directoryTree(fullPath);
23 | } else if (path.isFile()) {
24 | item.children[name] = {
25 | type: RouteType.FILE,
26 | name,
27 | path: fullPath,
28 | relativePath: `./${fullPath}`
29 | }
30 | } else {
31 | continue;
32 | }
33 | }
34 | return item;
35 | }
36 |
37 | export const isNotFound = (error: any) => {
38 | return error.code === 'ENOENT';
39 | };
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auto-cdk",
3 | "version": "0.1.6",
4 | "description": "Effortless APIs with CDK",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "repository": "git@github.com:wulfmann/cdk-express.git",
8 | "author": "Joe Snell ",
9 | "license": "MIT",
10 | "private": false,
11 | "bin": {
12 | "auto-cdk": "bin/auto-cdk"
13 | },
14 | "devDependencies": {
15 | "@aws-cdk/assert": "1.44.0",
16 | "@types/jest": "^25.2.3",
17 | "@types/node": "^14.0.9",
18 | "@types/webpack": "^4.41.17",
19 | "@types/yargs": "^15.0.5",
20 | "aws-cdk": "1.44.0",
21 | "jest": "^26.0.1",
22 | "prettier": "^2.0.5",
23 | "ts-jest": "^26.1.0",
24 | "ts-node": "^8.10.2",
25 | "tslint": "^6.1.2",
26 | "typescript": "^3.9.3"
27 | },
28 | "scripts": {
29 | "build:source": "tsc",
30 | "build:bin": "chmod +x bin/*",
31 | "build": "yarn build:source && yarn build:bin",
32 | "watch": "tsc -w",
33 | "cdk": "cdk",
34 | "test": "jest",
35 | "ts-node": "ts-node",
36 | "lint": "tslint '{src/**/*,test/**/*}.ts'",
37 | "format": "prettier '{src/**/*,test/**/*}.ts'"
38 | },
39 | "dependencies": {
40 | "@aws-cdk/aws-apigateway": "1.44.0",
41 | "@aws-cdk/aws-lambda": "1.44.0",
42 | "@aws-cdk/core": "1.44.0",
43 | "arg": "^4.1.3",
44 | "clean-webpack-plugin": "^3.0.0",
45 | "webpack": "^4.43.0"
46 | },
47 | "prettier": {
48 | "trailingComma": "es5",
49 | "tabWidth": 4,
50 | "semi": true,
51 | "singleQuote": true
52 | },
53 | "jest": {
54 | "roots": [
55 | ""
56 | ],
57 | "testMatch": [
58 | "/test/**/*.test.ts"
59 | ],
60 | "transform": {
61 | "^.+\\.ts$": "ts-jest"
62 | },
63 | "testPathIgnorePatterns": [
64 | "/node_modules/",
65 | "/lib/"
66 | ]
67 | },
68 | "files": [
69 | "lib/**/*",
70 | "cli/**/*",
71 | "bin/**/*"
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/test/autocdk.test.ts:
--------------------------------------------------------------------------------
1 | import { AutoCdk } from '../lib/autocdk';
2 | import { ResourceType } from '../lib/resources';
3 | import { RouteType } from '../lib/routes';
4 | import { Environment } from '../lib/config';
5 |
6 | describe('autocdk.ts', () => {
7 | it('constructRoutes', async () => {
8 | const app = new AutoCdk('MyApp', Environment.DEVELOPMENT, {
9 | config: { rootDirectory: 'test/mock' },
10 | });
11 | const res = await app.constructRoutes();
12 |
13 | expect(res).toEqual({
14 | name: 'mock',
15 | type: RouteType.DIRECTORY,
16 | path: 'test/mock',
17 | relativePath: './test/mock',
18 | children: {
19 | api: {
20 | name: 'api',
21 | type: RouteType.DIRECTORY,
22 | path: 'test/mock/api',
23 | relativePath: './test/mock/api',
24 | children: {
25 | '{id}': {
26 | name: '{id}',
27 | type: RouteType.DIRECTORY,
28 | path: 'test/mock/api/{id}',
29 | relativePath: './test/mock/api/{id}',
30 | children: {
31 | 'index.ts': {
32 | name: 'index.ts',
33 | path: 'test/mock/api/{id}/index.ts',
34 | relativePath:
35 | './test/mock/api/{id}/index.ts',
36 | type: RouteType.FILE,
37 | },
38 | 'settings.ts': {
39 | name: 'settings.ts',
40 | path: 'test/mock/api/{id}/settings.ts',
41 | relativePath:
42 | './test/mock/api/{id}/settings.ts',
43 | type: RouteType.FILE,
44 | },
45 | },
46 | },
47 | another: {
48 | name: 'another',
49 | path: 'test/mock/api/another',
50 | relativePath: './test/mock/api/another',
51 | type: RouteType.DIRECTORY,
52 | children: {
53 | 'test.ts': {
54 | name: 'test.ts',
55 | path: 'test/mock/api/another/test.ts',
56 | relativePath:
57 | './test/mock/api/another/test.ts',
58 | type: RouteType.FILE,
59 | },
60 | },
61 | },
62 | 'index.ts': {
63 | name: 'index.ts',
64 | path: 'test/mock/api/index.ts',
65 | relativePath: './test/mock/api/index.ts',
66 | type: RouteType.FILE,
67 | },
68 | },
69 | },
70 | },
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/build/webpack/entries.test.ts:
--------------------------------------------------------------------------------
1 | import { createEntrypoints } from '../../../lib/build/webpack/entrypoints';
2 | import { constructRouteMap } from '../../../lib/routes';
3 | import { Config, Environment } from '../../../lib/config';
4 |
5 | describe('build/webpack/entries.ts', () => {
6 | it('createEntrypoints no includeRoot', async () => {
7 | const config = new Config({ env: Environment.DEVELOPMENT });
8 | const routes = await constructRouteMap('./test/mock', config);
9 | const entries = createEntrypoints(routes, config.rootDirectory, config);
10 | expect(entries).toEqual({
11 | 'test/mock/api/another/test': './test/mock/api/another/test.ts',
12 | 'test/mock/api/index': './test/mock/api/index.ts',
13 | 'test/mock/api/{id}/index': './test/mock/api/{id}/index.ts',
14 | 'test/mock/api/{id}/settings': './test/mock/api/{id}/settings.ts',
15 | });
16 | });
17 | it('createEntrypoints includeRoot', async () => {
18 | const config = new Config({
19 | env: Environment.DEVELOPMENT,
20 | includeRoot: true,
21 | });
22 | const routes = await constructRouteMap('./test/mock', config);
23 | const entries = createEntrypoints(routes, config.rootDirectory, config);
24 | expect(entries).toEqual({
25 | 'test/mock/api/another/test': './test/mock/api/another/test.ts',
26 | 'test/mock/api/index': './test/mock/api/index.ts',
27 | 'test/mock/api/{id}/index': './test/mock/api/{id}/index.ts',
28 | 'test/mock/api/{id}/settings': './test/mock/api/{id}/settings.ts',
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/mock/api/another/test.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/test/mock/api/another/test.ts
--------------------------------------------------------------------------------
/test/mock/api/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/test/mock/api/index.ts
--------------------------------------------------------------------------------
/test/mock/api/{id}/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/test/mock/api/{id}/index.ts
--------------------------------------------------------------------------------
/test/mock/api/{id}/settings.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wulfmann/auto-cdk/9bbbaaf1fc535c88bb7cf8551700fbbba8f00c06/test/mock/api/{id}/settings.ts
--------------------------------------------------------------------------------
/test/routes.test.ts:
--------------------------------------------------------------------------------
1 | import { RouteType, constructRouteMap } from '../lib/routes';
2 | import { Config, Environment } from '../lib/config';
3 |
4 | describe('routes.ts', () => {
5 | it('constructRouteMap', async () => {
6 | const config = new Config({ env: Environment.DEVELOPMENT });
7 | const res = await constructRouteMap('test/mock', config);
8 | expect(res).toEqual({
9 | name: 'mock',
10 | type: RouteType.DIRECTORY,
11 | path: 'test/mock',
12 | relativePath: './test/mock',
13 | children: {
14 | api: {
15 | type: RouteType.DIRECTORY,
16 | name: 'api',
17 | path: 'test/mock/api',
18 | relativePath: './test/mock/api',
19 | children: {
20 | '{id}': {
21 | name: '{id}',
22 | type: RouteType.DIRECTORY,
23 | path: 'test/mock/api/{id}',
24 | relativePath: './test/mock/api/{id}',
25 | children: {
26 | 'index.ts': {
27 | name: 'index.ts',
28 | path: 'test/mock/api/{id}/index.ts',
29 | relativePath:
30 | './test/mock/api/{id}/index.ts',
31 | type: RouteType.FILE,
32 | },
33 | 'settings.ts': {
34 | name: 'settings.ts',
35 | path: 'test/mock/api/{id}/settings.ts',
36 | relativePath:
37 | './test/mock/api/{id}/settings.ts',
38 | type: RouteType.FILE,
39 | },
40 | },
41 | },
42 | another: {
43 | name: 'another',
44 | path: 'test/mock/api/another',
45 | relativePath: './test/mock/api/another',
46 | type: RouteType.DIRECTORY,
47 | children: {
48 | 'test.ts': {
49 | name: 'test.ts',
50 | path: 'test/mock/api/another/test.ts',
51 | relativePath:
52 | './test/mock/api/another/test.ts',
53 | type: RouteType.FILE,
54 | },
55 | },
56 | },
57 | 'index.ts': {
58 | name: 'index.ts',
59 | path: 'test/mock/api/index.ts',
60 | relativePath: './test/mock/api/index.ts',
61 | type: RouteType.FILE,
62 | },
63 | },
64 | },
65 | },
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/test/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { isNotFound } from '../lib/utils';
2 |
3 | describe('utils.ts', () => {
4 | it('isNotFound returns true', () => {
5 | const testError = {
6 | code: 'ENOENT',
7 | };
8 | expect(isNotFound(testError)).toBeTruthy();
9 | });
10 | it('isNotFound returns false', () => {
11 | const testError = {
12 | code: 'NOTENOENT',
13 | };
14 | expect(isNotFound(testError)).toBeFalsy();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "resolveJsonModule": true,
4 | "target": "ES2018",
5 | "module": "commonjs",
6 | "lib": [
7 | "es2018"
8 | ],
9 | "declaration": true,
10 | "strict": true,
11 | "noImplicitAny": true,
12 | "strictNullChecks": true,
13 | "noImplicitThis": true,
14 | "alwaysStrict": true,
15 | "noUnusedLocals": false,
16 | "noUnusedParameters": false,
17 | "noImplicitReturns": true,
18 | "noFallthroughCasesInSwitch": false,
19 | "inlineSourceMap": true,
20 | "inlineSources": true,
21 | "experimentalDecorators": true,
22 | "strictPropertyInitialization": false,
23 | "typeRoots": [
24 | "./node_modules/@types"
25 | ]
26 | },
27 | "include": [
28 | "src/**/*.ts",
29 | "bin/**/*.ts",
30 | "cli/**/*.ts"
31 | ],
32 | "exclude": [
33 | "cdk.out",
34 | "node_modules",
35 | "**/*.test.ts"
36 | ]
37 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {},
7 | "rules": {},
8 | "rulesDirectory": []
9 | }
10 |
--------------------------------------------------------------------------------