├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── ci.yml
├── .gitignore
├── .terranextrc
├── .versionrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin
└── terranext.js
├── integration
├── .DS_Store
├── .gitignore
├── app
│ ├── build.js
│ ├── next.config.js
│ ├── pages
│ │ ├── [pageId]
│ │ │ └── index.js
│ │ ├── about-us
│ │ │ └── index.js
│ │ ├── blogPost.js
│ │ ├── contact.js
│ │ └── index.js
│ └── routes.js
├── aws
│ └── api.tf
└── terraform
├── package.json
├── pnpm-lock.yaml
├── rome.json
├── src
├── compatLayer.js
├── configuration.js
├── constants.js
├── errors
│ ├── emptyConfigurationError.js
│ ├── errors.d.ts
│ ├── folderNotFoundError.js
│ ├── incorretRoutesError.js
│ ├── invalidMemorySize.js
│ ├── invalidTimeout.js
│ ├── missingKeyError.js
│ ├── providerNotSupported.js
│ └── validationError.js
├── index.d.ts
├── index.js
├── providers
│ ├── aws
│ │ ├── aws.declarations.ts
│ │ ├── awsConfig.js
│ │ ├── declarations.d.ts
│ │ ├── index.js
│ │ └── resources
│ │ │ ├── gateway.js
│ │ │ ├── gatewayIntegration.js
│ │ │ ├── gatewayMethod.js
│ │ │ ├── gatewayResource.js
│ │ │ ├── lambda.js
│ │ │ ├── lambdaPermission.js
│ │ │ ├── lambdaProperties.js
│ │ │ └── lambdaZip.js
│ └── baseProvider.js
├── shared.js
└── utils.js
├── tests
├── configuration.test.js
├── providers
│ └── aws
│ │ ├── __fixtures__
│ │ ├── .next
│ │ │ └── serverless
│ │ │ │ └── pages
│ │ │ │ ├── boar.js
│ │ │ │ ├── contact-us
│ │ │ │ └── index.js
│ │ │ │ └── index.js
│ │ ├── next9
│ │ │ ├── .next
│ │ │ │ └── serverless
│ │ │ │ │ └── pages
│ │ │ │ │ ├── boar.js
│ │ │ │ │ ├── contact-us
│ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ └── pages
│ │ │ │ └── [foo]
│ │ │ │ ├── [deep]
│ │ │ │ └── index.js
│ │ │ │ ├── [query].js
│ │ │ │ ├── bar.js
│ │ │ │ └── fixed
│ │ │ │ └── index.js
│ │ └── pages
│ │ │ ├── boar.js
│ │ │ ├── contact-us
│ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── __snapshots__
│ │ └── awsResources.test.js.snap
│ │ ├── awsResources.test.js
│ │ ├── gatewayIntegration.test.js
│ │ ├── gatewayMethod.test.js
│ │ ├── gatewayResource.test.js
│ │ ├── lambdaPermission.test.js
│ │ ├── lambdaProperties.test.js
│ │ ├── lambdaZip.test.js
│ │ └── shared.test.js
└── terranext.test.js
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | tab_width = 2
4 |
5 | indent_style = tab
6 |
7 | end_of_line = lf
8 |
9 | [*.{md}]
10 | indent_style = space
11 | indent_size = 2
12 |
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js text eol=lf
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | # Issue
11 |
12 | ## Expected Behavior
13 |
14 | ## Actual Behavior
15 |
16 | ## Steps to Reproduce the Problem
17 |
18 |
19 | ## Specifications
20 |
21 | - Version:
22 | - Platform:
23 | - Subsystem:
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Feature
2 |
3 | Fixes #
4 |
5 | ## Proposed Changes
6 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | matrix:
17 | node-version: [16.x, 14.x]
18 |
19 | steps:
20 | - uses: actions/checkout@v1
21 | - name: Use Node.js ${{ matrix.node-version }}
22 | uses: actions/setup-node@v1
23 | with:
24 | node-version: ${{ matrix.node-version }}
25 | - name: Install pnpm
26 | run: curl -L https://pnpm.js.org/pnpm.js | node - add --global pnpm
27 | - name: pnpm install
28 | run: pnpm install
29 | - name: Linting
30 | run: pnpm run ci
31 | - name: Unit Test
32 | run: pnpm run test:ci-unix
33 | env:
34 | CI: true
35 | - name: Install terraform
36 | run: |
37 | apt-get update && apt-get install -y jq
38 | curl -o terraform.zip https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip && unzip terraform.zip && mv terraform ./integration/aws
39 | - name: Run integration and copy files
40 | run: |
41 | pnpm run integration:build
42 | cp ./integration/app/gateway.terraform.tf.json ./integration/aws;
43 | cp ./integration/app/lambdas.terraform.tf.json ./integration/aws;
44 | - name: Validate resources
45 | run: |
46 | terraform init
47 | terraform validate
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | .idea/
61 | .DS_Store
62 |
63 |
--------------------------------------------------------------------------------
/.terranextrc:
--------------------------------------------------------------------------------
1 | {
2 | "routes": {
3 | "prefix": "",
4 | "mappings": [
5 | {
6 | "page": "/blogPost",
7 | "route": "/blog/:url"
8 | },
9 | {
10 | "page": "/contact",
11 | "route": "/contact-us"
12 | }
13 | ]
14 | }
15 | }
--------------------------------------------------------------------------------
/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "types": [
3 | { "type": "feat", "section": "Features" },
4 | { "type": "fix", "section": "Bug Fixes" },
5 | { "type": "chore", "section": "Chores", "hidden": false },
6 | { "type": "docs", "section": "Docs", "hidden": false },
7 | { "type": "style", "hidden": true },
8 | { "type": "refactor", "hidden": true },
9 | { "type": "perf", "hidden": true },
10 | { "type": "test", "hidden": true }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [4.0.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v3.0.0...v4.0.0) (2020-09-12)
6 |
7 |
8 | ### ⚠ BREAKING CHANGES
9 |
10 | * min node version is 12
11 |
12 | ### Features
13 |
14 | * updated to node 12 ([12834e4](https://github.com/ematipico/terraform-nextjs-plugin/commit/12834e455e2fdf6f0f263f27ed4a8fee9ff1dcac))
15 |
16 |
17 | ### Bug Fixes
18 |
19 | * typo (nextDirApp => nextAppDir) ([#295](https://github.com/ematipico/terraform-nextjs-plugin/issues/295)) ([97c4709](https://github.com/ematipico/terraform-nextjs-plugin/commit/97c47096fdbe89b311938b5e29b1338251fde2fb))
20 |
21 |
22 | ### Chores
23 |
24 | * updated various dev dependencies ([76369bf](https://github.com/ematipico/terraform-nextjs-plugin/commit/76369bfd850a73d5ee2d80a253881898223789e7))
25 | * **deps-dev:** bump standard-version from 8.0.0 to 8.0.1 ([#293](https://github.com/ematipico/terraform-nextjs-plugin/issues/293)) ([f95bcd5](https://github.com/ematipico/terraform-nextjs-plugin/commit/f95bcd515fc66563fb17e7d9fd6726beae1992b4))
26 | * fixed prettier warnings ([6e8c34b](https://github.com/ematipico/terraform-nextjs-plugin/commit/6e8c34bf42551d79939c658580da9fb927ddf544))
27 |
28 | ## [3.0.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v2.0.0...v3.0.0) (2020-06-11)
29 |
30 |
31 | ### ⚠ BREAKING CHANGES
32 |
33 | * minimum version of Next.js is now 9.3.2
34 |
35 | ### Features
36 |
37 | * dropped Node.js v8 ([56dbea5](https://github.com/ematipico/terraform-nextjs-plugin/commit/56dbea5aca11ab16bdcdb066610540009d877ebb))
38 |
39 |
40 | ### Bug Fixes
41 |
42 | * updated dependencies ([#199](https://github.com/ematipico/terraform-nextjs-plugin/issues/199)) ([2bd8e66](https://github.com/ematipico/terraform-nextjs-plugin/commit/2bd8e669b323dfa3cf562afa38525fc3b0b8abb5))
43 | * updated libs to fix security problems ([9cc3c96](https://github.com/ematipico/terraform-nextjs-plugin/commit/9cc3c968b9f9ec1e22ba53a7c9910bd30bc35081))
44 |
45 |
46 | ### Chores
47 |
48 | * **release:** 2.1.0 ([801797a](https://github.com/ematipico/terraform-nextjs-plugin/commit/801797a2624a0c713334fb61e90ce0dd8baff2fe))
49 |
50 | ## [2.1.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v2.0.0...v2.1.0) (2020-02-21)
51 |
52 |
53 | ### Features
54 |
55 | * dropped Node.js v8 ([56dbea5](https://github.com/ematipico/terraform-nextjs-plugin/commit/56dbea5aca11ab16bdcdb066610540009d877ebb))
56 |
57 |
58 | ### Bug Fixes
59 |
60 | * case where there's a generated folder by next.js ([e5a638e](https://github.com/ematipico/terraform-nextjs-plugin/commit/e5a638ee42c017335a2d4cf9cf38885f02b52627))
61 | * updated dependencies ([515d184](https://github.com/ematipico/terraform-nextjs-plugin/commit/515d1849f98ded668af03b99a0a5baeee5f8063e))
62 |
63 |
64 | ### Chores
65 |
66 | * moving binary inside integration folder ([2f829a6](https://github.com/ematipico/terraform-nextjs-plugin/commit/2f829a68cbffeaf581d56ac3226c21b3a2bc2cd2))
67 | * removed azure pipelines ([d0c8b90](https://github.com/ematipico/terraform-nextjs-plugin/commit/d0c8b90b1c3d1a7b7983b6fdb4c77531aaa6f9dc))
68 |
69 | ## [2.0.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.3.0...v2.0.0) (2020-01-17)
70 |
71 | * BREAKING CHANGE: dropped Node.js v8
72 |
73 | ## [1.3.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.2.1...v1.3.0) (2019-11-21)
74 |
75 |
76 | ### Features
77 |
78 | * Env vars and more configuration for lambdas ([#137](https://github.com/ematipico/terraform-nextjs-plugin/issues/137)) ([1439426](https://github.com/ematipico/terraform-nextjs-plugin/commit/1439426dee54a481ef96b8db6c5bc2aad42592e5))
79 |
80 |
81 | ### Bug Fixes
82 |
83 | * **ci:** fixed command ([9fb5500](https://github.com/ematipico/terraform-nextjs-plugin/commit/9fb5500f6dbd5dcb377440ab54d4091213bad77a))
84 |
85 |
86 | ### Docs
87 |
88 | * updated documentation ([e0edcdf](https://github.com/ematipico/terraform-nextjs-plugin/commit/e0edcdf363336addf18448cf8f5488f2410c0c79))
89 |
90 |
91 | ### Chores
92 |
93 | * **deps:** [security] bump https-proxy-agent from 2.2.2 to 2.2.4 ([#131](https://github.com/ematipico/terraform-nextjs-plugin/issues/131)) ([3004c41](https://github.com/ematipico/terraform-nextjs-plugin/commit/3004c417c17068bac774e956dc1bbd6f89fdff66))
94 |
95 | ### [1.2.1](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.2.0...v1.2.1) (2019-11-13)
96 |
97 |
98 | ### Bug Fixes
99 |
100 | * restored the compact layer ([#126](https://github.com/ematipico/terraform-nextjs-plugin/issues/126)) ([96d7923](https://github.com/ematipico/terraform-nextjs-plugin/commit/96d7923))
101 |
102 |
103 | ### Chores
104 |
105 | * **deps-dev:** bump @types/node from 12.7.11 to 12.11.7 ([#107](https://github.com/ematipico/terraform-nextjs-plugin/issues/107)) ([37e1271](https://github.com/ematipico/terraform-nextjs-plugin/commit/37e1271))
106 | * added CI build on Github => lint and unit test ([5236da2](https://github.com/ematipico/terraform-nextjs-plugin/commit/5236da2))
107 |
108 | ## [1.2.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.1.0...v1.2.0) (2019-10-04)
109 |
110 |
111 | ### Chores
112 |
113 | * **deps-dev:** bump react from 16.10.1 to 16.10.2 ([#90](https://github.com/ematipico/terraform-nextjs-plugin/issues/90)) ([f363711](https://github.com/ematipico/terraform-nextjs-plugin/commit/f363711))
114 |
115 |
116 | ### Features
117 |
118 | * writing files using native methods ([#92](https://github.com/ematipico/terraform-nextjs-plugin/issues/92)) ([118b30c](https://github.com/ematipico/terraform-nextjs-plugin/commit/118b30c))
119 |
120 | ## [1.1.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.0.1...v1.1.0) (2019-10-02)
121 |
122 |
123 | ### Chores
124 |
125 | * **deps-dev:** bump jest from 24.8.0 to 24.9.0 ([#54](https://github.com/ematipico/terraform-nextjs-plugin/issues/54)) ([b0a7468](https://github.com/ematipico/terraform-nextjs-plugin/commit/b0a7468))
126 | * added config to include more commits ([b03db8c](https://github.com/ematipico/terraform-nextjs-plugin/commit/b03db8c))
127 | * **deps:** [security] bump eslint-utils from 1.3.1 to 1.4.2 ([#64](https://github.com/ematipico/terraform-nextjs-plugin/issues/64)) ([37388fb](https://github.com/ematipico/terraform-nextjs-plugin/commit/37388fb))
128 | * **deps:** [security] bump mixin-deep from 1.3.1 to 1.3.2 ([#66](https://github.com/ematipico/terraform-nextjs-plugin/issues/66)) ([12a0070](https://github.com/ematipico/terraform-nextjs-plugin/commit/12a0070))
129 | * **deps-dev:** bump @types/jest from 24.0.15 to 24.0.16 ([#45](https://github.com/ematipico/terraform-nextjs-plugin/issues/45)) ([760da5e](https://github.com/ematipico/terraform-nextjs-plugin/commit/760da5e))
130 | * **deps-dev:** bump @types/jest from 24.0.16 to 24.0.17 ([0fe3020](https://github.com/ematipico/terraform-nextjs-plugin/commit/0fe3020))
131 | * **deps-dev:** bump @types/jest from 24.0.16 to 24.0.17 ([#48](https://github.com/ematipico/terraform-nextjs-plugin/issues/48)) ([87c356e](https://github.com/ematipico/terraform-nextjs-plugin/commit/87c356e))
132 | * **deps-dev:** bump @types/jest from 24.0.17 to 24.0.18 ([#57](https://github.com/ematipico/terraform-nextjs-plugin/issues/57)) ([ac4f280](https://github.com/ematipico/terraform-nextjs-plugin/commit/ac4f280))
133 | * **deps-dev:** bump babel-eslint from 10.0.1 to 10.0.2 ([#52](https://github.com/ematipico/terraform-nextjs-plugin/issues/52)) ([dccc496](https://github.com/ematipico/terraform-nextjs-plugin/commit/dccc496))
134 | * **deps-dev:** bump babel-eslint from 10.0.2 to 10.0.3 ([#63](https://github.com/ematipico/terraform-nextjs-plugin/issues/63)) ([8d1433e](https://github.com/ematipico/terraform-nextjs-plugin/commit/8d1433e))
135 | * **deps-dev:** bump eslint from 5.16.0 to 6.2.2 ([#60](https://github.com/ematipico/terraform-nextjs-plugin/issues/60)) ([81756b1](https://github.com/ematipico/terraform-nextjs-plugin/commit/81756b1))
136 | * **deps-dev:** bump eslint from 6.2.2 to 6.3.0 ([#68](https://github.com/ematipico/terraform-nextjs-plugin/issues/68)) ([00944af](https://github.com/ematipico/terraform-nextjs-plugin/commit/00944af))
137 | * **deps-dev:** bump eslint-config-prettier from 5.1.0 to 6.0.0 ([#53](https://github.com/ematipico/terraform-nextjs-plugin/issues/53)) ([db028c6](https://github.com/ematipico/terraform-nextjs-plugin/commit/db028c6))
138 | * **deps-dev:** bump eslint-config-prettier from 6.0.0 to 6.1.0 ([#58](https://github.com/ematipico/terraform-nextjs-plugin/issues/58)) ([c79ef3a](https://github.com/ematipico/terraform-nextjs-plugin/commit/c79ef3a))
139 | * **deps-dev:** bump eslint-config-prettier from 6.1.0 to 6.2.0 ([#69](https://github.com/ematipico/terraform-nextjs-plugin/issues/69)) ([d0b73cc](https://github.com/ematipico/terraform-nextjs-plugin/commit/d0b73cc))
140 | * **deps-dev:** bump eslint-config-prettier from 6.2.0 to 6.3.0 ([#71](https://github.com/ematipico/terraform-nextjs-plugin/issues/71)) ([1fda150](https://github.com/ematipico/terraform-nextjs-plugin/commit/1fda150))
141 | * **deps-dev:** bump eslint-plugin-node from 9.1.0 to 9.2.0 ([#67](https://github.com/ematipico/terraform-nextjs-plugin/issues/67)) ([bbe7869](https://github.com/ematipico/terraform-nextjs-plugin/commit/bbe7869))
142 | * **deps-dev:** bump eslint-plugin-node from 9.2.0 to 10.0.0 ([#70](https://github.com/ematipico/terraform-nextjs-plugin/issues/70)) ([e35deb4](https://github.com/ematipico/terraform-nextjs-plugin/commit/e35deb4))
143 | * **deps-dev:** bump eslint-plugin-prettier from 3.1.0 to 3.1.1 ([#76](https://github.com/ematipico/terraform-nextjs-plugin/issues/76)) ([75214e5](https://github.com/ematipico/terraform-nextjs-plugin/commit/75214e5))
144 | * **deps-dev:** bump eslint-plugin-unicorn from 9.1.0 to 9.1.1 ([#51](https://github.com/ematipico/terraform-nextjs-plugin/issues/51)) ([39f20d1](https://github.com/ematipico/terraform-nextjs-plugin/commit/39f20d1))
145 | * **deps-dev:** bump eslint-plugin-unicorn from 9.1.1 to 10.0.0 ([#62](https://github.com/ematipico/terraform-nextjs-plugin/issues/62)) ([5328438](https://github.com/ematipico/terraform-nextjs-plugin/commit/5328438))
146 | * **deps-dev:** bump jest-junit from 6.4.0 to 7.0.0 ([661e7e9](https://github.com/ematipico/terraform-nextjs-plugin/commit/661e7e9))
147 | * **deps-dev:** bump jest-junit from 6.4.0 to 7.0.0 ([#43](https://github.com/ematipico/terraform-nextjs-plugin/issues/43)) ([321d855](https://github.com/ematipico/terraform-nextjs-plugin/commit/321d855))
148 | * **deps-dev:** bump jest-junit from 7.0.0 to 8.0.0 ([#65](https://github.com/ematipico/terraform-nextjs-plugin/issues/65)) ([a768faf](https://github.com/ematipico/terraform-nextjs-plugin/commit/a768faf))
149 | * **deps-dev:** bump react from 16.8.6 to 16.9.0 ([#50](https://github.com/ematipico/terraform-nextjs-plugin/issues/50)) ([6533c71](https://github.com/ematipico/terraform-nextjs-plugin/commit/6533c71))
150 | * **deps-dev:** bump react-dom from 16.8.6 to 16.9.0 ([#49](https://github.com/ematipico/terraform-nextjs-plugin/issues/49)) ([78eec10](https://github.com/ematipico/terraform-nextjs-plugin/commit/78eec10))
151 | * **deps-dev:** bump standard-version from 6.0.1 to 7.0.0 ([71bd550](https://github.com/ematipico/terraform-nextjs-plugin/commit/71bd550))
152 | * **deps-dev:** bump standard-version from 6.0.1 to 7.0.0 ([#44](https://github.com/ematipico/terraform-nextjs-plugin/issues/44)) ([638066c](https://github.com/ematipico/terraform-nextjs-plugin/commit/638066c))
153 |
154 |
155 | ### Features
156 |
157 | * added support for next 9 ([#88](https://github.com/ematipico/terraform-nextjs-plugin/issues/88)) ([ee0a1b7](https://github.com/ematipico/terraform-nextjs-plugin/commit/ee0a1b7))
158 |
159 | ### [1.0.1](https://github.com/ematipico/terraform-nextjs-plugin/compare/v1.0.0...v1.0.1) (2019-07-11)
160 |
161 |
162 |
163 | ## [1.0.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.9.0...v1.0.0) (2019-07-08)
164 |
165 |
166 | ### Bug Fixes
167 |
168 | * set next app dir default value ([#35](https://github.com/ematipico/terraform-nextjs-plugin/issues/35)) ([7b02c66](https://github.com/ematipico/terraform-nextjs-plugin/commit/7b02c66))
169 |
170 |
171 |
172 | ## [0.9.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.8.0...v0.9.0) (2019-07-06)
173 |
174 |
175 | * BREAKING CHANGE: renamed CLI params (#33) ([24efe81](https://github.com/ematipico/terraform-nextjs-plugin/commit/24efe81)), closes [#33](https://github.com/ematipico/terraform-nextjs-plugin/issues/33)
176 |
177 |
178 | ### BREAKING CHANGES
179 |
180 | * renamed CLI params
181 |
182 | - gatewayKey => gateway-key
183 | - lambdaPath => next-app-dir
184 |
185 | * chore: removed next build app
186 | chore: udpated test
187 |
188 | * fix: tests
189 |
190 | * fix: added more info on the error msg
191 |
192 | * fix: path resolution
193 |
194 | * fix: requrie the correct build func
195 |
196 | * fix: correctly call build
197 |
198 |
199 |
200 | ## [0.8.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.6.1...v0.8.0) (2019-07-04)
201 |
202 |
203 | * BREAKING CHANGE: routes not mandatory (#29) ([e5f6e78](https://github.com/ematipico/terraform-nextjs-plugin/commit/e5f6e78)), closes [#29](https://github.com/ematipico/terraform-nextjs-plugin/issues/29)
204 |
205 |
206 | ### BREAKING CHANGES
207 |
208 | * routes not mandatory
209 |
210 | - routes is not mandatory
211 | - added more types to the code
212 | - create routes based on next pages
213 |
214 | * fix: removed .next from ignored files
215 |
216 | * fix: path resolution of lambdas
217 |
218 | - fix path resolution of where the lambdas are
219 | - removed default value from CLI
220 | - handling schema validation in a different way
221 |
222 | * fix: test reviewed
223 |
224 | * fix: path to lambda
225 |
226 |
227 |
228 | ## [0.7.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.6.1...v0.7.0) (2019-07-04)
229 |
230 |
231 | * BREAKING CHANGE: routes not mandatory (#29) ([e5f6e78](https://github.com/ematipico/terraform-nextjs-plugin/commit/e5f6e78)), closes [#29](https://github.com/ematipico/terraform-nextjs-plugin/issues/29)
232 |
233 |
234 | ### BREAKING CHANGES
235 |
236 | * routes not mandatory
237 |
238 | - routes is not mandatory
239 | - added more types to the code
240 | - create routes based on next pages
241 |
242 | * fix: removed .next from ignored files
243 |
244 | * fix: path resolution of lambdas
245 |
246 | - fix path resolution of where the lambdas are
247 | - removed default value from CLI
248 | - handling schema validation in a different way
249 |
250 | * fix: test reviewed
251 |
252 | * fix: path to lambda
253 |
254 |
255 |
256 | ### [0.6.1](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.4.1...v0.6.1) (2019-06-27)
257 |
258 |
259 | ### Bug Fixes
260 |
261 | * added provider to integration test ([2d3dfdb](https://github.com/ematipico/terraform-nextjs-plugin/commit/2d3dfdb))
262 | * better styling ([371efee](https://github.com/ematipico/terraform-nextjs-plugin/commit/371efee))
263 | * compatLayer path ([a222bba](https://github.com/ematipico/terraform-nextjs-plugin/commit/a222bba))
264 | * export correct functions ([e0d6115](https://github.com/ematipico/terraform-nextjs-plugin/commit/e0d6115))
265 | * typing for gateway integration ([598d256](https://github.com/ematipico/terraform-nextjs-plugin/commit/598d256))
266 |
267 |
268 | ### Features
269 |
270 | * added more types ([6e263b3](https://github.com/ematipico/terraform-nextjs-plugin/commit/6e263b3))
271 | * added typings to the functions ([a9d2d63](https://github.com/ematipico/terraform-nextjs-plugin/commit/a9d2d63))
272 | * fixed declarations in lambdas ([da752f0](https://github.com/ematipico/terraform-nextjs-plugin/commit/da752f0))
273 | * reactor based on providers ([786957b](https://github.com/ematipico/terraform-nextjs-plugin/commit/786957b))
274 |
275 |
276 |
277 | ### [0.5.1](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.4.1...v0.5.1) (2019-06-27)
278 |
279 |
280 | ### Bug Fixes
281 |
282 | * added provider to integration test ([2d3dfdb](https://github.com/ematipico/terraform-nextjs-plugin/commit/2d3dfdb))
283 | * better styling ([371efee](https://github.com/ematipico/terraform-nextjs-plugin/commit/371efee))
284 | * compatLayer path ([a222bba](https://github.com/ematipico/terraform-nextjs-plugin/commit/a222bba))
285 | * export correct functions ([e0d6115](https://github.com/ematipico/terraform-nextjs-plugin/commit/e0d6115))
286 | * typing for gateway integration ([598d256](https://github.com/ematipico/terraform-nextjs-plugin/commit/598d256))
287 |
288 |
289 | ### Features
290 |
291 | * added more types ([6e263b3](https://github.com/ematipico/terraform-nextjs-plugin/commit/6e263b3))
292 | * added typings to the functions ([a9d2d63](https://github.com/ematipico/terraform-nextjs-plugin/commit/a9d2d63))
293 | * fixed declarations in lambdas ([da752f0](https://github.com/ematipico/terraform-nextjs-plugin/commit/da752f0))
294 | * reactor based on providers ([786957b](https://github.com/ematipico/terraform-nextjs-plugin/commit/786957b))
295 |
296 |
297 |
298 | ## [0.5.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.4.1...v0.5.0) (2019-06-21)
299 |
300 |
301 | ### Bug Fixes
302 |
303 | * better styling ([d7825fd](https://github.com/ematipico/terraform-nextjs-plugin/commit/d7825fd))
304 | * typing for gateway integration ([94c4e17](https://github.com/ematipico/terraform-nextjs-plugin/commit/94c4e17))
305 |
306 |
307 | ### Features
308 |
309 | * added more types ([eaa526d](https://github.com/ematipico/terraform-nextjs-plugin/commit/eaa526d))
310 | * added typings to the functions ([ad6ec11](https://github.com/ematipico/terraform-nextjs-plugin/commit/ad6ec11))
311 | * fixed declarations in lambdas ([2f1ab5e](https://github.com/ematipico/terraform-nextjs-plugin/commit/2f1ab5e))
312 | * TypeScript types for the main function ([#18](https://github.com/ematipico/terraform-nextjs-plugin/issues/18)) ([2610b7e](https://github.com/ematipico/terraform-nextjs-plugin/commit/2610b7e))
313 |
314 |
315 |
316 | ### [0.4.1](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.4.0...v0.4.1) (2019-06-20)
317 |
318 |
319 | ### Bug Fixes
320 |
321 | * added bash example ([05002b8](https://github.com/ematipico/terraform-nextjs-plugin/commit/05002b8))
322 |
323 |
324 |
325 | ## [0.4.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.3.0...v0.4.0) (2019-06-20)
326 |
327 |
328 | ### Bug Fixes
329 |
330 | * menu documentation ([57b2ae1](https://github.com/ematipico/terraform-nextjs-plugin/commit/57b2ae1))
331 |
332 |
333 | ### Features
334 |
335 | * create CLI utility ([0475466](https://github.com/ematipico/terraform-nextjs-plugin/commit/0475466))
336 | * create CLI utility ([#15](https://github.com/ematipico/terraform-nextjs-plugin/issues/15)) ([2162101](https://github.com/ematipico/terraform-nextjs-plugin/commit/2162101))
337 |
338 |
339 |
340 | ## [0.3.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.2.0...v0.3.0) (2019-06-10)
341 |
342 |
343 | ### Bug Fixes
344 |
345 | * copy files ([653f868](https://github.com/ematipico/terraform-nextjs-plugin/commit/653f868))
346 | * copy single files ([200b3cf](https://github.com/ematipico/terraform-nextjs-plugin/commit/200b3cf))
347 | * correct gateway key ([10d0cd1](https://github.com/ematipico/terraform-nextjs-plugin/commit/10d0cd1))
348 | * correct name ([fd5156f](https://github.com/ematipico/terraform-nextjs-plugin/commit/fd5156f))
349 | * correct path to nextjs build ([cc0838e](https://github.com/ematipico/terraform-nextjs-plugin/commit/cc0838e))
350 | * execute file to generate files ([e80549d](https://github.com/ematipico/terraform-nextjs-plugin/commit/e80549d))
351 | * integration name and how to validate files ([9c0a33a](https://github.com/ematipico/terraform-nextjs-plugin/commit/9c0a33a))
352 | * run next cli ([023236f](https://github.com/ematipico/terraform-nextjs-plugin/commit/023236f))
353 | * script folder ([094f034](https://github.com/ematipico/terraform-nextjs-plugin/commit/094f034))
354 | * split up based on type of tests ([dbd32e2](https://github.com/ematipico/terraform-nextjs-plugin/commit/dbd32e2))
355 |
356 |
357 | ### Features
358 |
359 | * added bash to run ([b66da4a](https://github.com/ematipico/terraform-nextjs-plugin/commit/b66da4a))
360 | * added task via template ([f13b577](https://github.com/ematipico/terraform-nextjs-plugin/commit/f13b577))
361 | * added terraform init command ([45f77c7](https://github.com/ematipico/terraform-nextjs-plugin/commit/45f77c7))
362 | * integration test ([44d2303](https://github.com/ematipico/terraform-nextjs-plugin/commit/44d2303))
363 | * integration test ([#13](https://github.com/ematipico/terraform-nextjs-plugin/issues/13)) ([e19544e](https://github.com/ematipico/terraform-nextjs-plugin/commit/e19544e))
364 | * split integration from normal tests ([4160305](https://github.com/ematipico/terraform-nextjs-plugin/commit/4160305))
365 |
366 |
367 |
368 | ## [0.2.0](https://github.com/ematipico/terraform-nextjs-plugin/compare/v0.1.0...v0.2.0) (2019-06-08)
369 |
370 |
371 | ### Features
372 |
373 | * read configuration from config file ([d75cde3](https://github.com/ematipico/terraform-nextjs-plugin/commit/d75cde3))
374 |
375 |
376 | * BREAKING CHANGE: refactored how params are passed (#9) ([efe1cd7](https://github.com/ematipico/terraform-nextjs-plugin/commit/efe1cd7)), closes [#9](https://github.com/ematipico/terraform-nextjs-plugin/issues/9)
377 |
378 |
379 | ### BREAKING CHANGES
380 |
381 | * refactored how params are passed
382 |
383 |
384 |
385 | ## 0.1.0 (2019-06-07)
386 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Emanuele Stoppa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform nextjs plugin
2 |
3 |
4 |
5 |
6 |
7 |
8 | A plugin to generate terraform configuration from nextjs pages
9 |
10 | [![Build Status][build-status-azure]][build-status-azure-url]
11 | [![Codacy Badge][code-quality]][code-quality-url]
12 | [![npm][npm]][npm-url]
13 | [![Conventional Commits][conventional]][conventional-url]
14 | [![codecov][coverage]][coverage-url]
15 |
16 | ## The reason
17 |
18 | Nextjs supports serverless pages, where it creates files that can be used by some lambdas to render the pages.
19 | Unfortunately, here you are left alone. So here a solution for your troubles.
20 |
21 | - [Installation](#installation)
22 | - [Usage](#usage)
23 | - [Via CLI](#via-cli)
24 | - [Via API](#via-api)
25 | - [Configuration](#configuration)
26 | - [Mapping explained](#mapping-explained)
27 | - [Providers](#providers)
28 | - [AWS](#aws)
29 |
30 | ## Installation
31 |
32 | ```bash
33 | npm i --save-dev @ematipico/terraform-nextjs-plugin
34 | ```
35 |
36 | Or
37 |
38 | ```bash
39 | yarn add --dev @ematipico/terraform-nextjs-plugin
40 | ```
41 |
42 | **This package requires at least Next v8.**
43 |
44 | ## Usage
45 |
46 | ```bash
47 | terranext --provider=AWS
48 | ```
49 |
50 | This library supports [cosmiconfig](https://github.com/davidtheclark/cosmiconfig): you just need to have a file called `terranextrc` that matches the criteria. This repository has [one](./terranextrc).
51 |
52 | ### Via CLI
53 |
54 | You can use the simple CLI available. At moment you _can't_ pass the `routes` parameter, you will need to use the config object or use the [API](#via-api).
55 |
56 | Using the CLI will automatically emit the configuration files.
57 |
58 | _**Arguments passed via CLI will *override* the ones that are defined inside the config file**_.
59 |
60 | ```bash
61 | terranext --provider=AWS --gateway-key=CustomKey --next-dir-app=../../nextjs-project/
62 | ```
63 |
64 | Or you can use the aliases:
65 |
66 | ```bash
67 | terranext --provider=AWS -g=CustomKey -p=../../nextjs-project/
68 | ```
69 |
70 | ### Help section
71 |
72 | ```block
73 |
74 | Usage
75 | $ terranext
76 |
77 | Options
78 | --gateway-key, -g The API Gateway key of the project. Default is "Terranext"
79 | --next-app-dir, -d The path that Terraform CLI has to follow to reach the nextjs project.
80 | --provider The Cloud provider to use when exporting the configuration
81 | --env A way for passing environment variables to the lambdas
82 |
83 |
84 | Examples
85 | $ terranext
86 | $ terranext --gateway-key=CustomKey --next-app-dir=../../nextjs-project/
87 | $ terranext --provider=AWS --next-app-dir=../../nextjs-project/
88 | $ terranext -g=CustomKey -d=../../nextjs-project/
89 | $ terranext --env="DEBUG,express:*" --env="API_KEY,1234"
90 | ```
91 |
92 | ### Via API
93 |
94 | ```js
95 | const generateResources = require("@ematipico/terraform-nextjs-plugin");
96 |
97 | const configuration = {
98 | gatewayKey: "AmazingWebsite",
99 | lambdaPath: "../../project/build",
100 | provider: "AWS",
101 | env: [
102 | {
103 | key: "KEY",
104 | value: "2940"
105 | }
106 | ]
107 | };
108 |
109 | const resources = generateResources(configuration); // inside resources you have the terraform json configuration
110 | generateResources(configuration, true); // it creates two files
111 | ```
112 |
113 | If the second argument is a boolean and it's `true`, the library will create two files:
114 |
115 | - `gateway.terraform.tf.json`
116 | - `lambdas.terraform.tf.json`
117 |
118 | Having a suffix with `.tf.` will tell automatically to `terraform` that should be validated and planned.
119 | It will be up to you to consume them in a proper way.
120 |
121 | ## Configuration
122 |
123 | | Name | Type | Default | Description |
124 | | ------------ | ------------------------------ | ------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
125 | | `gatewayKey` | `string` | Terranext | A name that will be prefixed to your resources. Usually it's the project name. _Default value: `Terranext`_. |
126 | | `provider` | `string` | Must be provided | The Cloud Provider. Based on the value, a different configuration will be exported. Supported providers: `AWS` |
127 | | `nextAppDir` | `string` | Must be provided | This is the path where your Next.js project is. Usually you will run `terraform` CLI from a different project/folder. So you need to tell `terraform` where this folder is. The library will take care of the rest. _Default value: `"./"`_ |
128 | | `routes` | `Array`, `Mapping` | Optional | This is the structure of the routes that describe your pages. |
129 | | `env` | `Array` | Optional | Environments passed via CLI have to be split using `,`: `--env="KEY,VALUE"`. When using the API, you always have to pass an array of objects `{ key: "MyKeyName", "value": "MyKeyValue" }`. **_Environment variables are applied to all the lambdas_** |
130 | | `nodeVersion` | `10` or `12` | `10` | Runtime to use
131 |
132 | ### Mapping explained
133 |
134 | These mappings are only needed if you have custom routes. If you don't, `routes` is not needed as this library is able to create mappings from the files that Nextjs generates.
135 |
136 | Let's say we want to describe the following URLs:
137 |
138 | - `/about-us/contacts`
139 | - `/about-us/the-company`
140 | - `/blog/first-blog-post`
141 | - `/blog/second-blog-post`
142 | - `/credits?hideComments`: here, `hideComments` is not mandatory. If it is mandatory, it will be marked `true` in the configuration
143 |
144 | ```js
145 | const routes = [
146 | {
147 | prefix: "/about-us",
148 | mappings: [
149 | {
150 | route: "/contacts", // the URL
151 | page: "/companyContacts" // the nextjs file, inside pages folder, that is responsible to render this page
152 | },
153 | {
154 | route: "/the-company",
155 | page: "/aboutTheCompany"
156 | }
157 | ]
158 | },
159 | {
160 | prefix: "",
161 | mappings: [
162 | {
163 | route: "/blog/:url",
164 | page: "/blogPost"
165 | },
166 | {
167 | route: "/credits",
168 | page: "/credits",
169 | params: {
170 | hideComments: false
171 | }
172 | }
173 | ]
174 | }
175 | ];
176 | ```
177 |
178 | ## Providers
179 |
180 | At the moment the project supports only AWS but it's up to support more providers in the future.
181 |
182 | ### AWS
183 |
184 | Once you generate the resource files, you will need to consume them. Also, you will need to create the following resource:
185 |
186 | ```hcl
187 | resource "aws_api_gateway_rest_api" "CustomKey" {
188 | name = "WebApi"
189 | description = "Web API"
190 | }
191 |
192 | locals {
193 | groupname = "WebApi"
194 | lambda_iam_role = "arn:aws:iam::202020202020:role/lambda_execution_role"
195 | aws_region = "${data.aws_region.current.name}"
196 | }
197 | ```
198 |
199 | Please check the [integration](/integration/aws/api.tf) testing to see how to consume the configuration.
200 |
201 | [build-status-azure]: https://myburning.visualstudio.com/terraform-nextjs-plugin/_apis/build/status/ematipico.terraform-nextjs-plugin?branchName=master
202 | [build-status-azure-url]: https://myburning.visualstudio.com/terraform-nextjs-plugin/_build/latest?definitionId=1&branchName=master
203 | [npm]: https://img.shields.io/npm/v/@ematipico/terraform-nextjs-plugin.svg
204 | [npm-url]: https://www.npmjs.com/package/@ematipico/terraform-nextjs-plugin
205 | [code-quality]: https://api.codacy.com/project/badge/Grade/f77ac77e550449ffb821cd6e7cc4fd72
206 | [code-quality-url]: https://www.codacy.com/app/ematipico/terraform-nextjs-plugin?utm_source=github.com&utm_medium=referral&utm_content=ematipico/terraform-nextjs-plugin&utm_campaign=Badge_Grade
207 | [conventional]: https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg
208 | [conventional-url]: https://conventionalcommits.org
209 | [coverage]: https://codecov.io/gh/ematipico/terraform-nextjs-plugin/branch/master/graph/badge.svg
210 | [coverage-url]: https://codecov.io/gh/ematipico/terraform-nextjs-plugin
211 |
--------------------------------------------------------------------------------
/bin/terranext.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const { cosmiconfig } = require("cosmiconfig");
4 | const meow = require("meow");
5 | const generateResources = require("../src");
6 |
7 | const explorer = cosmiconfig("terranext");
8 |
9 | const cli = meow(
10 | `
11 | Usage
12 | $ terranext
13 |
14 | Options
15 | --gateway-key, -g The API Gateway key of the project. Default is "Terranext"
16 | --next-app-dir, -d The path that Terraform CLI has to follow to reach the nextjs project.
17 | --provider The Cloud provider to use when exporting the configuration
18 | --env A way for passing environment variables to the lambdas
19 |
20 | Examples
21 | $ terranext
22 | $ terranext --gateway-key=CustomKey --next-app-dir=../../nextjs-project/
23 | $ terranext --provider=AWS --next-app-dir=../../nextjs-project/
24 | $ terranext -g=CustomKey -d=../../nextjs-project/
25 | $ terranext --env="DEBUG,express:*" --env="API_KEY,1234"
26 | `,
27 | {
28 | flags: {
29 | gatewayKey: {
30 | type: "string",
31 | default: "Terranext",
32 | alias: "g",
33 | },
34 | // eslint-disable-next-line unicorn/prevent-abbreviations
35 | nextAppDir: {
36 | type: "string",
37 | alias: "d",
38 | default: "./",
39 | },
40 | provider: {
41 | type: "string",
42 | },
43 | env: {
44 | type: "string",
45 | isMultiple: true,
46 | },
47 | },
48 | },
49 | );
50 |
51 | explorer
52 | .search()
53 | .then(async (result) => {
54 | const { gatewayKey, nextAppDir, provider, env } = cli.flags;
55 | let parsedEnvs;
56 | if (env) {
57 | parsedEnvs = parseEnv(env, {});
58 | }
59 | const options = {
60 | ...result.config,
61 | gatewayKey,
62 | nextAppDir,
63 | provider,
64 | env: parsedEnvs,
65 | };
66 | await generateResources(options, true);
67 | })
68 | .catch((error) => {
69 | // eslint-disable-next-line no-console
70 | console.error(error);
71 | process.exit(1);
72 | // Do something constructive.
73 | });
74 |
75 | function parseEnv(unparsedEnv) {
76 | if (Array.isArray(unparsedEnv)) {
77 | return unparsedEnv.map((env) => {
78 | const splitEnv = env.split(",");
79 | return {
80 | key: splitEnv[0],
81 | value: splitEnv[1],
82 | };
83 | });
84 | }
85 |
86 | const splitEnv = unparsedEnv.split(",");
87 |
88 | return [
89 | {
90 | key: splitEnv[0],
91 | value: splitEnv[1],
92 | },
93 | ];
94 | }
95 |
--------------------------------------------------------------------------------
/integration/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/integration/.DS_Store
--------------------------------------------------------------------------------
/integration/.gitignore:
--------------------------------------------------------------------------------
1 | .next/
2 | .terraform/
3 | gateway.terraform.tf.json
4 | lambdas.terraform.tf.json
--------------------------------------------------------------------------------
/integration/app/build.js:
--------------------------------------------------------------------------------
1 | const generateResources = require("../../src");
2 | const routes = require("./routes");
3 | generateResources(
4 | {
5 | gatewayKey: "CustomKey",
6 | nextAppDir: "../app/",
7 | provider: "AWS",
8 | routes,
9 | env: [
10 | {
11 | key: "DEBUG",
12 | value: "express:*",
13 | },
14 | ],
15 | memorySize: "1024",
16 | timeout: "180",
17 | },
18 | true,
19 | );
20 |
--------------------------------------------------------------------------------
/integration/app/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | target: "serverless",
3 | };
4 |
--------------------------------------------------------------------------------
/integration/app/pages/[pageId]/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function BlogPage() {
4 | return Blog post page
;
5 | }
6 |
7 | BlogPage.getInitialProps = function () {
8 | return {
9 | name: "foo",
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/integration/app/pages/about-us/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function AboutUs() {
4 | return Contacts page
;
5 | }
6 |
7 | AboutUs.getInitialProps = function () {
8 | return {
9 | name: "foo",
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/integration/app/pages/blogPost.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function BlogPost() {
4 | return Blog post page
;
5 | }
6 |
7 | BlogPost.getInitialProps = function () {
8 | return {
9 | name: "foo",
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/integration/app/pages/contact.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Contacts() {
4 | return Contacts page
;
5 | }
6 |
7 | Contacts.getInitialProps = function () {
8 | return {
9 | name: "foo",
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/integration/app/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Index() {
4 | return Index page
;
5 | }
6 |
7 | Index.getInitialProps = function () {
8 | return {
9 | name: "foo",
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/integration/app/routes.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | prefix: "",
3 | mappings: [
4 | {
5 | page: "/blogPost",
6 | route: "/blog/:url",
7 | },
8 | {
9 | page: "/contact",
10 | route: "/contact-us",
11 | },
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/integration/aws/api.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | profile = "token"
3 | region = "eu-west-1"
4 | version = "~> 2.0"
5 | }
6 |
7 | locals {
8 | groupname = "WebApi"
9 |
10 | lambda_iam_role = "arn:aws:iam::202020202020:role/lambda_execution_role"
11 |
12 | aws_region = "${data.aws_region.current.name}"
13 | }
14 |
15 | variable "env" {
16 | default = "dev"
17 | }
18 |
19 | resource "aws_api_gateway_rest_api" "CustomKey" {
20 | name = "WebApi"
21 | description = "Web API"
22 | }
23 |
24 | resource "aws_api_gateway_stage" "CustomKey" {
25 | stage_name = "dev"
26 | rest_api_id = "${aws_api_gateway_rest_api.CustomKey.id}"
27 | deployment_id = "${aws_api_gateway_deployment.CustomKey.id}"
28 | }
29 |
30 | resource "aws_api_gateway_deployment" "CustomKey" {
31 | rest_api_id = "${aws_api_gateway_rest_api.CustomKey.id}"
32 | stage_name = "dev"
33 | }
34 |
--------------------------------------------------------------------------------
/integration/terraform:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/integration/terraform
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ematipico/terraform-nextjs-plugin",
3 | "version": "4.0.0",
4 | "description": "A nextjs plugin to generate cloud providers configuration for Terraform",
5 | "main": "src/index.js",
6 | "bin": "bin/terranext.js",
7 | "scripts": {
8 | "test": "jest --watch",
9 | "test:ci-unix": "CI=true jest --colors --coverage --maxWorkers=4 --reporters=default --reporters=jest-junit && codecov -t 260305d4-6357-42d2-a73c-4c9b255d278a",
10 | "test:ci-win": "jest --colors --coverage --maxWorkers=4 --reporters=default --reporters=jest-junit",
11 | "release": "standard-version",
12 | "ci": "rome ci . && pnpm run lint:types",
13 | "lint": "pnpm run lint:files && pnpm run lint:types",
14 | "lint:files": "rome check ./src ./bin ./tests",
15 | "lint:fix": "pnpm run lint:files -- --apply-suggested",
16 | "lint:types": "tsc",
17 | "integration:build": "cd ./integration/app; node ./build.js"
18 | },
19 | "typings": "./src/index.d.ts",
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/ematipico/terraform-nextjs-plugin.git"
23 | },
24 | "keywords": [
25 | "nextjs",
26 | "terraform",
27 | "serverless",
28 | "plugin",
29 | "aws",
30 | "cloud",
31 | "providers"
32 | ],
33 | "author": "Emanuele Stoppa",
34 | "license": "MIT",
35 | "bugs": {
36 | "url": "https://github.com/ematipico/terraform-nextjs-plugin/issues"
37 | },
38 | "homepage": "https://github.com/ematipico/terraform-nextjs-plugin#readme",
39 | "devDependencies": {
40 | "@types/jest": "26.0.20",
41 | "@types/node": "14.14.28",
42 | "codecov": "3.8.1",
43 | "jest": "26.6.3",
44 | "jest-junit": "12.0.0",
45 | "next": "9.4.4",
46 | "react": "16.13.1",
47 | "react-dom": "16.13.1",
48 | "rome": "0.10.1-next",
49 | "standard-version": "9.1.0",
50 | "typescript": "4.1.5"
51 | },
52 | "dependencies": {
53 | "cosmiconfig": "7.0.0",
54 | "meow": "9.0.0"
55 | },
56 | "engines": {
57 | "node": ">= 12.x.x"
58 | },
59 | "peerDependencies": {
60 | "next": ">=9.3.2"
61 | },
62 | "files": [
63 | "bin/*",
64 | "src/*"
65 | ]
66 | }
--------------------------------------------------------------------------------
/rome.json:
--------------------------------------------------------------------------------
1 | {
2 | "linter": {
3 | "enabled": true,
4 | "ignore": [
5 | "integration/app/.next"
6 | ],
7 | "rules": {
8 | "recommended": true
9 | }
10 | },
11 | "formatter": {
12 | "lineWidth": 140,
13 | "ignore": [
14 | "integration/app/.next"
15 | ]
16 | }
17 | }
--------------------------------------------------------------------------------
/src/compatLayer.js:
--------------------------------------------------------------------------------
1 | // @ts-nocheck
2 | /**
3 | * Credits to https://github.com/danielcondemarin
4 | * https://github.com/danielcondemarin/serverless-nextjs-plugin/blob/master/packages/next-aws-lambda/lib/compatLayer.js
5 | */
6 | const Stream = require("stream");
7 | const queryString = require("querystring");
8 |
9 | const requestResponseMapper = (event, callback) => {
10 | const base64Support = process.env.BINARY_SUPPORT === "yes";
11 | const response = {
12 | body: Buffer.from(""),
13 | isBase64Encoded: base64Support,
14 | statusCode: 200,
15 | multiValueHeaders: {},
16 | };
17 |
18 | const request = new Stream.Readable();
19 | request.url = (event.requestContext.path || event.path || "").replace(new RegExp(`^/${event.requestContext.stage}`), "");
20 |
21 | let qs = "";
22 |
23 | if (event.multiValueQueryStringParameters) {
24 | qs += queryString.stringify(event.multiValueQueryStringParameters);
25 | }
26 |
27 | if (event.pathParameters) {
28 | const pathParametersQs = queryString.stringify(event.pathParameters);
29 |
30 | if (qs.length > 0) {
31 | qs += `&${pathParametersQs}`;
32 | } else {
33 | qs += pathParametersQs;
34 | }
35 | }
36 |
37 | const hasQueryString = qs.length > 0;
38 |
39 | if (hasQueryString) {
40 | request.url += `?${qs}`;
41 | }
42 |
43 | request.method = event.httpMethod;
44 | request.rawHeaders = [];
45 | request.headers = {};
46 |
47 | const headers = event.multiValueHeaders || {};
48 |
49 | for (const key of Object.keys(headers)) {
50 | for (const value of headers[key]) {
51 | request.rawHeaders.push(key);
52 | request.rawHeaders.push(value);
53 | }
54 | request.headers[key.toLowerCase()] = headers[key].toString();
55 | }
56 |
57 | request.getHeader = (name) => {
58 | return request.headers[name.toLowerCase()];
59 | };
60 | request.getHeaders = () => {
61 | return request.headers;
62 | };
63 |
64 | request.connection = {};
65 |
66 | // eslint-disable-next-line unicorn/prevent-abbreviations
67 | const res = new Stream();
68 | Object.defineProperty(res, "statusCode", {
69 | get() {
70 | return response.statusCode;
71 | },
72 | set(statusCode) {
73 | response.statusCode = statusCode;
74 | },
75 | });
76 | res.headers = {};
77 | res.writeHead = (status, headers) => {
78 | response.statusCode = status;
79 | if (headers) {
80 | res.headers = Object.assign(res.headers, headers);
81 | }
82 | };
83 | res.write = (chunk) => {
84 | response.body = Buffer.concat([response.body, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
85 | };
86 | res.setHeader = (name, value) => {
87 | res.headers[name] = value;
88 | };
89 | res.removeHeader = (name) => {
90 | res.headers[name] = undefined;
91 | };
92 | res.getHeader = (name) => {
93 | return res.headers[name.toLowerCase()];
94 | };
95 | res.getHeaders = () => {
96 | return res.headers;
97 | };
98 | res.end = (text) => {
99 | if (text) {
100 | res.write(text);
101 | }
102 | response.body = Buffer.from(response.body).toString(base64Support ? "base64" : undefined);
103 | response.multiValueHeaders = res.headers;
104 | res.writeHead(response.statusCode);
105 | fixApiGatewayMultipleHeaders();
106 | callback(undefined, response);
107 | };
108 | if (event.body) {
109 | request.push(event.body, event.isBase64Encoded ? "base64" : undefined);
110 | request.push();
111 | }
112 |
113 | // eslint-disable-next-line unicorn/consistent-function-scoping
114 | function fixApiGatewayMultipleHeaders() {
115 | for (const key of Object.keys(response.multiValueHeaders)) {
116 | if (!Array.isArray(response.multiValueHeaders[key])) {
117 | response.multiValueHeaders[key] = [response.multiValueHeaders[key]];
118 | }
119 | }
120 | }
121 |
122 | // eslint-disable-next-line unicorn/prevent-abbreviations
123 | return { req: request, res };
124 | };
125 |
126 | module.exports = requestResponseMapper;
127 |
--------------------------------------------------------------------------------
/src/configuration.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable unicorn/prevent-abbreviations */
2 | const MissingKeyError = require("./errors/missingKeyError");
3 | const ProviderNotSupported = require("./errors/providerNotSupported");
4 | const IncorrectRoutesError = require("./errors/incorretRoutesError");
5 | const EmptyConfigurationError = require("./errors/emptyConfigurationError");
6 | const InvalidMemorySize = require("./errors/invalidMemorySize");
7 | const InvalidTimeout = require("./errors/invalidTimeout");
8 | const { PROVIDERS, NEXT_CONFIG } = require("./constants");
9 | const path = require("path");
10 | const fs = require("fs");
11 |
12 | /**
13 | * @typedef {import('./errors/errors').ValidationError} ValidationError
14 | * @typedef {import('./index').Route} Route
15 | * @typedef {import('./index').Configuration} GlobalConfiguration
16 | */
17 |
18 | class Configuration {
19 | /**
20 | *
21 | * @param {GlobalConfiguration} config
22 | */
23 | constructor(config) {
24 | Configuration.checkConfiguration(config);
25 | const { gatewayKey, nextAppDir, routes, provider, buildPath } = config;
26 | this.properties = {
27 | ...config,
28 | gatewayKey: gatewayKey || "Terranext",
29 | buildPath: buildPath || ".next",
30 | provider,
31 | nextAppDir: nextAppDir ? path.resolve(process.cwd(), nextAppDir) : "./",
32 | routes,
33 | };
34 | }
35 |
36 | /**
37 | *
38 | *
39 | * @param {GlobalConfiguration=} config
40 | * @returns {Boolean|ValidationError[]}
41 | */
42 | static checkConfiguration(config) {
43 | let errors = [];
44 | if (!config) {
45 | errors.push(new EmptyConfigurationError());
46 | return errors;
47 | }
48 | const { gatewayKey, nextAppDir, routes, provider, memorySize, timeout } = config;
49 |
50 | if (!gatewayKey) {
51 | errors.push(new MissingKeyError("gatewayKey"));
52 | }
53 | if (!nextAppDir) {
54 | errors.push(new MissingKeyError("nextAppDir"));
55 | }
56 |
57 | if (routes) {
58 | if (Array.isArray(routes)) {
59 | const isInvalid = routes.some((r) => Configuration.checkRoutes(r) === false);
60 | if (isInvalid === true) {
61 | errors.push(new IncorrectRoutesError());
62 | }
63 | } else {
64 | if (!Configuration.checkRoutes(routes)) {
65 | errors.push(new IncorrectRoutesError());
66 | }
67 | }
68 | }
69 |
70 | if (!provider) {
71 | errors.push(new MissingKeyError("provider"));
72 | }
73 |
74 | if (!Object.keys(PROVIDERS).includes(provider)) {
75 | errors.push(new ProviderNotSupported(provider));
76 | }
77 |
78 | if (memorySize) {
79 | Number.isNaN(Number(memorySize)) && errors.push(new InvalidMemorySize());
80 | }
81 |
82 | if (timeout) {
83 | Number.isNaN(Number(timeout)) && errors.push(new InvalidTimeout());
84 | }
85 |
86 | if (errors.length > 0) {
87 | return errors;
88 | }
89 |
90 | return true;
91 | }
92 |
93 | get getConfiguration() {
94 | return this.properties;
95 | }
96 |
97 | static checkRoutes(routes) {
98 | let valid = true;
99 |
100 | if (typeof routes.prefix === "undefined" || typeof routes.mappings === "undefined") {
101 | return false;
102 | }
103 |
104 | if (typeof routes.prefix !== "string") {
105 | return false;
106 | }
107 |
108 | valid = routes.mappings.every((mapping) => {
109 | return mapping.route && mapping.page;
110 | });
111 |
112 | return valid;
113 | }
114 |
115 | /**
116 | * @returns {string}
117 | */
118 | getLambdaPath() {
119 | return path.resolve(this.properties.nextAppDir, this.properties.buildPath, "lambdas");
120 | }
121 |
122 | /**
123 | * @returns {string}
124 | */
125 | getServerlessPagesPath() {
126 | return path.resolve(this.properties.nextAppDir, this.properties.buildPath, "serverless", "pages");
127 | }
128 |
129 | /**
130 | * @returns {string}
131 | */
132 | getGatewayKey() {
133 | return this.properties.gatewayKey;
134 | }
135 |
136 | /**
137 | *
138 | * @returns {Route[]}
139 | */
140 | getRoutes() {
141 | if (Array.isArray(this.properties.routes)) {
142 | return this.properties.routes;
143 | }
144 | return [this.properties.routes];
145 | }
146 |
147 | /**
148 | * @returns {string}
149 | */
150 | getBuildPath() {
151 | return path.resolve(process.cwd(), this.properties.buildPath);
152 | }
153 |
154 | /**
155 | *
156 | * @returns {string}
157 | */
158 | getServerlessBuildPath() {
159 | return path.resolve(process.cwd(), this.properties.buildPath, "serverless/pages");
160 | }
161 |
162 | /**
163 | *
164 | * @returns {any}
165 | */
166 | getNextConfig() {
167 | const nextConfigFilePath = path.resolve(this.properties.nextAppDir, NEXT_CONFIG);
168 | if (fs.existsSync(nextConfigFilePath)) {
169 | return require(nextConfigFilePath);
170 | }
171 | throw new Error(`Missing config file inside the Next.js folder: ${nextConfigFilePath}`);
172 | }
173 |
174 | getNextAppDir() {
175 | return this.properties.nextAppDir;
176 | }
177 |
178 | getNodeVersion() {
179 | switch (this.properties.nodeVersion) {
180 | case "8": {
181 | return "nodejs8.10";
182 | }
183 | case "10": {
184 | return "nodejs10.x";
185 | }
186 |
187 | case "12": {
188 | return "nodejs12.x";
189 | }
190 |
191 | default:
192 | return "nodejs8.10";
193 | }
194 | }
195 |
196 | getMemorySize() {
197 | return this.properties.memorySize || "1024";
198 | }
199 |
200 | getTimeout() {
201 | return this.properties.timeout || "180";
202 | }
203 |
204 | hasEnvs() {
205 | return Boolean(this.properties.env);
206 | }
207 |
208 | getEnvs() {
209 | return this.properties.env.reduce((result, env) => {
210 | result[env.key] = env.value;
211 | return result;
212 | }, {});
213 | }
214 | }
215 |
216 | module.exports = Configuration;
217 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | const PROVIDERS = {
4 | AWS: "AWS",
5 | };
6 |
7 | const FILE_NAMES = {
8 | LAMBDAS: "lambdas.terraform.tf.json",
9 | GATEWAY: "gateway.terraform.tf.json",
10 | };
11 |
12 | const NEXT_CONFIG = "next.config.js";
13 | const COMPAT_LAYER_PATH = path.resolve(__dirname);
14 |
15 | module.exports = {
16 | PROVIDERS,
17 | FILE_NAMES,
18 | NEXT_CONFIG,
19 | COMPAT_LAYER_PATH,
20 | };
21 |
--------------------------------------------------------------------------------
/src/errors/emptyConfigurationError.js:
--------------------------------------------------------------------------------
1 | const ValidationError = require("./validationError");
2 |
3 | class EmptyConfigurationError extends ValidationError {
4 | constructor() {
5 | super();
6 | this.type = "EmptyConfigurationError";
7 | this.message = "Empty configuration, cannot proceed.";
8 | }
9 | }
10 |
11 | module.exports = EmptyConfigurationError;
12 |
--------------------------------------------------------------------------------
/src/errors/errors.d.ts:
--------------------------------------------------------------------------------
1 | export declare class ValidationError extends Error {}
2 |
--------------------------------------------------------------------------------
/src/errors/folderNotFoundError.js:
--------------------------------------------------------------------------------
1 | class FolderNotFoundError extends Error {
2 | constructor(folderName, error) {
3 | super();
4 | this.name = "FolderNotFoundError";
5 | this.message = `${folderName} doesn't exist`;
6 | this.stack = error.stack;
7 | }
8 | }
9 |
10 | module.exports = FolderNotFoundError;
11 |
--------------------------------------------------------------------------------
/src/errors/incorretRoutesError.js:
--------------------------------------------------------------------------------
1 | const ValidationError = require("./validationError");
2 |
3 | class IncorrectRoutesError extends ValidationError {
4 | constructor() {
5 | super();
6 | this.type = "IncorrectRoutesError";
7 | this.message = "The object containing the routes is not correct";
8 | }
9 | }
10 |
11 | module.exports = IncorrectRoutesError;
12 |
--------------------------------------------------------------------------------
/src/errors/invalidMemorySize.js:
--------------------------------------------------------------------------------
1 | const ValidationError = require("./validationError");
2 |
3 | class InvalidMemorySize extends ValidationError {
4 | constructor() {
5 | super();
6 | this.type = "InvalidMemorySize";
7 | this.message = "memorySize value is invalid, if it is provided, it must be a string containing a number between 128 and 10240";
8 | }
9 | }
10 |
11 | module.exports = InvalidMemorySize;
12 |
--------------------------------------------------------------------------------
/src/errors/invalidTimeout.js:
--------------------------------------------------------------------------------
1 | const ValidationError = require("./validationError");
2 |
3 | class InvalidTimeout extends ValidationError {
4 | constructor() {
5 | super();
6 | this.type = "InvalidTimeout";
7 | this.message = "timeout value is invalid, if it is provided, it must be a string containing a number smaller than 900";
8 | }
9 | }
10 |
11 | module.exports = InvalidTimeout;
12 |
--------------------------------------------------------------------------------
/src/errors/missingKeyError.js:
--------------------------------------------------------------------------------
1 | const ValidationError = require("./validationError");
2 |
3 | class MissingKeyError extends ValidationError {
4 | constructor(key) {
5 | super();
6 | this.key = key;
7 | this.type = "MissingKeyError";
8 | this.message = `${key} is missing, it must be provided`;
9 | }
10 | }
11 |
12 | module.exports = MissingKeyError;
13 |
--------------------------------------------------------------------------------
/src/errors/providerNotSupported.js:
--------------------------------------------------------------------------------
1 | const { PROVIDERS } = require("../constants");
2 | const ValidationError = require("./validationError");
3 |
4 | class ProviderNotSupported extends ValidationError {
5 | constructor(provider) {
6 | super();
7 | this.provider = provider;
8 | this.type = "ProviderNotSupported";
9 | this.message = `${provider} provider is not supported. Choose between: ${Object.keys(PROVIDERS).join(", ")}`;
10 | }
11 | }
12 |
13 | module.exports = ProviderNotSupported;
14 |
--------------------------------------------------------------------------------
/src/errors/validationError.js:
--------------------------------------------------------------------------------
1 | class ValidationError extends Error {
2 | constructor() {
3 | super();
4 | this.name = "ValidationError";
5 | }
6 | }
7 |
8 | module.exports = ValidationError;
9 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AWS } from "./providers/aws/aws.declarations";
2 | import { Param } from "./providers/aws/declarations";
3 |
4 | export interface Configuration {
5 | gatewayKey?: string;
6 | nextAppDir?: string;
7 | routes?: Route[] | Route;
8 | buildPath?: string;
9 | provider: "AWS";
10 | memorySize?: string;
11 | timeout?: string;
12 | nodeVersion?: "8" | "10" | "12";
13 | env?: EnvParam[];
14 | }
15 |
16 | export interface EnvParam {
17 | key: string;
18 | value: string;
19 | }
20 |
21 | export interface Result {
22 | gateway: G;
23 | lambdas: L;
24 | }
25 |
26 | export interface Route {
27 | prefix?: string;
28 |
29 | mappings: Mapping[];
30 | }
31 |
32 | export interface Mapping {
33 | page: string;
34 |
35 | route: string;
36 |
37 | params?: Param[];
38 | }
39 |
40 | export interface GatewayResources {
41 | resource: {
42 | aws_api_gateway_resource: AWS.Resource;
43 | aws_api_gateway_method: AWS.Method;
44 | aws_api_gateway_integration: AWS.Integration;
45 | };
46 | variable: {
47 | integrationList: {
48 | default: string[];
49 | };
50 | };
51 | }
52 |
53 | declare function terranext(configuration: Configuration, write: boolean): Promise;
54 | declare function terranext(configuration: Configuration): Promise>;
55 | declare function terranext(): Promise>;
56 |
57 | export default terranext;
58 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const AwsConfig = require("./providers/aws/awsConfig");
2 | // @ts-ignore
3 | const AwsResources = require("./providers/aws");
4 | // @ts-ignore
5 | const { cosmiconfig } = require("cosmiconfig");
6 | // @ts-ignore
7 | const build = require("next/dist/build").default;
8 |
9 | /**
10 | * @typedef {import("./index").Configuration} Configuration
11 | * @typedef {import("./index").Result} Result
12 | */
13 |
14 | /**
15 | *
16 | * @param {Configuration} configuration The configuration needed to generate the resources
17 | * @param {boolean} [write=false]
18 | * @returns {Promise}
19 | */
20 | async function terranext(configuration, write = false) {
21 | try {
22 | /**
23 | * @type {Configuration}
24 | */
25 | const fileConfiguration = await retrieveConfiguration();
26 | /**
27 | *
28 | * @type {Configuration}
29 | */
30 | const finalConfiguration = {
31 | ...fileConfiguration,
32 | ...configuration,
33 | };
34 | const config = new AwsConfig(finalConfiguration);
35 | const nextConfig = config.getNextConfig();
36 | // @ts-ignore
37 | nextConfig.target = "serverless";
38 | // @ts-ignore
39 | await build(config.getNextAppDir(), nextConfig);
40 | const aws = new AwsResources(config);
41 |
42 | if (write === true) {
43 | await aws.generateGatewayResources(write);
44 | await aws.generateLambdaResources(write);
45 | } else {
46 | const lambdas = aws.generateLambdaResources();
47 | const gateway = await aws.generateGatewayResources();
48 | return {
49 | gateway,
50 | lambdas,
51 | };
52 | }
53 | } catch (error) {
54 | // eslint-disable-next-line no-console
55 | console.error(error);
56 | process.exit(1);
57 | }
58 | }
59 |
60 | async function retrieveConfiguration() {
61 | const explorer = cosmiconfig("terranext");
62 | try {
63 | const result = await explorer.search();
64 | return result.config;
65 | } catch {
66 | return;
67 | }
68 | }
69 |
70 | module.exports = terranext;
71 |
--------------------------------------------------------------------------------
/src/providers/aws/aws.declarations.ts:
--------------------------------------------------------------------------------
1 | export declare namespace AWS {
2 | interface Method {
3 | [key: string]: AWS.GatewayMethod;
4 | }
5 |
6 | interface Resource {
7 | [key: string]: AWS.GatewayResource;
8 | }
9 |
10 | interface Integration {
11 | [key: string]: AWS.GatewayIntegration;
12 | }
13 |
14 | interface RequestParameter {
15 | [key: string]: { name: string };
16 | }
17 |
18 | interface Lambdas {
19 | aws_lambda_function: AWS.LambdaFunction;
20 | aws_lambda_permission: AWS.LambdaPermission;
21 | }
22 |
23 | interface LambdaData {
24 | [key: string]: AWS.Data;
25 | }
26 |
27 | interface Data {
28 | output_path: string;
29 | type: string;
30 | source_dir: string;
31 | }
32 |
33 | interface GatewayMethod {
34 | rest_api_id: string;
35 | resource_id: string;
36 | http_method: string;
37 | authorization: string;
38 | request_parameters?: AWS.RequestParameter;
39 | }
40 |
41 | interface GatewayIntegration {
42 | rest_api_id: string;
43 | resource_id: string;
44 | http_method: string;
45 | integration_http_method: string;
46 | type: string;
47 | uri: string;
48 | request_parameters?: AWS.RequestParameter;
49 | }
50 |
51 | interface GatewayResource {
52 | rest_api_id: string;
53 | parent_id: string;
54 | path_part: string;
55 | }
56 |
57 | interface LambdaFunction {
58 | [key: string]: AWS.Function;
59 | }
60 |
61 | interface Function {
62 | filename: string;
63 | function_name: string;
64 | source_code_hash: string;
65 | handler: string;
66 | runtime: string;
67 | memory_size: string;
68 | timeout: string;
69 | role: string;
70 | environment?: Environment;
71 | }
72 |
73 | interface Environment {
74 | variables: {
75 | [key: string]: string;
76 | };
77 | }
78 |
79 | interface LambdaPermission {
80 | [key: string]: Permission;
81 | }
82 |
83 | interface Permission {
84 | statement_id: string;
85 | action: string;
86 | function_name: string;
87 | principal: string;
88 | source_arn: string;
89 | }
90 |
91 | interface LambdaResources {
92 | resource?: AWS.Lambdas;
93 | data: {
94 | archive_file: AWS.LambdaData;
95 | };
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/providers/aws/awsConfig.js:
--------------------------------------------------------------------------------
1 | const Configuration = require("../../configuration");
2 |
3 | /**
4 | * @typedef {import('../../index').Configuration} GlobalConfiguration
5 | */
6 | class AwsConfig extends Configuration {
7 | /**
8 | *
9 | * @param {GlobalConfiguration} configuration
10 | */
11 | constructor(configuration) {
12 | super(configuration);
13 | }
14 |
15 | /**
16 | * @returns {string}
17 | */
18 | getLambdaPrefix() {
19 | return `lambdaFor${this.getGatewayKey()}`;
20 | }
21 |
22 | /**
23 | * @returns {string}
24 | */
25 | getGatewayResourceId() {
26 | return `\${aws_api_gateway_rest_api.${this.getGatewayKey()}.id}`;
27 | }
28 |
29 | /**
30 | * @returns {string}
31 | */
32 | getRootResource() {
33 | return `\${aws_api_gateway_rest_api.${this.getGatewayKey()}.root_resource_id}`;
34 | }
35 | }
36 |
37 | module.exports = AwsConfig;
38 |
--------------------------------------------------------------------------------
/src/providers/aws/declarations.d.ts:
--------------------------------------------------------------------------------
1 | import { AWS } from "./aws.declarations";
2 |
3 | export interface Param {
4 | name: string;
5 | mandatory?: boolean;
6 | }
7 |
8 | export interface AwsGatewayOptions {
9 | parentId?: string;
10 | id: string;
11 | isUrlParameter?: boolean;
12 | pathname: string;
13 | params?: Param[];
14 | queryStringParams?: Param[];
15 | lambdaName: string;
16 | }
17 |
18 | export interface GenerateLambdaResource {
19 | resourceUniqueId: string;
20 | resource: AWS.Function;
21 | }
22 |
23 | export interface LambdaPermission {
24 | permissionUniqueId: string;
25 | resource: AWS.Permission;
26 | }
27 |
28 | export interface GenerateGatewayResource {
29 | uniqueId: string;
30 | resource: AWS.GatewayResource;
31 | }
32 |
33 | export interface HandleResource {
34 | pathPart: string;
35 | index: number;
36 | parts: string[];
37 | pathname: string;
38 | lambdaName: string;
39 | params: Param[];
40 | }
41 |
42 | export interface GenerateGatewayResourcePayload {
43 | id: string;
44 | pathname: string;
45 | parentId?: string;
46 | isUrlParam?: boolean;
47 | }
48 |
49 | export interface GenerateGatewayIntegrationPayload {
50 | id: string;
51 | gatewayResourceId: string;
52 | lambdaName: string;
53 | params?: Param[];
54 | queryStringParams?: Param[];
55 | }
56 |
57 | export interface GenerateGatewayMethodPayload {
58 | uniqueName: string;
59 | gatewayResourceId: string;
60 | params?: Param[];
61 | queryStringParams?: Param[];
62 | }
63 |
64 | export interface LambdaOptions {
65 | id: string;
66 | directoryName: string;
67 | }
68 |
--------------------------------------------------------------------------------
/src/providers/aws/index.js:
--------------------------------------------------------------------------------
1 | const Lambda = require("./resources/lambda");
2 | const BaseProvider = require("../baseProvider");
3 | const fs = require("fs");
4 | const path = require("path");
5 | const { generateMappingsFromFiles, getLambdaFiles } = require("../../shared");
6 | const Gateway = require("./resources/gateway");
7 | const { generateUniqueName } = require("../../utils");
8 | const { FILE_NAMES, COMPAT_LAYER_PATH } = require("../../constants");
9 | const FolderNotFoundError = require("../../errors/folderNotFoundError");
10 |
11 | class AwsResources extends BaseProvider {
12 | constructor(config) {
13 | super(config);
14 | this.terraformConfiguration = {};
15 | this.apiGatewayResource = {};
16 | this.apiGatewayMethod = {};
17 | this.apiGatewayIntegration = {};
18 | this.lambdasResources = {};
19 | this.lambdasPermissions = {};
20 | this.lambdaZip = {};
21 | }
22 |
23 | parseParameters(parameters) {
24 | return Object.keys(parameters).map((parameterKey) => {
25 | return {
26 | name: parameterKey,
27 | mandatory: parameters[parameterKey],
28 | };
29 | });
30 | }
31 |
32 | getParametersFromPath(pathname) {
33 | return pathname
34 | .split("/")
35 | .map((pathPart) => {
36 | if (pathPart.includes(":")) {
37 | return {
38 | name: pathPart.replace(":", ""),
39 | mandatory: true,
40 | };
41 | }
42 | return;
43 | })
44 | .filter(Boolean);
45 | }
46 |
47 | /**
48 | *
49 | * @param {import("./declarations").HandleResource} payload
50 | */
51 | handleResource({ pathPart, index, parts, pathname, lambdaName, params }) {
52 | const isUrlParameter = pathPart.includes(":");
53 | const currentPathName = pathPart.replace(":", "");
54 | let urlParameters = [];
55 | let queryStringParameters = [];
56 | if (isUrlParameter) {
57 | urlParameters = this.getParametersFromPath(pathname);
58 | }
59 | if (params) {
60 | queryStringParameters = this.parseParameters(params);
61 | }
62 | const gateway = new Gateway(this.config, {
63 | parentId: index > 0 ? generateUniqueName(parts.slice(0, index)) : undefined,
64 | pathname: currentPathName,
65 | isUrlParameter,
66 | id: generateUniqueName(parts.slice(0, index + 1)),
67 | params: urlParameters,
68 | queryStringParams: queryStringParameters,
69 | lambdaName,
70 | });
71 |
72 | const gatewayResource = gateway.generate();
73 |
74 | this.apiGatewayResource[gatewayResource.resource.uniqueId] = gatewayResource.resource.resource;
75 | this.apiGatewayMethod[gatewayResource.method.uniqueId] = gatewayResource.method.resource;
76 | this.apiGatewayIntegration[gatewayResource.integration.uniqueId] = gatewayResource.integration.resource;
77 | }
78 |
79 | /*
80 | *
81 | * @param {Route} routeObject
82 | */
83 | generateResourcesFromRoute(routeObject) {
84 | routeObject.mappings.forEach((currentRoute) => {
85 | const { params, page, route } = currentRoute;
86 | const prefix = routeObject.prefix ? routeObject.prefix : "";
87 | const pathname = prefix + route;
88 | const lambdaName = page.replace("/", "");
89 | pathname
90 | .split("/")
91 | .filter(Boolean)
92 | .forEach((pathPart, index, parts) => {
93 | this.handleResource({
94 | pathPart,
95 | index,
96 | parts,
97 | pathname,
98 | lambdaName,
99 | params,
100 | });
101 | });
102 | });
103 | }
104 |
105 | generateResources(routesObject) {
106 | if (Array.isArray(routesObject)) {
107 | routesObject.forEach((routeObject) => this.generateResourcesFromRoute(routeObject));
108 | } else {
109 | this.generateResourcesFromRoute(routesObject);
110 | }
111 | }
112 |
113 | deleteDir(pathToDelete) {
114 | if (fs.existsSync(pathToDelete)) {
115 | fs.readdirSync(pathToDelete).forEach((file) => {
116 | const curPath = path.join(pathToDelete, file);
117 | if (fs.lstatSync(curPath).isDirectory()) {
118 | // recurse
119 | this.deleteDir(curPath);
120 | } else {
121 | // delete file
122 | fs.unlinkSync(curPath);
123 | }
124 | });
125 | fs.rmdirSync(pathToDelete);
126 | }
127 | }
128 |
129 | /**
130 | *
131 | * @param {boolean} write
132 | * @returns {Promise<{}|*>}
133 | */
134 | async generateGatewayResources(write = false) {
135 | try {
136 | const routes = this.config.getRoutes();
137 | const lambdaPath = this.config.getServerlessPagesPath();
138 | const files = await getLambdaFiles(lambdaPath);
139 | const nextRoutes = generateMappingsFromFiles(files);
140 | const finalRoutes = routes ? [...routes, nextRoutes] : nextRoutes;
141 | this.generateResources(finalRoutes);
142 |
143 | this.terraformConfiguration = {
144 | resource: {
145 | aws_api_gateway_resource: this.apiGatewayResource,
146 | aws_api_gateway_method: this.apiGatewayMethod,
147 | aws_api_gateway_integration: this.apiGatewayIntegration,
148 | },
149 | variable: {
150 | integrationList: {
151 | default: Object.keys(this.apiGatewayIntegration).map((key) => `aws_api_gateway_integration.${key}`),
152 | },
153 | },
154 | };
155 |
156 | if (write) {
157 | // eslint-disable-next-line no-console
158 | console.log(`Generating file ${FILE_NAMES.GATEWAY}`);
159 | fs.writeFileSync(path.join(process.cwd(), FILE_NAMES.GATEWAY), JSON.stringify(this.terraformConfiguration, undefined, 4), {
160 | encoding: "utf-8",
161 | });
162 | } else {
163 | return this.terraformConfiguration;
164 | }
165 | } catch (error) {
166 | throw new Error(error);
167 | }
168 | }
169 |
170 | generateLambdaResources(write = false) {
171 | const buildPath = this.config.getBuildPath();
172 | const serverlessBuildPath = this.config.getServerlessBuildPath();
173 |
174 | if (fs.existsSync(`${buildPath}/lambdas`)) {
175 | this.deleteDir(`${buildPath}/lambdas`);
176 | }
177 | // it creates the folder that will contain the lambdas
178 | fs.mkdirSync(`${buildPath}/lambdas`);
179 |
180 | return getLambdaFiles(serverlessBuildPath)
181 | .then((files) => {
182 | files.forEach((file) => {
183 | const pathToFile = path.resolve(serverlessBuildPath, file);
184 | if (!fs.lstatSync(pathToFile).isDirectory()) {
185 | /**
186 | * 1. create a folder with name of the file
187 | * 2. copy the next file with a suffix .original.js
188 | * 3. create the lambda from the template
189 | * 4. copy the compact layer
190 | * 5. generate the lambda resource
191 | * 6. generate the zip file resource
192 | */
193 | // 1.
194 | const lambdaName = file.replace(".js", "");
195 | const lambdaPath = `${path.resolve(buildPath, "lambdas")}/${lambdaName}`;
196 | fs.mkdirSync(lambdaPath);
197 |
198 | // 2.
199 | const newFilename = file.replace(".js", ".original.js");
200 | fs.copyFileSync(pathToFile, path.resolve(buildPath, "lambdas", lambdaName, newFilename));
201 |
202 | const lambda = new Lambda(this.config, {
203 | id: lambdaName,
204 | directoryName: lambdaName,
205 | });
206 | // 3.
207 | lambda.emitLambdaFile(lambdaName, buildPath);
208 |
209 | // 4.
210 | fs.copyFileSync(
211 | path.resolve(COMPAT_LAYER_PATH, "./compatLayer.js"),
212 | path.resolve(buildPath, "lambdas", lambdaName, "compatLayer.js"),
213 | );
214 |
215 | // 5.
216 | const lambdaResource = lambda.generate();
217 | this.lambdasResources[lambdaResource.properties.resourceUniqueId] = lambdaResource.properties.resource;
218 | this.lambdasPermissions[lambdaResource.permissions.permissionUniqueId] = lambdaResource.permissions.resource;
219 | this.lambdaZip[lambdaResource.zip.uniqueId] = lambdaResource.zip.resource;
220 | }
221 | });
222 | // it gets files that are inside the serverless folder created by next
223 | fs.readdirSync(serverlessBuildPath);
224 |
225 | let lambdaResources = {
226 | resource: {
227 | aws_lambda_function: this.lambdasResources,
228 | aws_lambda_permission: this.lambdasPermissions,
229 | },
230 | data: {
231 | archive_file: this.lambdaZip,
232 | },
233 | };
234 |
235 | if (write === true) {
236 | // eslint-disable-next-line no-console
237 | console.log(`Generating file ${FILE_NAMES.LAMBDAS}`);
238 | fs.writeFileSync(path.join(process.cwd(), FILE_NAMES.LAMBDAS), JSON.stringify(lambdaResources, undefined, 4), {
239 | encoding: "utf-8",
240 | });
241 | } else {
242 | return lambdaResources;
243 | }
244 | })
245 | .catch((error) => {
246 | throw new FolderNotFoundError(serverlessBuildPath, error);
247 | });
248 | }
249 | }
250 |
251 | module.exports = AwsResources;
252 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/gateway.js:
--------------------------------------------------------------------------------
1 | const GatewayIntegration = require("./gatewayIntegration");
2 | const GatewayMethod = require("./gatewayMethod");
3 | const GatewayResource = require("./gatewayResource");
4 |
5 | /** @typedef {import('../aws.declarations').AWS.GatewayIntegration} GatewayIntegrationObject */
6 | /** @typedef {import('../declarations').Param} Param */
7 | /** @typedef {import('../declarations').AwsGatewayOptions} AwsGatewayOptions */
8 | /**
9 | * @typef {} AwsConfig
10 | */
11 |
12 | class Gateway {
13 | /**
14 | *
15 | * @param config
16 | * @param {AwsGatewayOptions} options
17 | */
18 | constructor(config, options) {
19 | this.gatewayIntegration = new GatewayIntegration(config, options);
20 | this.gatewayMethod = new GatewayMethod(config, options);
21 | this.gatewayResource = new GatewayResource(config, options);
22 | }
23 |
24 | /**
25 | * @returns
26 | */
27 | getResource() {
28 | return this.gatewayResource.generateGatewayResource();
29 | }
30 |
31 | /**
32 | *
33 | * @param {string} gatewayResourceId
34 | * @returns {{resource: {authorization: string, rest_api_id: *, http_method: string, resource_id: string}, uniqueId: string}}
35 | */
36 | getMethod(gatewayResourceId) {
37 | return this.gatewayMethod.generateGatewayMethod(gatewayResourceId);
38 | }
39 |
40 | /**
41 | *
42 | * @param {string} gatewayResourceId
43 | * @returns {{uniqueId: string, resource: GatewayIntegrationObject}}
44 | */
45 | getIntegration(gatewayResourceId) {
46 | return this.gatewayIntegration.generateGatewayIntegration(gatewayResourceId);
47 | }
48 |
49 | /**
50 | * @returns {object}
51 | */
52 | generate() {
53 | const resource = this.getResource();
54 | const method = this.getMethod(resource.uniqueId);
55 | const integration = this.getIntegration(resource.uniqueId);
56 |
57 | return {
58 | resource,
59 | method,
60 | integration,
61 | };
62 | }
63 | }
64 |
65 | module.exports = Gateway;
66 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/gatewayIntegration.js:
--------------------------------------------------------------------------------
1 | /** @typedef {import('../aws.declarations').AWS.GatewayIntegration} GatewayIntegrationObject */
2 | /** @typedef {import('../declarations').Param} Param */
3 | /** @typedef {import('../declarations').AwsGatewayOptions} AwsGatewayOptions */
4 | /**
5 | * @typedef {import('../awsConfig')} AwsConfig
6 | */
7 |
8 | class GatewayIntegration {
9 | /**
10 | *
11 | * @param {AwsConfig} config
12 | * @param {AwsGatewayOptions} options
13 | */
14 | constructor(config, options) {
15 | this.config = config;
16 | this.options = options;
17 | }
18 | /**
19 | * It generates the integration resource
20 | *
21 | * @param {string} gatewayResourceId
22 | * @returns {{ uniqueId: string, resource: GatewayIntegrationObject }}
23 | */
24 | generateGatewayIntegration(gatewayResourceId) {
25 | return {
26 | uniqueId: `${this.config.getGatewayKey()}-${this.options.id}`,
27 | resource: this.generateResource(gatewayResourceId),
28 | };
29 | }
30 |
31 | /**
32 | *
33 | *
34 | * @param {string} gatewayResourceId
35 | * @returns {GatewayIntegrationObject}
36 | */
37 | generateResource(gatewayResourceId) {
38 | const resource = {
39 | rest_api_id: `\${aws_api_gateway_rest_api.${this.config.getGatewayKey()}.id}`,
40 | resource_id: `\${aws_api_gateway_resource.${gatewayResourceId}.id}`,
41 | http_method: "GET",
42 | integration_http_method: "POST",
43 | type: "AWS_PROXY",
44 | uri: `arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.${this.config.getLambdaPrefix()}-${
45 | this.options.lambdaName
46 | }.arn}/invocations`,
47 | };
48 |
49 | if (this.options.params && this.options.params.length > 0) {
50 | resource.request_parameters = this.options.params.reduce((result, parameter) => {
51 | result[`integration.request.path.${parameter.name}`] = `method.request.path.${parameter.name}`;
52 |
53 | return result;
54 | }, resource.request_parameters || {});
55 | }
56 |
57 | if (this.options.queryStringParams && this.options.queryStringParams.length > 0) {
58 | resource.request_parameters = this.options.queryStringParams.reduce((result, parameter) => {
59 | result[`integration.request.querystring.${parameter.name}`] = `method.request.querystring.${parameter.name}`;
60 |
61 | return result;
62 | }, resource.request_parameters || {});
63 | }
64 |
65 | return resource;
66 | }
67 | }
68 |
69 | module.exports = GatewayIntegration;
70 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/gatewayMethod.js:
--------------------------------------------------------------------------------
1 | /** @typedef {import('../declarations').AwsGatewayOptions} AwsGatewayOptions */
2 | /**
3 | * @typedef {import('../awsConfig')} AwsConfig
4 | */
5 | class GatewayMethod {
6 | /**
7 | *
8 | * @param {AwsConfig} config
9 | * @param {AwsGatewayOptions} options
10 | */
11 | constructor(config, options) {
12 | this.config = config;
13 | this.options = options;
14 | }
15 |
16 | /**
17 | * It generates the resource for the single method
18 | *
19 | * @returns
20 | */
21 | generateGatewayMethod(gatewayResourceId) {
22 | return {
23 | uniqueId: `${this.config.getGatewayKey()}-${this.options.id}`,
24 | resource: this.generateResource(gatewayResourceId),
25 | };
26 | }
27 |
28 | /**
29 | *
30 | * @param {string} resourceId
31 | */
32 | generateResource(resourceId) {
33 | const resource = {
34 | rest_api_id: this.config.getGatewayResourceId(),
35 | resource_id: `\${aws_api_gateway_resource.${resourceId}.id}`,
36 | http_method: "GET",
37 | authorization: "NONE",
38 | };
39 | if (this.options.params && this.options.params.length > 0) {
40 | resource.request_parameters = this.options.params.reduce((result, parameter) => {
41 | result[`method.request.path.${parameter.name}`] = parameter.mandatory;
42 |
43 | return result;
44 | }, resource.request_parameters || {});
45 | }
46 | if (this.options.queryStringParams && this.options.queryStringParams.length > 0) {
47 | resource.request_parameters = this.options.queryStringParams.reduce((result, parameter) => {
48 | result[`method.request.querystring.${parameter.name}`] = parameter.mandatory;
49 |
50 | return result;
51 | }, resource.request_parameters || {});
52 | }
53 | return resource;
54 | }
55 | }
56 |
57 | module.exports = GatewayMethod;
58 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/gatewayResource.js:
--------------------------------------------------------------------------------
1 | /** @typedef {import('../declarations').AwsGatewayOptions} AwsGatewayOptions */
2 | /**
3 | * @typedef {import('../awsConfig')} AwsConfig
4 | */
5 | class GatewayResource {
6 | /**
7 | *
8 | * @param {AwsConfig} config
9 | * @param {AwsGatewayOptions} options
10 | */
11 | constructor(config, options) {
12 | this.config = config;
13 | this.options = options;
14 | this.parentResourceName = this.createUniqueId(this.options.parentId);
15 | }
16 |
17 | /**
18 | * It generates the ApiGateway resource
19 | * @returns
20 | */
21 | generateGatewayResource() {
22 | return {
23 | uniqueId: this.generateUniqueId(),
24 | resource: this.generateResource(),
25 | };
26 | }
27 |
28 | /**
29 | *
30 | * @returns string
31 | */
32 | generateUniqueId() {
33 | return `${this.config.getGatewayKey()}-${this.options.id}`;
34 | }
35 |
36 | /**
37 | *
38 | * @returns string
39 | */
40 | createUniqueId(id) {
41 | if (!id) {
42 | return;
43 | }
44 | return `${this.config.getGatewayKey()}-${id}`;
45 | }
46 |
47 | /**
48 | * It generates the single resource
49 | */
50 | generateResource() {
51 | return {
52 | rest_api_id: this.config.getGatewayResourceId(),
53 | parent_id: this.options.parentId ? `\${aws_api_gateway_resource.${this.parentResourceName}.id}` : this.config.getRootResource(),
54 | path_part: this.options.isUrlParameter ? `{${this.options.pathname}}` : this.options.pathname,
55 | };
56 | }
57 | }
58 |
59 | module.exports = GatewayResource;
60 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/lambda.js:
--------------------------------------------------------------------------------
1 | const LambdaPermission = require("./lambdaPermission");
2 | const LambdaZip = require("./lambdaZip");
3 | const LambdaProperties = require("./lambdaProperties");
4 | const fs = require("fs");
5 | const path = require("path");
6 |
7 | /**
8 | * @typedef {import('../awsConfig')} AwsConfig
9 | */
10 |
11 | class Lambda {
12 | /**
13 | *
14 | * @param {AwsConfig} config
15 | * @param options
16 | */
17 | constructor(config, options) {
18 | this.config = config;
19 | this.options = options;
20 | this.properties = new LambdaProperties(this.config, this.options);
21 | this.zip = new LambdaZip(this.config, this.options);
22 | this.permisssions = new LambdaPermission(this.config, this.options);
23 | }
24 |
25 | emitLambdaFile(filename, thePath) {
26 | const lambdaTemplate = `
27 | const compatLayer = require('./compatLayer.js');
28 | const page = require('./${filename}.original.js');
29 |
30 | exports.render = (event, context, callback) => {
31 | const { req, res } = compatLayer(event, callback);
32 | page.render(req, res);
33 | }`;
34 |
35 | fs.writeFileSync(path.resolve(thePath, "lambdas", filename, `${filename}.js`), lambdaTemplate, {
36 | encoding: "utf-8",
37 | });
38 | }
39 |
40 | generate() {
41 | const properties = this.properties.generateLambdaProperties();
42 | const zip = this.zip.generateZipResource();
43 | const permissions = this.permisssions.generateLambdaPermissions();
44 |
45 | return {
46 | properties,
47 | zip,
48 | permissions,
49 | };
50 | }
51 | }
52 | module.exports = Lambda;
53 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/lambdaPermission.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {import('../awsConfig')} AwsConfig
3 | */
4 | /**
5 | * @typedef {import('../declarations').LambdaOptions} LambdaOptions
6 | */
7 |
8 | class LambdaPermission {
9 | /**
10 | *
11 | * @param {AwsConfig} config
12 | * @param {LambdaOptions} options
13 | */
14 | constructor(config, options) {
15 | this.config = config;
16 | this.options = options;
17 | }
18 |
19 | /**
20 | * It generates the Lambda resource
21 | *
22 | * @returns {import('../declarations').LambdaPermission}
23 | */
24 | generateLambdaPermissions() {
25 | const cleanedId = this.options.id.replace(/\[|]/g, "");
26 | const lambdaId = `${this.config.getLambdaPrefix()}-${cleanedId}`;
27 | return {
28 | permissionUniqueId: lambdaId,
29 | resource: {
30 | statement_id: "AllowExecutionFromAPIGateway",
31 | action: "lambda:InvokeFunction",
32 | function_name: `\${aws_lambda_function.${lambdaId}.function_name}`,
33 | principal: "apigateway.amazonaws.com",
34 | source_arn: `\${aws_api_gateway_rest_api.${this.config.getGatewayKey()}.execution_arn}/*/*/*`,
35 | },
36 | };
37 | }
38 | }
39 |
40 | module.exports = LambdaPermission;
41 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/lambdaProperties.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {import('../awsConfig')} AwsConfig
3 | */
4 | /**
5 | * @typedef {import('../declarations').LambdaOptions} LambdaOptions
6 | */
7 | class LambdaProperties {
8 | /**
9 | *
10 | * @param {AwsConfig} config
11 | * @param {LambdaOptions} options
12 | */
13 | constructor(config, options) {
14 | this.config = config;
15 | this.options = options;
16 | }
17 |
18 | /**
19 | * It generates the Lambda resource
20 | *
21 | * @returns {import('../declarations').GenerateLambdaResource}
22 | */
23 | generateLambdaProperties() {
24 | const cleanedId = this.options.id.replace(/\[|]/g, "");
25 | const lambdaId = `${this.config.getLambdaPrefix()}-${cleanedId}`;
26 | const resource = {
27 | resourceUniqueId: lambdaId,
28 | resource: {
29 | filename: `\${data.archive_file.packLambda-${cleanedId}.output_path}`,
30 | function_name: `\${local.groupname}-${cleanedId}`,
31 | source_code_hash: `\${data.archive_file.packLambda-${cleanedId}.output_base64sha256}`,
32 | handler: `${this.options.id}.render`,
33 | runtime: this.config.getNodeVersion(),
34 | memory_size: this.config.getMemorySize(),
35 | timeout: this.config.getTimeout(),
36 | role: "${local.lambda_iam_role}",
37 | },
38 | };
39 | if (this.config.hasEnvs()) {
40 | resource.resource.environment = {
41 | variables: this.config.getEnvs(),
42 | };
43 | }
44 |
45 | return resource;
46 | }
47 | }
48 |
49 | module.exports = LambdaProperties;
50 |
--------------------------------------------------------------------------------
/src/providers/aws/resources/lambdaZip.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | /**
3 | * @typedef {import('../awsConfig')} AwsConfig
4 | */
5 | /** @typedef {import('../aws.declarations').AWS.Data} Data */
6 | /** @typedef {{ uniqueId: string; resource: Data }} Result */
7 | /**
8 | * @typedef {import('../declarations').LambdaOptions} LambdaOptions
9 | */
10 |
11 | class LambdaZip {
12 | /**
13 | *
14 | * @param {AwsConfig} config
15 | * @param {LambdaOptions} options
16 | */
17 | constructor(config, options) {
18 | this.config = config;
19 | this.options = options;
20 | }
21 |
22 | /**
23 | * It generates the Lambda resource
24 | * @returns {Result}
25 | */
26 | generateZipResource() {
27 | const cleanedId = this.options.id.replace(/\[|]/g, "");
28 |
29 | return {
30 | uniqueId: `packLambda-${cleanedId}`,
31 | resource: {
32 | output_path: `files/\${local.groupname}-${this.options.id}.zip`,
33 | type: "zip",
34 | // eslint-disable-next-line unicorn/prevent-abbreviations
35 | source_dir: path.join(this.config.getLambdaPath(), this.options.directoryName),
36 | },
37 | };
38 | }
39 | }
40 |
41 | module.exports = LambdaZip;
42 |
--------------------------------------------------------------------------------
/src/providers/baseProvider.js:
--------------------------------------------------------------------------------
1 | class BaseProvider {
2 | constructor(config) {
3 | this.config = config;
4 | }
5 | }
6 |
7 | module.exports = BaseProvider;
8 |
--------------------------------------------------------------------------------
/src/shared.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const { promisify } = require("util");
4 |
5 | /** @typedef {import('./index').Route} Route */
6 |
7 | /**
8 | *
9 | * @param {string} fileName
10 | * @param {string} [pathPart]
11 | * @returns {string}
12 | */
13 | function generatePathFromFile(fileName, pathPart) {
14 | if (fileName.includes("index") && pathPart) {
15 | const parts = pathPart.split("/");
16 | return `/${parts[parts.length - 1].replace(".js", "").replace(":", "").replace(/\[/gm, "").replace(/]/gm, "")}`;
17 | }
18 | return `/${fileName.replace(".js", "").replace(/\[/gm, "").replace(/]/gm, "")}`;
19 | }
20 |
21 | /**
22 | * It returns the files inside a folder
23 | *
24 | * @param {string} lambdaPath The Path to the lambdas
25 | * @returns {Promise}
26 | */
27 | async function getLambdaFiles(lambdaPath) {
28 | try {
29 | const readDirectory = promisify(fs.readdir);
30 |
31 | const files = await readDirectory(lambdaPath);
32 |
33 | return files.map((file) => {
34 | const pathToFile = path.resolve(lambdaPath, file);
35 | if (fs.existsSync(pathToFile) && !fs.lstatSync(pathToFile).isDirectory()) {
36 | return file;
37 | }
38 | return file;
39 | });
40 | } catch {
41 | return [];
42 | }
43 | }
44 |
45 | /**
46 | * It returns a Route object from a list of files
47 | *
48 | * @param {string[]} files An array of file names
49 | * @returns {Route}
50 | */
51 | function generateMappingsFromFiles(files) {
52 | const mappings = files.reduce((mappings, file) => {
53 | const path = generatePathFromFile(file);
54 | mappings.push({
55 | route: path,
56 | page: path,
57 | });
58 |
59 | return mappings;
60 | }, []);
61 |
62 | return {
63 | prefix: "",
64 | mappings,
65 | };
66 | }
67 |
68 | /**
69 | *
70 | * @param {string} pathToPagesFolder
71 | * @returns {Route}
72 | */
73 | function generateMappingsFromPagesFolder(pathToPagesFolder) {
74 | const mappings = [];
75 |
76 | recursiveBuildMappings(pathToPagesFolder, mappings);
77 |
78 | return {
79 | prefix: "",
80 | mappings,
81 | };
82 | }
83 |
84 | function recursiveBuildMappings(directoryPath, mappings = [], pathPart = "") {
85 | // it starts by reading files inside the given folder
86 | const files = fs.readdirSync(directoryPath);
87 | files.forEach((file) => {
88 | const absoluteFilePath = path.join(directoryPath, file);
89 | const newPathPart = fromNextPathToQueryPath(pathPart, file);
90 | const hasIndex = directoryContainsIndexFile(absoluteFilePath);
91 | if (fs.statSync(absoluteFilePath).isDirectory() && !hasIndex) {
92 | recursiveBuildMappings(absoluteFilePath, mappings, newPathPart);
93 | } else {
94 | const mapping = {
95 | route: newPathPart,
96 | page: generatePathFromFile(file, newPathPart),
97 | };
98 | // if ()
99 | mappings.push(mapping);
100 | }
101 | });
102 | }
103 |
104 | function isUrlPathname(string) {
105 | return /^\[.*[\dA-z]]$/gm.test(string);
106 | }
107 |
108 | function fromNextPathToQueryPath(pathPart, file) {
109 | const cleanedFile = file.replace(".js", "");
110 | if (isUrlPathname(cleanedFile)) {
111 | return `${pathPart}/${`:${cleanedFile.replace(/\[/gm, "").replace(/]/gm, "")}`}`;
112 | } else {
113 | return `${pathPart}/${cleanedFile}`;
114 | }
115 | }
116 |
117 | /**
118 | *
119 | * @param {string} absoluteFilePath
120 | */
121 | function directoryContainsIndexFile(absoluteFilePath) {
122 | if (fs.statSync(absoluteFilePath).isDirectory()) {
123 | return fs.existsSync(path.resolve(absoluteFilePath, "index.js"));
124 | }
125 |
126 | return false;
127 | }
128 |
129 | module.exports = {
130 | getLambdaFiles,
131 | generateMappingsFromFiles,
132 | generateMappingsFromPagesFolder,
133 | };
134 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * From an array of parts of an URL, it generates a string
3 | * with only dash.
4 | *
5 | * It can be used inside a terraform configuration
6 | *
7 | * @param {string[]} pathParts
8 | * @returns {string}
9 | */
10 | const generateUniqueName = (pathParts) => {
11 | return pathParts
12 | .filter(Boolean)
13 | .map((p) => p.replace(":", "").replace("?", ""))
14 | .join("-");
15 | };
16 |
17 | module.exports = {
18 | generateUniqueName,
19 | };
20 |
--------------------------------------------------------------------------------
/tests/configuration.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable unicorn/prevent-abbreviations */
2 | // @ts-nocheck
3 |
4 | const Configuration = require("../src/configuration");
5 | const AwsConfig = require("../src/providers/aws/awsConfig");
6 | const { PROVIDERS } = require("../src/constants");
7 |
8 | describe("Configuration", () => {
9 | it("should throw an error when configuration is empty", () => {
10 | const errors = Configuration.checkConfiguration();
11 |
12 | expect(errors).toHaveLength(1);
13 | const er = errors.find((error) => error.type === "EmptyConfigurationError");
14 | expect(er.message).toBe("Empty configuration, cannot proceed.");
15 | });
16 |
17 | it("should throw an error when gatewayKey is not provided", () => {
18 | const errors = Configuration.checkConfiguration({});
19 | expect(errors.find((error) => error.message === "gatewayKey is missing, it must be provided")).toBeDefined();
20 | });
21 |
22 | it("should throw an error when lambdaPath is not provided", () => {
23 | const errors = Configuration.checkConfiguration({});
24 | expect(errors.find((error) => error.message === "nextAppDir is missing, it must be provided")).toBeDefined();
25 | });
26 |
27 | it("should throw an error when routes is are malformed", () => {
28 | const errors = Configuration.checkConfiguration({
29 | gatewayKey: "myTest",
30 | lambdaPath: "/path",
31 | routes: {
32 | prefix: "",
33 | mappings: [
34 | {
35 | page: "/",
36 | },
37 | ],
38 | },
39 | });
40 |
41 | expect(errors.find((error) => error.message === "The object containing the routes is not correct")).toBeDefined();
42 |
43 | const errors2 = Configuration.checkConfiguration({
44 | gatewayKey: "myTest",
45 | lambdaPath: "/path",
46 | routes: [
47 | {
48 | prefix: "home",
49 | mappings: [
50 | {
51 | page: "/content",
52 | route: "ehy",
53 | },
54 | ],
55 | },
56 | {
57 | prefix: "blog",
58 | mappings: [
59 | {
60 | route: "ehy",
61 | },
62 | ],
63 | },
64 | ],
65 | });
66 | expect(errors2.find((error) => error.message === "The object containing the routes is not correct")).toBeDefined();
67 | });
68 |
69 | it("should throw an error when memorySize value is invalid", () => {
70 | const errors = Configuration.checkConfiguration({
71 | gatewayKey: "myTest",
72 | lambdaPath: "/path",
73 | memorySize: "abcd",
74 | });
75 | expect(
76 | errors.find(
77 | (error) =>
78 | error.message === "memorySize value is invalid, if it is provided, it must be a string containing a number between 128 and 10240",
79 | ),
80 | ).toBeDefined();
81 | });
82 |
83 | it("should throw an error when timeout value is invalid", () => {
84 | const errors = Configuration.checkConfiguration({
85 | gatewayKey: "myTest",
86 | lambdaPath: "/path",
87 | timeout: "abcd",
88 | });
89 | expect(
90 | errors.find(
91 | (error) =>
92 | error.message === "timeout value is invalid, if it is provided, it must be a string containing a number smaller than 900",
93 | ),
94 | ).toBeDefined();
95 | });
96 |
97 | it("should return true when the configuration is correct", () => {
98 | expect(
99 | Configuration.checkConfiguration({
100 | gatewayKey: "myTest",
101 | nextAppDir: "/path",
102 | routes: {
103 | prefix: "",
104 | mappings: [
105 | {
106 | page: "/content",
107 | route: "ehy",
108 | },
109 | ],
110 | },
111 | provider: "AWS",
112 | }),
113 | ).toBe(true);
114 |
115 | expect(
116 | Configuration.checkConfiguration({
117 | gatewayKey: "myTest",
118 | nextAppDir: "/path",
119 | routes: [
120 | {
121 | prefix: "home",
122 | mappings: [
123 | {
124 | page: "/content",
125 | route: "ehy",
126 | },
127 | ],
128 | },
129 | {
130 | prefix: "blog",
131 | mappings: [
132 | {
133 | page: "/content",
134 | route: "ehy",
135 | },
136 | ],
137 | },
138 | ],
139 |
140 | provider: "AWS",
141 | }),
142 | ).toBe(true);
143 | });
144 |
145 | it("should return the gateway key", () => {
146 | const c = new AwsConfig({ gatewayKey: "myTest", nextAppDir: "/path" });
147 | expect(c.getGatewayResourceId()).toEqual("${aws_api_gateway_rest_api.myTest.id}");
148 | });
149 |
150 | it("should return the gateway key", () => {
151 | const c = new AwsConfig({ gatewayKey: "myTest", nextAppDir: "/path" });
152 | expect(c.getGatewayKey()).toEqual("myTest");
153 | });
154 |
155 | it("should throw an error when provider is not passed", () => {
156 | const errors = Configuration.checkConfiguration({
157 | gatewayKey: "myTest",
158 | nextAppDir: "/path",
159 | routes: {
160 | prefix: "",
161 | mappings: [
162 | {
163 | page: "/content",
164 | route: "ehy",
165 | },
166 | ],
167 | },
168 | });
169 | expect(errors.find((error) => error.message === "provider is missing, it must be provided")).toBeDefined();
170 | });
171 |
172 | it("should throw an error when provider is not supported", () => {
173 | const errors = Configuration.checkConfiguration({
174 | gatewayKey: "myTest",
175 | nextAppDir: "/path",
176 | routes: {
177 | prefix: "",
178 | mappings: [
179 | {
180 | page: "/content",
181 | route: "ehy",
182 | },
183 | ],
184 | },
185 | // @ts-ignore
186 | provider: "Azure",
187 | });
188 |
189 | expect(
190 | errors.find((error) => error.message === `Azure provider is not supported. Choose between: ${Object.keys(PROVIDERS).join(", ")}`),
191 | ).toBeDefined();
192 | });
193 | });
194 |
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/.next/serverless/pages/boar.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/.next/serverless/pages/boar.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/.next/serverless/pages/contact-us/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/.next/serverless/pages/contact-us/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/.next/serverless/pages/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/.next/serverless/pages/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/boar.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/boar.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/contact-us/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/contact-us/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/.next/serverless/pages/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/pages/[foo]/[deep]/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/pages/[foo]/[deep]/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/pages/[foo]/[query].js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/pages/[foo]/[query].js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/pages/[foo]/bar.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/pages/[foo]/bar.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/next9/pages/[foo]/fixed/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/next9/pages/[foo]/fixed/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/pages/boar.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/pages/boar.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/pages/contact-us/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/pages/contact-us/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__fixtures__/pages/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ematipico/terraform-nextjs-plugin/a391cf3af56b8ff57025e60bfc822c88e1c3e9f6/tests/providers/aws/__fixtures__/pages/index.js
--------------------------------------------------------------------------------
/tests/providers/aws/__snapshots__/awsResources.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`AwsResources should generate the correct configuration 1`] = `
4 | Object {
5 | "resource": Object {
6 | "aws_api_gateway_integration": Object {
7 | "CustomKey-blog": Object {
8 | "http_method": "GET",
9 | "integration_http_method": "POST",
10 | "request_parameters": Object {
11 | "integration.request.querystring.0": "method.request.querystring.0",
12 | "integration.request.querystring.1": "method.request.querystring.1",
13 | },
14 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog.id}",
15 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
16 | "type": "AWS_PROXY",
17 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-blogPost.arn}/invocations",
18 | },
19 | "CustomKey-blog-detail": Object {
20 | "http_method": "GET",
21 | "integration_http_method": "POST",
22 | "request_parameters": Object {
23 | "integration.request.querystring.0": "method.request.querystring.0",
24 | "integration.request.querystring.1": "method.request.querystring.1",
25 | },
26 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-detail.id}",
27 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
28 | "type": "AWS_PROXY",
29 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-blogPost.arn}/invocations",
30 | },
31 | "CustomKey-blog-detail-url": Object {
32 | "http_method": "GET",
33 | "integration_http_method": "POST",
34 | "request_parameters": Object {
35 | "integration.request.path.url": "method.request.path.url",
36 | "integration.request.querystring.0": "method.request.querystring.0",
37 | "integration.request.querystring.1": "method.request.querystring.1",
38 | },
39 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-detail-url.id}",
40 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
41 | "type": "AWS_PROXY",
42 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-blogPost.arn}/invocations",
43 | },
44 | "CustomKey-blog-url": Object {
45 | "http_method": "GET",
46 | "integration_http_method": "POST",
47 | "request_parameters": Object {
48 | "integration.request.path.url": "method.request.path.url",
49 | },
50 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-url.id}",
51 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
52 | "type": "AWS_PROXY",
53 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-singleBlogPost.arn}/invocations",
54 | },
55 | "CustomKey-boar": Object {
56 | "http_method": "GET",
57 | "integration_http_method": "POST",
58 | "resource_id": "\${aws_api_gateway_resource.CustomKey-boar.id}",
59 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
60 | "type": "AWS_PROXY",
61 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-boar.arn}/invocations",
62 | },
63 | "CustomKey-contact-us": Object {
64 | "http_method": "GET",
65 | "integration_http_method": "POST",
66 | "resource_id": "\${aws_api_gateway_resource.CustomKey-contact-us.id}",
67 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
68 | "type": "AWS_PROXY",
69 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-contact-us.arn}/invocations",
70 | },
71 | "CustomKey-index": Object {
72 | "http_method": "GET",
73 | "integration_http_method": "POST",
74 | "resource_id": "\${aws_api_gateway_resource.CustomKey-index.id}",
75 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
76 | "type": "AWS_PROXY",
77 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-index.arn}/invocations",
78 | },
79 | "CustomKey-new-blog-post-amazing": Object {
80 | "http_method": "GET",
81 | "integration_http_method": "POST",
82 | "resource_id": "\${aws_api_gateway_resource.CustomKey-new-blog-post-amazing.id}",
83 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
84 | "type": "AWS_PROXY",
85 | "uri": "arn:aws:apigateway:\${local.aws_region}:lambda:path/2015-03-31/functions/\${aws_lambda_function.lambdaForCustomKey-blog-post.arn}/invocations",
86 | },
87 | },
88 | "aws_api_gateway_method": Object {
89 | "CustomKey-blog": Object {
90 | "authorization": "NONE",
91 | "http_method": "GET",
92 | "request_parameters": Object {
93 | "method.request.querystring.0": Object {
94 | "mandatory": true,
95 | "name": "page",
96 | },
97 | "method.request.querystring.1": Object {
98 | "mandatory": false,
99 | "name": "hideComments",
100 | },
101 | },
102 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog.id}",
103 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
104 | },
105 | "CustomKey-blog-detail": Object {
106 | "authorization": "NONE",
107 | "http_method": "GET",
108 | "request_parameters": Object {
109 | "method.request.querystring.0": Object {
110 | "mandatory": true,
111 | "name": "page",
112 | },
113 | "method.request.querystring.1": Object {
114 | "mandatory": false,
115 | "name": "hideComments",
116 | },
117 | },
118 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-detail.id}",
119 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
120 | },
121 | "CustomKey-blog-detail-url": Object {
122 | "authorization": "NONE",
123 | "http_method": "GET",
124 | "request_parameters": Object {
125 | "method.request.path.url": true,
126 | "method.request.querystring.0": Object {
127 | "mandatory": true,
128 | "name": "page",
129 | },
130 | "method.request.querystring.1": Object {
131 | "mandatory": false,
132 | "name": "hideComments",
133 | },
134 | },
135 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-detail-url.id}",
136 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
137 | },
138 | "CustomKey-blog-url": Object {
139 | "authorization": "NONE",
140 | "http_method": "GET",
141 | "request_parameters": Object {
142 | "method.request.path.url": true,
143 | },
144 | "resource_id": "\${aws_api_gateway_resource.CustomKey-blog-url.id}",
145 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
146 | },
147 | "CustomKey-boar": Object {
148 | "authorization": "NONE",
149 | "http_method": "GET",
150 | "resource_id": "\${aws_api_gateway_resource.CustomKey-boar.id}",
151 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
152 | },
153 | "CustomKey-contact-us": Object {
154 | "authorization": "NONE",
155 | "http_method": "GET",
156 | "resource_id": "\${aws_api_gateway_resource.CustomKey-contact-us.id}",
157 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
158 | },
159 | "CustomKey-index": Object {
160 | "authorization": "NONE",
161 | "http_method": "GET",
162 | "resource_id": "\${aws_api_gateway_resource.CustomKey-index.id}",
163 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
164 | },
165 | "CustomKey-new-blog-post-amazing": Object {
166 | "authorization": "NONE",
167 | "http_method": "GET",
168 | "resource_id": "\${aws_api_gateway_resource.CustomKey-new-blog-post-amazing.id}",
169 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
170 | },
171 | },
172 | "aws_api_gateway_resource": Object {
173 | "CustomKey-blog": Object {
174 | "parent_id": "\${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
175 | "path_part": "blog",
176 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
177 | },
178 | "CustomKey-blog-detail": Object {
179 | "parent_id": "\${aws_api_gateway_resource.CustomKey-blog.id}",
180 | "path_part": "detail",
181 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
182 | },
183 | "CustomKey-blog-detail-url": Object {
184 | "parent_id": "\${aws_api_gateway_resource.CustomKey-blog-detail.id}",
185 | "path_part": "{url}",
186 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
187 | },
188 | "CustomKey-blog-url": Object {
189 | "parent_id": "\${aws_api_gateway_resource.CustomKey-blog.id}",
190 | "path_part": "{url}",
191 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
192 | },
193 | "CustomKey-boar": Object {
194 | "parent_id": "\${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
195 | "path_part": "boar",
196 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
197 | },
198 | "CustomKey-contact-us": Object {
199 | "parent_id": "\${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
200 | "path_part": "contact-us",
201 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
202 | },
203 | "CustomKey-index": Object {
204 | "parent_id": "\${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
205 | "path_part": "index",
206 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
207 | },
208 | "CustomKey-new-blog-post-amazing": Object {
209 | "parent_id": "\${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
210 | "path_part": "new-blog-post-amazing",
211 | "rest_api_id": "\${aws_api_gateway_rest_api.CustomKey.id}",
212 | },
213 | },
214 | },
215 | "variable": Object {
216 | "integrationList": Object {
217 | "default": Array [
218 | "aws_api_gateway_integration.CustomKey-new-blog-post-amazing",
219 | "aws_api_gateway_integration.CustomKey-blog",
220 | "aws_api_gateway_integration.CustomKey-blog-url",
221 | "aws_api_gateway_integration.CustomKey-blog-detail",
222 | "aws_api_gateway_integration.CustomKey-blog-detail-url",
223 | "aws_api_gateway_integration.CustomKey-boar",
224 | "aws_api_gateway_integration.CustomKey-contact-us",
225 | "aws_api_gateway_integration.CustomKey-index",
226 | ],
227 | },
228 | },
229 | }
230 | `;
231 |
--------------------------------------------------------------------------------
/tests/providers/aws/awsResources.test.js:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | const AwsResources = require("../../../src/providers/aws");
3 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
4 | const path = require("path");
5 |
6 | describe("AwsResources", () => {
7 | it("should generate the correct configuration", async () => {
8 | const config = new AwsConfig({
9 | gatewayKey: "CustomKey",
10 | // eslint-disable-next-line unicorn/prevent-abbreviations
11 | nextAppDir: path.resolve(__dirname, "__fixtures__"),
12 | provider: "AWS",
13 | routes: {
14 | prefix: "",
15 | mappings: [
16 | {
17 | page: "/blog-post",
18 | route: "/new-blog-post-amazing",
19 | },
20 | {
21 | page: "/singleBlogPost",
22 | route: "/blog/:url",
23 | },
24 | {
25 | page: "/blogPost",
26 | route: "/blog/detail/:url",
27 | params: [
28 | {
29 | name: "page",
30 | mandatory: true,
31 | },
32 | {
33 | name: "hideComments",
34 | mandatory: false,
35 | },
36 | ],
37 | },
38 | ],
39 | },
40 | });
41 |
42 | const awsResources = new AwsResources(config);
43 | const result = await awsResources.generateGatewayResources();
44 |
45 | expect(result).toMatchSnapshot();
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/tests/providers/aws/gatewayIntegration.test.js:
--------------------------------------------------------------------------------
1 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
2 | const GatewayIntegration = require("../../../src/providers/aws/resources/gatewayIntegration");
3 |
4 | describe("Gateway integration", () => {
5 | it("should return the expected resource", () => {
6 | const config = new AwsConfig({
7 | gatewayKey: "CustomKey",
8 | provider: "AWS",
9 | });
10 |
11 | const resource = new GatewayIntegration(config, {
12 | id: "index",
13 | lambdaName: "index",
14 | pathname: "/",
15 | });
16 |
17 | const result = resource.generateGatewayIntegration("CustomKey-index");
18 |
19 | expect(result.uniqueId).toBe("CustomKey-index");
20 | expect(result.resource).toStrictEqual({
21 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
22 | resource_id: "${aws_api_gateway_resource.CustomKey-index.id}",
23 | http_method: "GET",
24 | integration_http_method: "POST",
25 | type: "AWS_PROXY",
26 | uri: "arn:aws:apigateway:${local.aws_region}:lambda:path/2015-03-31/functions/${aws_lambda_function.lambdaForCustomKey-index.arn}/invocations",
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/tests/providers/aws/gatewayMethod.test.js:
--------------------------------------------------------------------------------
1 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
2 | const GatewayMethod = require("../../../src/providers/aws/resources/gatewayMethod");
3 |
4 | describe("terraFormGatewayMethod", () => {
5 | it("should create a correct resource", () => {
6 | const config = new AwsConfig({
7 | gatewayKey: "CustomKey",
8 | provider: "AWS",
9 | });
10 |
11 | const method = new GatewayMethod(config, {
12 | id: "index",
13 | pathname: "/",
14 | lambdaName: "index",
15 | });
16 |
17 | const result = method.generateGatewayMethod("index");
18 |
19 | expect(result.uniqueId).toBe("CustomKey-index");
20 | expect(result.resource).toStrictEqual({
21 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
22 | resource_id: "${aws_api_gateway_resource.index.id}",
23 | http_method: "GET",
24 | authorization: "NONE",
25 | });
26 | });
27 |
28 | it("should create a correct resource when there are normal params", () => {
29 | const config = new AwsConfig({
30 | gatewayKey: "CustomKey",
31 | provider: "AWS",
32 | });
33 |
34 | const method = new GatewayMethod(config, {
35 | id: "index",
36 | pathname: "/",
37 | lambdaName: "index",
38 | params: [
39 | {
40 | name: "accountId",
41 | mandatory: true,
42 | },
43 | {
44 | name: "socialId",
45 | mandatory: true,
46 | },
47 | ],
48 | });
49 |
50 | const result = method.generateGatewayMethod("index");
51 |
52 | expect(result.uniqueId).toBe("CustomKey-index");
53 | expect(result.resource).toStrictEqual({
54 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
55 | resource_id: "${aws_api_gateway_resource.index.id}",
56 | http_method: "GET",
57 | authorization: "NONE",
58 | request_parameters: {
59 | "method.request.path.accountId": true,
60 | "method.request.path.socialId": true,
61 | },
62 | });
63 | });
64 |
65 | it("should create a correct resource when there are normal query string params", () => {
66 | const config = new AwsConfig({
67 | gatewayKey: "CustomKey",
68 | provider: "AWS",
69 | });
70 |
71 | const method = new GatewayMethod(config, {
72 | id: "index",
73 | pathname: "/",
74 | lambdaName: "index",
75 | queryStringParams: [
76 | {
77 | name: "accountId",
78 | mandatory: false,
79 | },
80 | {
81 | name: "socialId",
82 | mandatory: false,
83 | },
84 | ],
85 | });
86 |
87 | const result = method.generateGatewayMethod("index");
88 |
89 | expect(result.uniqueId).toBe("CustomKey-index");
90 | expect(result.resource).toStrictEqual({
91 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
92 | resource_id: "${aws_api_gateway_resource.index.id}",
93 | http_method: "GET",
94 | authorization: "NONE",
95 | request_parameters: {
96 | "method.request.querystring.accountId": false,
97 | "method.request.querystring.socialId": false,
98 | },
99 | });
100 | });
101 |
102 | it("should create a correct resource when there are both type of params", () => {
103 | const config = new AwsConfig({
104 | gatewayKey: "CustomKey",
105 | provider: "AWS",
106 | });
107 |
108 | const method = new GatewayMethod(config, {
109 | id: "index",
110 | pathname: "/",
111 | lambdaName: "index",
112 | queryStringParams: [
113 | {
114 | name: "accountId",
115 | mandatory: false,
116 | },
117 | {
118 | name: "socialId",
119 | mandatory: false,
120 | },
121 | ],
122 | params: [
123 | {
124 | name: "url",
125 | mandatory: true,
126 | },
127 | ],
128 | });
129 |
130 | const result = method.generateGatewayMethod("index");
131 |
132 | expect(result.uniqueId).toBe("CustomKey-index");
133 | expect(result.resource).toStrictEqual({
134 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
135 | resource_id: "${aws_api_gateway_resource.index.id}",
136 | http_method: "GET",
137 | authorization: "NONE",
138 | request_parameters: {
139 | "method.request.querystring.accountId": false,
140 | "method.request.querystring.socialId": false,
141 | "method.request.path.url": true,
142 | },
143 | });
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/tests/providers/aws/gatewayResource.test.js:
--------------------------------------------------------------------------------
1 | const GatewayResource = require("../../../src/providers/aws/resources/gatewayResource");
2 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
3 |
4 | describe("Gateway integration", () => {
5 | it("should return the expected resource for a top level", () => {
6 | const c = new AwsConfig({
7 | gatewayKey: "CustomKey",
8 | provider: "AWS",
9 | });
10 |
11 | const method = new GatewayResource(c, {
12 | id: "myId",
13 | pathname: "personal-page",
14 | lambdaName: "myId",
15 | });
16 |
17 | const result = method.generateGatewayResource();
18 |
19 | expect(result.uniqueId).toBe("CustomKey-myId");
20 | expect(result.resource).toStrictEqual({
21 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
22 | parent_id: "${aws_api_gateway_rest_api.CustomKey.root_resource_id}",
23 | path_part: "personal-page",
24 | });
25 | });
26 |
27 | it("should return the expected resource when it has a parent", () => {
28 | const c = new AwsConfig({
29 | gatewayKey: "CustomKey",
30 | provider: "AWS",
31 | });
32 |
33 | const method = new GatewayResource(c, {
34 | id: "mySecondId",
35 | pathname: "personal-page",
36 | parentId: "myId",
37 | lambdaName: "mySecondId",
38 | });
39 |
40 | const result = method.generateGatewayResource();
41 |
42 | expect(result.uniqueId).toBe("CustomKey-mySecondId");
43 | expect(result.resource).toStrictEqual({
44 | rest_api_id: "${aws_api_gateway_rest_api.CustomKey.id}",
45 | parent_id: "${aws_api_gateway_resource.CustomKey-myId.id}",
46 | path_part: "personal-page",
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/tests/providers/aws/lambdaPermission.test.js:
--------------------------------------------------------------------------------
1 | const LambdaPermission = require("../../../src/providers/aws/resources/lambdaPermission");
2 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
3 |
4 | describe("terraFormLambda", () => {
5 | it("should create the correct resource", () => {
6 | const c = new AwsConfig({
7 | gatewayKey: "CustomKey",
8 | provider: "AWS",
9 | });
10 |
11 | const properties = new LambdaPermission(c, {
12 | id: "index",
13 | directoryName: "/test",
14 | });
15 |
16 | const result = properties.generateLambdaPermissions();
17 |
18 | expect(result.permissionUniqueId).toBe("lambdaForCustomKey-index");
19 | expect(result.resource).toStrictEqual({
20 | statement_id: "AllowExecutionFromAPIGateway",
21 | action: "lambda:InvokeFunction",
22 | function_name: "${aws_lambda_function.lambdaForCustomKey-index.function_name}",
23 | principal: "apigateway.amazonaws.com",
24 | source_arn: "${aws_api_gateway_rest_api.CustomKey.execution_arn}/*/*/*",
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/tests/providers/aws/lambdaProperties.test.js:
--------------------------------------------------------------------------------
1 | const LambdaProperties = require("../../../src/providers/aws/resources/lambdaProperties");
2 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
3 |
4 | describe("terraFormLambda", () => {
5 | it("should create the correct resource", () => {
6 | const c = new AwsConfig({
7 | gatewayKey: "CustomKey",
8 | provider: "AWS",
9 | });
10 |
11 | const properties = new LambdaProperties(c, {
12 | id: "index",
13 | directoryName: "/test",
14 | });
15 |
16 | const result = properties.generateLambdaProperties();
17 |
18 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
19 | expect(result.resource).toStrictEqual({
20 | filename: "${data.archive_file.packLambda-index.output_path}",
21 | function_name: "${local.groupname}-index",
22 | source_code_hash: "${data.archive_file.packLambda-index.output_base64sha256}",
23 | handler: "index.render",
24 | runtime: "nodejs8.10",
25 | memory_size: "1024",
26 | timeout: "180",
27 | role: "${local.lambda_iam_role}",
28 | });
29 | });
30 |
31 | it("return the correct node version 10", () => {
32 | const c = new AwsConfig({
33 | gatewayKey: "CustomKey",
34 | nodeVersion: "10",
35 | provider: "AWS",
36 | });
37 |
38 | const properties = new LambdaProperties(c, {
39 | id: "index",
40 | directoryName: "/test",
41 | });
42 |
43 | const result = properties.generateLambdaProperties();
44 |
45 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
46 | expect(result.resource.runtime).toEqual("nodejs10.x");
47 | });
48 |
49 | it("return the correct node version 12", () => {
50 | const c = new AwsConfig({
51 | gatewayKey: "CustomKey",
52 | nodeVersion: "12",
53 | provider: "AWS",
54 | });
55 |
56 | const properties = new LambdaProperties(c, {
57 | id: "index",
58 | directoryName: "/test",
59 | });
60 |
61 | const result = properties.generateLambdaProperties();
62 |
63 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
64 | expect(result.resource.runtime).toEqual("nodejs12.x");
65 | });
66 |
67 | it("return the provided memorySize value", () => {
68 | const c = new AwsConfig({
69 | gatewayKey: "CustomKey",
70 | provider: "AWS",
71 | memorySize: "2048",
72 | });
73 |
74 | const properties = new LambdaProperties(c, {
75 | id: "index",
76 | directoryName: "/test",
77 | });
78 |
79 | const result = properties.generateLambdaProperties();
80 |
81 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
82 | expect(result.resource.memory_size).toEqual("2048");
83 | });
84 |
85 | it("return the provided timeout value", () => {
86 | const c = new AwsConfig({
87 | gatewayKey: "CustomKey",
88 | provider: "AWS",
89 | timeout: "120",
90 | });
91 |
92 | const properties = new LambdaProperties(c, {
93 | id: "index",
94 | directoryName: "/test",
95 | });
96 |
97 | const result = properties.generateLambdaProperties();
98 |
99 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
100 | expect(result.resource.timeout).toEqual("120");
101 | });
102 |
103 | it("should return the environment variables", () => {
104 | const c = new AwsConfig({
105 | gatewayKey: "CustomKey",
106 | nodeVersion: "12",
107 | provider: "AWS",
108 | env: [
109 | {
110 | key: "PROD_KEY",
111 | value: "230402",
112 | },
113 | ],
114 | });
115 |
116 | const properties = new LambdaProperties(c, {
117 | id: "index",
118 | directoryName: "/test",
119 | });
120 |
121 | const result = properties.generateLambdaProperties();
122 |
123 | expect(result.resourceUniqueId).toBe("lambdaForCustomKey-index");
124 | expect(result.resource.environment).toBeTruthy();
125 | expect(result.resource.environment).toStrictEqual({
126 | variables: {
127 | PROD_KEY: "230402",
128 | },
129 | });
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/tests/providers/aws/lambdaZip.test.js:
--------------------------------------------------------------------------------
1 | const LambdaZip = require("../../../src/providers/aws/resources/lambdaZip");
2 | const AwsConfig = require("../../../src/providers/aws/awsConfig");
3 |
4 | describe("terraFormZip", () => {
5 | it("should create the correct resource", () => {
6 | const config = new AwsConfig({
7 | gatewayKey: "Test",
8 | provider: "AWS",
9 | });
10 |
11 | const zip = new LambdaZip(config, {
12 | id: "index",
13 | directoryName: "index",
14 | });
15 |
16 | const result = zip.generateZipResource();
17 | expect(result.uniqueId).toBe("packLambda-index");
18 | expect(result.resource.output_path).toEqual("files/${local.groupname}-index.zip");
19 | expect(result.resource.type).toEqual("zip");
20 | expect(result.resource.source_dir).toContain("lambdas");
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tests/providers/aws/shared.test.js:
--------------------------------------------------------------------------------
1 | const { generateMappingsFromFiles, getLambdaFiles, generateMappingsFromPagesFolder } = require("../../../src/shared");
2 | const path = require("path");
3 |
4 | describe("shared.js", () => {
5 | describe("generateMappingsFromFiles", () => {
6 | it("should generate a routes object from files", () => {
7 | const routes = generateMappingsFromFiles(["file.js", "file2.js", "boar.js"]);
8 |
9 | expect(routes).toStrictEqual({
10 | prefix: "",
11 | mappings: [
12 | {
13 | page: "/file",
14 | route: "/file",
15 | },
16 | {
17 | page: "/file2",
18 | route: "/file2",
19 | },
20 | {
21 | page: "/boar",
22 | route: "/boar",
23 | },
24 | ],
25 | });
26 | });
27 | });
28 |
29 | it("should generate routes object from a real folder", async () => {
30 | const lambdaPath = path.resolve(__dirname, "__fixtures__", "pages");
31 | const files = await getLambdaFiles(lambdaPath);
32 | const routes = generateMappingsFromFiles(files);
33 | expect(routes).toEqual({
34 | prefix: "",
35 | mappings: [
36 | {
37 | page: "/boar",
38 | route: "/boar",
39 | },
40 | {
41 | page: "/contact-us",
42 | route: "/contact-us",
43 | },
44 | {
45 | page: "/index",
46 | route: "/index",
47 | },
48 | ],
49 | });
50 | });
51 |
52 | it("should generate routes from next 9 folder structure", async () => {
53 | const testPath = path.resolve(__dirname, "__fixtures__", "next9", "pages");
54 |
55 | const routes = await generateMappingsFromPagesFolder(testPath);
56 | expect(routes).toEqual({
57 | prefix: "",
58 | mappings: [
59 | {
60 | page: "/deep",
61 | route: "/:foo/:deep",
62 | },
63 | {
64 | page: "/query",
65 | route: "/:foo/:query",
66 | },
67 | {
68 | page: "/bar",
69 | route: "/:foo/bar",
70 | },
71 | {
72 | page: "/fixed",
73 | route: "/:foo/fixed",
74 | },
75 | ],
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/tests/terranext.test.js:
--------------------------------------------------------------------------------
1 | describe.skip("", () => {
2 | it("expec", () => {
3 | expect(true).toBe(true);
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "noEmit": true,
5 | "allowJs": true,
6 | "checkJs": true,
7 | "downlevelIteration": true,
8 | "declaration": true,
9 | "declarationDir": "./types",
10 | "skipLibCheck": true
11 | },
12 | "include": ["./src", "./tests"],
13 | "exclude": ["./src/compatLayer.js"]
14 | }
15 |
--------------------------------------------------------------------------------