start separating lambdas by functionality,
This commit is contained in:
parent
048904c000
commit
5db32796f6
28 changed files with 463 additions and 465 deletions
2
.d.ts
vendored
2
.d.ts
vendored
|
|
@ -56,7 +56,7 @@ type BaseDeploymentOptions = {
|
||||||
onPreDeploy?: () => Promise<void>;
|
onPreDeploy?: () => Promise<void>;
|
||||||
onPostDeploy?: () => Promise<void>;
|
onPostDeploy?: () => Promise<void>;
|
||||||
onShutdown?: () => Promise<void>;
|
onShutdown?: () => Promise<void>;
|
||||||
build?: BuildOptions | boolean;
|
buildOptions?: BuildOptions | boolean;
|
||||||
nextConfigDir?: string;
|
nextConfigDir?: string;
|
||||||
domain?: string | string[];
|
domain?: string | string[];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
216
README.md
216
README.md
|
|
@ -6,16 +6,16 @@ Effortless deployment for Next.js apps 🚀
|
||||||
|
|
||||||
- [Getting Started](#Getting-Started)
|
- [Getting Started](#Getting-Started)
|
||||||
- [Background](#Background)
|
- [Background](#Background)
|
||||||
- [Configuration](#Configuration)
|
|
||||||
- [AWS](#AWS)
|
|
||||||
- [GitHub](#GitHub)
|
|
||||||
- [Advanced Configuration](#Advanced-Configuration)
|
|
||||||
- [CLI](#CLI)
|
- [CLI](#CLI)
|
||||||
|
- [Environment](#Environment)
|
||||||
|
- [GitHub](#GitHub)
|
||||||
|
- [AWS](#AWS)
|
||||||
- [Configuration Options](#Configuration-Options)
|
- [Configuration Options](#Configuration-Options)
|
||||||
- [Base Options](#Base-Options)
|
- [Base Options](#Base-Options)
|
||||||
- [GitHub Options](#GitHub-Options)
|
- [GitHub Options](#GitHub-Options)
|
||||||
- [AWS Options](#AWS-Options)
|
- [AWS Options](#AWS-Options)
|
||||||
- [CI/CD](#CI/CD)
|
- [Advanced Configuration](#Advanced-Configuration)
|
||||||
|
- [CI/CD](#CICD)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|
@ -36,7 +36,27 @@ Next Deploy was created to deploy web applications built using the wonderful [Ne
|
||||||
|
|
||||||
Next Deploy started as a fork of [serverless-next.js](https://github.com/serverless-nextjs/serverless-next.js) which itself is an orchestrator of various orphaned [serverless-components](https://github.com/serverless-components/). Next Deploy was created out of a need for a better, strongly typed codebase and an ability to provide more advanced functionality without the [influence of corporate backers](https://opencollective.com/goserverless#section-contributions).
|
Next Deploy started as a fork of [serverless-next.js](https://github.com/serverless-nextjs/serverless-next.js) which itself is an orchestrator of various orphaned [serverless-components](https://github.com/serverless-components/). Next Deploy was created out of a need for a better, strongly typed codebase and an ability to provide more advanced functionality without the [influence of corporate backers](https://opencollective.com/goserverless#section-contributions).
|
||||||
|
|
||||||
## Configuration
|
## CLI
|
||||||
|
|
||||||
|
Next Deploy comes with a `next-deploy [argument]` CLI that you can run with `npx next-deploy` or `yarn next-deploy`.
|
||||||
|
|
||||||
|
There are currently 5 supported arguments:
|
||||||
|
|
||||||
|
- **Default** (default): Runs **Build** followed by **Deploy**.
|
||||||
|
|
||||||
|
- **Init**: Creates the base next-deploy.config.js configuration for your project.
|
||||||
|
|
||||||
|
- **Build**: Build the application for deployment.
|
||||||
|
|
||||||
|
- **Deploy**: Deploy the built application.
|
||||||
|
|
||||||
|
- **Remove**: Remove the deployed resources. Note that some resources (such as lambda@edge lambdas need to be cleaned up manually due to a timing constraint).
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
### GitHub
|
||||||
|
|
||||||
|
No specific environment configuration is necessary. By default, your app will be built and exported to the `gh-pages` branch.
|
||||||
|
|
||||||
### AWS
|
### AWS
|
||||||
|
|
||||||
|
|
@ -47,86 +67,158 @@ AWS_ACCESS_KEY_ID=******
|
||||||
AWS_SECRET_ACCESS_KEY=******
|
AWS_SECRET_ACCESS_KEY=******
|
||||||
```
|
```
|
||||||
|
|
||||||
If your account is restricted, [ensure that you have enough permissions to deploy](docs/aws-permissions.md).
|
If your account is restricted, ensure that you have enough permissions to deploy.
|
||||||
|
You will need the following permissions:
|
||||||
|
|
||||||
### GitHub
|
<details>
|
||||||
|
<summary>Click to view</summary>
|
||||||
|
|
||||||
No specific configuration is necessary. By default, your app will be built and exported to the `gh-pages` branch.
|
```
|
||||||
|
'acm:DescribeCertificate', // only for custom domains
|
||||||
### Advanced Configuration
|
'acm:ListCertificates', // only for custom domains
|
||||||
|
'acm:RequestCertificate', // only for custom domains
|
||||||
The deployment configuration is to be provided through `next-deploy.config.js`, which will be automatically created for you the first time you run `next-deploy`.
|
'cloudfront:CreateCloudFrontOriginAccessIdentity',
|
||||||
|
'cloudfront:CreateDistribution',
|
||||||
```javascript
|
'cloudfront:CreateInvalidation',
|
||||||
module.exports = {
|
'cloudfront:GetDistribution',
|
||||||
engine: 'aws',
|
'cloudfront:GetDistributionConfig',
|
||||||
debug: true,
|
'cloudfront:ListCloudFrontOriginAccessIdentities',
|
||||||
};
|
'cloudfront:ListDistributions',
|
||||||
|
'cloudfront:ListDistributionsByLambdaFunction',
|
||||||
|
'cloudfront:ListDistributionsByWebACLId',
|
||||||
|
'cloudfront:ListFieldLevelEncryptionConfigs',
|
||||||
|
'cloudfront:ListFieldLevelEncryptionProfiles',
|
||||||
|
'cloudfront:ListInvalidations',
|
||||||
|
'cloudfront:ListPublicKeys',
|
||||||
|
'cloudfront:ListStreamingDistributions',
|
||||||
|
'cloudfront:UpdateDistribution',
|
||||||
|
'iam:AttachRolePolicy',
|
||||||
|
'iam:CreateRole',
|
||||||
|
'iam:CreateServiceLinkedRole',
|
||||||
|
'iam:GetRole',
|
||||||
|
'iam:PassRole',
|
||||||
|
'lambda:CreateFunction',
|
||||||
|
'lambda:EnableReplication',
|
||||||
|
'lambda:DeleteFunction', // only for custom domains
|
||||||
|
'lambda:GetFunction',
|
||||||
|
'lambda:GetFunctionConfiguration',
|
||||||
|
'lambda:PublishVersion',
|
||||||
|
'lambda:UpdateFunctionCode',
|
||||||
|
'lambda:UpdateFunctionConfiguration',
|
||||||
|
'route53:ChangeResourceRecordSets', // only for custom domains
|
||||||
|
'route53:ListHostedZonesByName',
|
||||||
|
'route53:ListResourceRecordSets', // only for custom domains
|
||||||
|
's3:CreateBucket',
|
||||||
|
's3:GetAccelerateConfiguration',
|
||||||
|
's3:GetObject', // only if persisting state to S3 for CI/CD
|
||||||
|
's3:HeadBucket',
|
||||||
|
's3:ListBucket',
|
||||||
|
's3:PutAccelerateConfiguration',
|
||||||
|
's3:PutBucketPolicy',
|
||||||
|
's3:PutObject';
|
||||||
```
|
```
|
||||||
|
|
||||||
A more advanced configuration that sets more [configurable options](#Configuration-Options):
|
</details>
|
||||||
|
|
||||||
```javascript
|
|
||||||
module.exports = {
|
|
||||||
engine: 'aws',
|
|
||||||
onShutdown: () => console.log('⛔ Interrupted ⛔'),
|
|
||||||
onPostDeploy: () => console.log('🌟 Deployment Complete 🌟'),
|
|
||||||
debug: true,
|
|
||||||
|
|
||||||
bucketName: 'my-bucket-name',
|
|
||||||
description: 'My new lambda description.',
|
|
||||||
name: 'lambda-name',
|
|
||||||
domain: ['foobar', 'example.com'],
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Environment variables may be substituted from `process.env` to allow for more flexibility that one would need for CI/CD.
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
|
|
||||||
Next Deploy comes with a `next-deploy [argument]` CLI that you can run like `npx next-deploy` or `yarn next-deploy`.
|
|
||||||
|
|
||||||
There are currently 5 supported arguments:
|
|
||||||
|
|
||||||
**Default** (default): Runs **Build** followed by **Deploy**.
|
|
||||||
|
|
||||||
**Init**: Creates the base next-deploy.config.js configuration for your project.
|
|
||||||
|
|
||||||
**Build**: Build the application for deployment.
|
|
||||||
|
|
||||||
**Deploy**: Deploy the built application.
|
|
||||||
|
|
||||||
**Remove**: Remove, or at least attempt to remove, the deployed resources. Note that some resources (such as lambda@edge lambdas need to be cleaned up manually due to a timing constraint).
|
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
The next-deploy config varies by the provider (engine) that you're deploying to. All configuration options are optional and come with sensible defaults.
|
The next-deploy config varies by the provider (engine) that you're deploying to. All configuration options are optional and come with sensible defaults.
|
||||||
|
The deployment configuration is to be provided through `next-deploy.config.js`, which will be automatically created for you the first time you run `next-deploy` or `next-deploy init`.
|
||||||
|
|
||||||
### Base Options
|
### Base Options
|
||||||
|
|
||||||
All engines support the basic options:
|
All engines support the basic options:
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| :------------ | :---------------------- | :---------- | :------------------------------------------------------------------------------------------------------- |
|
| ------------- | ------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
|
||||||
| engine | `"aws"\|"github"` | `"aws"` | The platform to deploy to. |
|
| engine | `"aws"\|"github"` | `aws` | The platform to deploy to. |
|
||||||
| debug | `boolean` | `false` | Print helpful messages to |
|
| debug | `boolean` | `false` | Print helpful messages to |
|
||||||
| onPreDeploy | `() => Promise<void>` | `undefined` | A callback that gets called before the deployment. |
|
| onPreDeploy | `() => Promise<void>` | `null` | A callback that gets called before the deployment. |
|
||||||
| onPostDeploy | `() => Promise<void>` | `undefined` | A callback that gets called after the deployment successfully finishes. |
|
| onPostDeploy | `() => Promise<void>` | `null` | A callback that gets called after the deployment successfully finishes. |
|
||||||
| onShutdown | `() => Promise<void>` | `undefined` | A callback that gets called after the deployment is shutdown by a INT/QUIT/TERM signal like from ctrl+c. |
|
| onShutdown | `() => Promise<void>` | `null` | A callback that gets called after the deployment is shutdown by a INT/QUIT/TERM signal like from ctrl+c. |
|
||||||
| build | `BuildOptions\|boolean` | `true` | Whether a new build should be run run or not. |
|
| buildOptions | [`BuildOptions`](#BuildOptions) | `{}` | Build related options. |
|
||||||
| nextConfigDir | `string` | `./` | The directory holding the `next.config.js`. |
|
| nextConfigDir | `string` | `./` | The directory holding the `next.config.js`. |
|
||||||
| domain | `string\|string[]` | `null` | The domain to deploy to . |
|
| domain | `string\|string[]` | `null` | The domain to deploy to . |
|
||||||
|
|
||||||
|
#### BuildOptions
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ---- | ---------- | ------------------------ | ---------------------------------------------------- |
|
||||||
|
| cmd | `string` | `node_modules/.bin/next` | The build command. |
|
||||||
|
| args | `string[]` | `['build']` | A list of arguments to provide to the build command. |
|
||||||
|
| cwd | `string` | `./` | The current working directory. |
|
||||||
|
|
||||||
### Github Options
|
### Github Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| :------------- | :-------------------------------------------------------------- | :--------------------------------- | :--------------------------------------------------------------------------------------- |
|
| -------------- | --------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||||
| publishOptions | [`PublishOptions`](https://github.com/tschaub/gh-pages#options) | `{message: '...', dotfiles: true}` | The [git-hub page options](https://github.com/tschaub/gh-pages#options) to publish with. |
|
| publishOptions | [`PublishOptions`](https://github.com/tschaub/gh-pages#options) | `{message: "Next Deployment Update", dotfiles: true}` | The [git-hub page options](https://github.com/tschaub/gh-pages#options) to publish with. |
|
||||||
|
|
||||||
### AWS Options
|
### AWS Options
|
||||||
|
|
||||||
TODO
|
| Name | Type | Default | Description |
|
||||||
|
| -------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| bucketName | `string` | `*auto generated*` | Custom bucket name where static assets are stored. |
|
||||||
|
| bucketRegion | `string` | `us-east-1` | Region where you want to host your S3 bucket. |
|
||||||
|
| publicDirectoryCache | `boolean\|`[`PublicDirectoryCache`](#PublicDirectoryCache) | `true` | Customize the public/static directory asset caching policy. Assigning an object lets you customize the caching policy and the types of files being cached. Assigning false disables caching. |
|
||||||
|
| memory | `number` | `512` | The amount of memory that a lambda has access to. Increasing the lambda's memory also increases its CPU allocation. The value must be a multiple of 64 MB. |
|
||||||
|
| timeout | `number` | `10` | The amount of time that the lambda allows a function to run before stopping it. The maximum allowed value is 900 seconds. |
|
||||||
|
| name | `string` | `*auto generated*` | The name of the lambda function. |
|
||||||
|
| runtime | `string` | `nodejs12.x` | The identifier of the lambda's runtime. |
|
||||||
|
| description | `string` | <details>`"*lambda type* handler for the Next CloudFront distribution."`</details> | A description of the lambda. |
|
||||||
|
| policy | `string` | <details>`arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole`</details> | The arn policy of the lambda. |
|
||||||
|
| domainType | `"www"\|"apex"\|"both"` | `both` | Can be one of: "**apex**" - apex domain only, don't create a www subdomain. "**www**" - www domain only, don't create an apex subdomain. "**both**" - create both www and apex domains when either one is provided. |
|
||||||
|
| cloudfront | [`CloudFront`](#CloudFront) | `{}` | Additional cloudfront options. |
|
||||||
|
|
||||||
## CI/CD
|
#### PublicDirectoryCache
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ----- | -------- | ------------------------------------------------- | ---------------------------------------- |
|
||||||
|
| test | `string` | `/\.(gif|jpe?g|jp2|tiff|png|webp|bmp|svg|ico)$/i` | The test to apply the caching behaviour. |
|
||||||
|
| value | `string` | `public, max-age=31536000, must-revalidate` | The caching behavior. |
|
||||||
|
|
||||||
|
#### CloudFront
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ---------------------- | ------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| ttl | `number` | `0` | The amount of time that you want objects to stay in CloudFront's cache before it forwards another request to determine whether the object has been updated. |
|
||||||
|
| smoothStreaming | `boolean` | `false` | Indicates whether you want to distribute media files in the Microsoft Smooth Streaming format. |
|
||||||
|
| viewerProtocolPolicy | `string` | `redirect-to-https` | The policy for viewers to access the content. |
|
||||||
|
| fieldLevelEncryptionId | `string` | `""` | The value of the ID for the field-level encryption configuration that you want to use. |
|
||||||
|
| forward | [`Forward`](#Forward) | `{}` | Determines the forwarding configuration |
|
||||||
|
| viewerCertificate | [`ViewerCertificate`](#ViewerCertificate) | `{}` | Determines the SSL/TLS configuration for communicating with viewers. |
|
||||||
|
| cookies | `string\|string[]` | `all` | Indicates which cookies should be forwarded. |
|
||||||
|
| queryString | `boolean` | `true` | Indicates whether the query string should be forwarded. |
|
||||||
|
| lambda@edge | [`LambdaAtEdgeConfig`](#LambdaAtEdgeConfig) | `TODO` | TODO |
|
||||||
|
|
||||||
|
#### Forward
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| -------------------- | ---------- | ------- | ----------- |
|
||||||
|
| cookies | `string[]` | `TODO` | TODO |
|
||||||
|
| queryString | `string` | `TODO` | TODO |
|
||||||
|
| headers | `string[]` | `TODO` | TODO |
|
||||||
|
| queryStringCacheKeys | `string[]` | `TODO` | TODO |
|
||||||
|
|
||||||
|
#### ViewerCertificate
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ---------------------- | -------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| ACMCertificateArn | `string` | `null` | If the SSL/TLS certificate is stored in ACM, provide the ARN of the ACM certificate. CloudFront only supports ACM certificates in the us-east-1. |
|
||||||
|
| SSLSupportMethod | `string` | `sni-only` | Specifies which viewers the distribution accepts HTTPS connections from. **sni-only** – The distribution accepts HTTPS connections only from viewers that support server SNI (all modern browsers). **vip** – The distribution accepts HTTPS connections from **all** (not recommended and results in additional monthly charges). |
|
||||||
|
| minimumProtocolVersion | `string` | `TLSv1.2_2018` | The security policy that you want to use for HTTPS connections with viewers. |
|
||||||
|
|
||||||
|
#### LambdaAtEdgeConfig
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ----------- | --------- | ------- | ----------- |
|
||||||
|
| arn | `string` | `null` | TODO |
|
||||||
|
| includeBody | `boolean` | `TODO` | TODO |
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
Environment variables may be substituted from `process.env` to allow for more flexibility.
|
||||||
|
|
||||||
|
### CI/CD
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "next-deploy",
|
"name": "next-deploy",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0-alpha-0",
|
||||||
"description": "Effortless deployment for Next.js apps 🚀",
|
"description": "Effortless deployment for Next.js apps 🚀",
|
||||||
"author": "Nidratech Ltd. <egor@nidratech.com>",
|
"author": "Nidratech Ltd. <egor@nidratech.com>",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
@ -77,15 +77,15 @@
|
||||||
"@types/klaw": "^3.0.1",
|
"@types/klaw": "^3.0.1",
|
||||||
"@types/klaw-sync": "^6.0.0",
|
"@types/klaw-sync": "^6.0.0",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.0",
|
||||||
"@types/node": "^14.0.22",
|
"@types/node": "^14.0.23",
|
||||||
"@types/path-to-regexp": "^1.7.0",
|
"@types/path-to-regexp": "^1.7.0",
|
||||||
"@types/ramda": "^0.27.11",
|
"@types/ramda": "^0.27.11",
|
||||||
"@types/react": "^16.9.43",
|
"@types/react": "^16.9.43",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/strip-ansi": "^5.2.1",
|
"@types/strip-ansi": "^5.2.1",
|
||||||
"@types/webpack": "^4.41.21",
|
"@types/webpack": "^4.41.21",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
||||||
"@typescript-eslint/parser": "^3.6.0",
|
"@typescript-eslint/parser": "^3.6.1",
|
||||||
"eslint": "^7.4.0",
|
"eslint": "^7.4.0",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
|
|
@ -103,8 +103,5 @@
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,md,yml}": "prettier --write"
|
"*.{js,ts,md,yml}": "prettier --write"
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"which": "^2.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ class CloudFrontComponent extends Component {
|
||||||
region: this.state.region,
|
region: this.state.region,
|
||||||
});
|
});
|
||||||
|
|
||||||
await deleteCloudFrontDistribution(cf, this.state.id);
|
this.context.debug(
|
||||||
|
`Removing CloudFront distribution of ID ${this.state.id}. It could take a while.`
|
||||||
|
);
|
||||||
|
await deleteCloudFrontDistribution(cf, this.state.id, this.context.debug);
|
||||||
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
await this.save();
|
await this.save();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { LambdaAtEdgeConfig, LambdaAtEdgeType } from '../../types';
|
||||||
|
|
||||||
const validLambdaTriggers = [
|
const validLambdaTriggers = [
|
||||||
'viewer-request',
|
'viewer-request',
|
||||||
'origin-request',
|
'origin-request',
|
||||||
|
|
@ -5,10 +7,31 @@ const validLambdaTriggers = [
|
||||||
'viewer-response',
|
'viewer-response',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const triggersAllowedBody = ['viewer-request', 'origin-request'];
|
||||||
|
|
||||||
|
const makeCacheItem = (eventType: string, lambdaConfig: LambdaAtEdgeType) => {
|
||||||
|
let arn, includeBody;
|
||||||
|
if (typeof lambdaConfig === 'string') {
|
||||||
|
arn = lambdaConfig;
|
||||||
|
includeBody = triggersAllowedBody.includes(eventType);
|
||||||
|
} else {
|
||||||
|
({ arn, includeBody } = lambdaConfig);
|
||||||
|
if (includeBody && !triggersAllowedBody.includes(eventType)) {
|
||||||
|
throw new Error(`"includeBody" not allowed for ${eventType} lambda triggers.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
EventType: eventType,
|
||||||
|
LambdaFunctionARN: arn,
|
||||||
|
IncludeBody: includeBody,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// adds lambda@edge to cache behavior passed
|
// adds lambda@edge to cache behavior passed
|
||||||
const addLambdaAtEdgeToCacheBehavior = (
|
const addLambdaAtEdgeToCacheBehavior = (
|
||||||
cacheBehavior: any,
|
cacheBehavior: any,
|
||||||
lambdaAtEdgeConfig: Record<string, unknown> = {}
|
lambdaAtEdgeConfig: LambdaAtEdgeConfig = {}
|
||||||
) => {
|
) => {
|
||||||
Object.keys(lambdaAtEdgeConfig).forEach((eventType) => {
|
Object.keys(lambdaAtEdgeConfig).forEach((eventType) => {
|
||||||
if (!validLambdaTriggers.includes(eventType)) {
|
if (!validLambdaTriggers.includes(eventType)) {
|
||||||
|
|
@ -17,13 +40,12 @@ const addLambdaAtEdgeToCacheBehavior = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheBehavior.LambdaFunctionAssociations.Items.push(
|
||||||
|
makeCacheItem(eventType, lambdaAtEdgeConfig[eventType])
|
||||||
|
);
|
||||||
|
|
||||||
cacheBehavior.LambdaFunctionAssociations.Quantity =
|
cacheBehavior.LambdaFunctionAssociations.Quantity =
|
||||||
cacheBehavior.LambdaFunctionAssociations.Quantity + 1;
|
cacheBehavior.LambdaFunctionAssociations.Quantity + 1;
|
||||||
cacheBehavior.LambdaFunctionAssociations.Items.push({
|
|
||||||
EventType: eventType,
|
|
||||||
LambdaFunctionARN: lambdaAtEdgeConfig[eventType],
|
|
||||||
IncludeBody: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import addLambdaAtEdgeToCacheBehavior from './addLambdaAtEdgeToCacheBehavior';
|
||||||
import getForwardedValues from './getForwardedValues';
|
import getForwardedValues from './getForwardedValues';
|
||||||
import { PathPatternConfig } from '../../types';
|
import { PathPatternConfig } from '../../types';
|
||||||
|
|
||||||
const getDefaultCacheBehavior = (originId: string, defaults: Partial<PathPatternConfig> = {}) => {
|
const getDefaultCacheBehavior = (originId: string, defaults: PathPatternConfig = {}) => {
|
||||||
const {
|
const {
|
||||||
allowedHttpMethods = ['HEAD', 'GET'],
|
allowedHttpMethods = ['HEAD', 'GET'],
|
||||||
forward = {},
|
forward = {},
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ const forwardDefaults = {
|
||||||
* @param defaults Default framework values (default cache behavior and custom cache behavior have different default values)
|
* @param defaults Default framework values (default cache behavior and custom cache behavior have different default values)
|
||||||
* @returns Object
|
* @returns Object
|
||||||
*/
|
*/
|
||||||
export default function getForwardedValues(
|
export default function getForwardedValues(config: Forward = {}, defaults?: PathPatternConfig) {
|
||||||
config: Forward = {},
|
|
||||||
defaults?: Partial<PathPatternConfig>
|
|
||||||
) {
|
|
||||||
const defaultValues = { ...forwardDefaults, ...defaults };
|
const defaultValues = { ...forwardDefaults, ...defaults };
|
||||||
const {
|
const {
|
||||||
cookies,
|
cookies,
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ import { Origin } from '../../types';
|
||||||
|
|
||||||
const getOriginConfig = (origin: string | Origin, { originAccessIdentityId = '' }) => {
|
const getOriginConfig = (origin: string | Origin, { originAccessIdentityId = '' }) => {
|
||||||
const originUrl = typeof origin === 'string' ? origin : origin.url;
|
const originUrl = typeof origin === 'string' ? origin : origin.url;
|
||||||
|
const protocolPolicy = typeof origin === 'string' ? null : origin.protocolPolicy;
|
||||||
|
|
||||||
const { hostname, pathname } = url.parse(originUrl);
|
const { hostname, pathname } = url.parse(originUrl);
|
||||||
|
|
||||||
const originConfig: CloudFront.Origin = {
|
const originConfig: CloudFront.Origin = {
|
||||||
Id: hostname as string,
|
Id: `${hostname}${pathname}`.replace(/\/$/, ''),
|
||||||
DomainName: hostname as string,
|
DomainName: hostname as string,
|
||||||
CustomHeaders: {
|
CustomHeaders: {
|
||||||
Quantity: 0,
|
Quantity: 0,
|
||||||
|
|
@ -18,10 +19,11 @@ const getOriginConfig = (origin: string | Origin, { originAccessIdentityId = ''
|
||||||
OriginPath: pathname === '/' ? '' : (pathname as string),
|
OriginPath: pathname === '/' ? '' : (pathname as string),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (originUrl.includes('s3')) {
|
if (originUrl.includes('s3') && !originUrl.includes('s3-website')) {
|
||||||
const bucketName = (hostname as string).split('.')[0];
|
const bucketName = (hostname as string).split('.')[0];
|
||||||
originConfig.Id = bucketName;
|
|
||||||
originConfig.DomainName = `${bucketName}.s3.amazonaws.com`;
|
originConfig.Id = pathname === '/' ? bucketName : `${bucketName}${pathname}`;
|
||||||
|
originConfig.DomainName = hostname as string;
|
||||||
originConfig.S3OriginConfig = {
|
originConfig.S3OriginConfig = {
|
||||||
OriginAccessIdentity: originAccessIdentityId
|
OriginAccessIdentity: originAccessIdentityId
|
||||||
? `origin-access-identity/cloudfront/${originAccessIdentityId}`
|
? `origin-access-identity/cloudfront/${originAccessIdentityId}`
|
||||||
|
|
@ -31,7 +33,7 @@ const getOriginConfig = (origin: string | Origin, { originAccessIdentityId = ''
|
||||||
originConfig.CustomOriginConfig = {
|
originConfig.CustomOriginConfig = {
|
||||||
HTTPPort: 80,
|
HTTPPort: 80,
|
||||||
HTTPSPort: 443,
|
HTTPSPort: 443,
|
||||||
OriginProtocolPolicy: 'https-only',
|
OriginProtocolPolicy: protocolPolicy || 'https-only',
|
||||||
OriginSslProtocols: {
|
OriginSslProtocols: {
|
||||||
Quantity: 1,
|
Quantity: 1,
|
||||||
Items: ['TLSv1.2'],
|
Items: ['TLSv1.2'],
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,20 @@ export { default as createInvalidation } from './createInvalidation';
|
||||||
|
|
||||||
const servePrivateContentEnabled = (inputs: CloudFrontInputs) =>
|
const servePrivateContentEnabled = (inputs: CloudFrontInputs) =>
|
||||||
inputs?.origins?.some((origin: string | Origin) => origin && (origin as Origin).private === true);
|
inputs?.origins?.some((origin: string | Origin) => origin && (origin as Origin).private === true);
|
||||||
|
const unique = (value: string, index: number, self: string[]) => self.indexOf(value) === index;
|
||||||
const updateBucketsPolicies = async (
|
const updateBucketsPolicies = async (
|
||||||
s3: S3,
|
s3: S3,
|
||||||
origins: CloudFront.Origins,
|
origins: CloudFront.Origins,
|
||||||
s3CanonicalUserId: string
|
s3CanonicalUserId: string
|
||||||
) => {
|
) => {
|
||||||
// update bucket policies with cloudfront access
|
// update bucket policies with cloudfront access
|
||||||
const bucketNames = origins.Items.filter((origin) => origin.S3OriginConfig).map(
|
const bucketNames = origins.Items.filter((origin) => origin.S3OriginConfig)
|
||||||
(origin) => origin.Id
|
.map(
|
||||||
);
|
(origin) =>
|
||||||
|
// remove path from the bucket name if origin had pathname
|
||||||
|
origin.Id.split('/')[0]
|
||||||
|
)
|
||||||
|
.filter(unique);
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
bucketNames.map((bucketName: string) =>
|
bucketNames.map((bucketName: string) =>
|
||||||
|
|
@ -142,7 +146,11 @@ export const updateCloudFrontDistribution = async (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const disableCloudFrontDistribution = async (cf: CloudFront, distributionId: string) => {
|
const disableCloudFrontDistribution = async (
|
||||||
|
cf: CloudFront,
|
||||||
|
distributionId: string,
|
||||||
|
debug: (message: string) => void
|
||||||
|
) => {
|
||||||
const distributionConfigResponse = await cf
|
const distributionConfigResponse = await cf
|
||||||
.getDistributionConfig({ Id: distributionId })
|
.getDistributionConfig({ Id: distributionId })
|
||||||
.promise();
|
.promise();
|
||||||
|
|
@ -162,27 +170,24 @@ const disableCloudFrontDistribution = async (cf: CloudFront, distributionId: str
|
||||||
|
|
||||||
const res = await cf.updateDistribution(updateDistributionRequest).promise();
|
const res = await cf.updateDistribution(updateDistributionRequest).promise();
|
||||||
|
|
||||||
return {
|
debug(`Waiting for the CloudFront distribution changes to be deployed.`);
|
||||||
id: res?.Distribution?.Id,
|
await cf.waitFor('distributionDeployed', { Id: distributionId }).promise();
|
||||||
arn: res?.Distribution?.ARN,
|
|
||||||
url: `https://${res?.Distribution?.DomainName}`,
|
return res;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteCloudFrontDistribution = async (
|
export const deleteCloudFrontDistribution = async (
|
||||||
cf: CloudFront,
|
cf: CloudFront,
|
||||||
distributionId: string
|
distributionId: string,
|
||||||
|
debug: (message: string) => void
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
let res = await cf.getDistributionConfig({ Id: distributionId }).promise();
|
||||||
const res = await cf.getDistributionConfig({ Id: distributionId }).promise();
|
|
||||||
|
if (res?.DistributionConfig?.Enabled) {
|
||||||
|
debug(`Disabling CloudFront distribution of ID ${distributionId} before removal.`);
|
||||||
|
res = await disableCloudFrontDistribution(cf, distributionId, debug);
|
||||||
|
}
|
||||||
|
|
||||||
const params = { Id: distributionId, IfMatch: res.ETag };
|
const params = { Id: distributionId, IfMatch: res.ETag };
|
||||||
await cf.deleteDistribution(params).promise();
|
await cf.deleteDistribution(params).promise();
|
||||||
} catch (e) {
|
|
||||||
if (e.code === 'DistributionNotDisabled') {
|
|
||||||
await disableCloudFrontDistribution(cf, distributionId);
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
39
packages/aws-cloudfront/types.d.ts
vendored
39
packages/aws-cloudfront/types.d.ts
vendored
|
|
@ -7,20 +7,43 @@ export type CloudFrontInputs = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type PathPatternConfig = {
|
type PathPatternConfig = {
|
||||||
allowedHttpMethods: string[];
|
allowedHttpMethods?: string[];
|
||||||
ttl: number;
|
ttl?: number;
|
||||||
compress: boolean;
|
compress?: boolean;
|
||||||
smoothStreaming: boolean;
|
smoothStreaming?: boolean;
|
||||||
viewerProtocolPolicy: string;
|
viewerProtocolPolicy?: string;
|
||||||
fieldLevelEncryptionId: string;
|
fieldLevelEncryptionId?: string;
|
||||||
forward: Forward;
|
forward?: Forward;
|
||||||
[path: string]: any;
|
viewerCertificate?: ViewerCertificate;
|
||||||
|
cookies?: string;
|
||||||
|
queryString?: boolean;
|
||||||
|
'lambda@edge'?: LambdaAtEdgeConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ViewerCertificate = {
|
||||||
|
ACMCertificateArn: string;
|
||||||
|
SSLSupportMethod: string;
|
||||||
|
minimumProtocolVersion: string;
|
||||||
|
certificate: string;
|
||||||
|
certificateSource: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LambdaAtEdgeConfig = {
|
||||||
|
[type: string]: LambdaAtEdgeType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LambdaAtEdgeType =
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
arn: string;
|
||||||
|
includeBody: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type Origin = {
|
type Origin = {
|
||||||
url: string;
|
url: string;
|
||||||
private?: boolean;
|
private?: boolean;
|
||||||
pathPatterns?: Record<string, PathPatternConfig>;
|
pathPatterns?: Record<string, PathPatternConfig>;
|
||||||
|
protocolPolicy?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Forward = {
|
type Forward = {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,30 @@
|
||||||
import { Component } from '@serverless/core';
|
import { Component } from '@serverless/core';
|
||||||
import { pathExists, readJSON } from 'fs-extra';
|
import { readJSON } from 'fs-extra';
|
||||||
import { resolve, join } from 'path';
|
import { resolve, join } from 'path';
|
||||||
|
|
||||||
import Builder from '@next-deploy/aws-lambda-builder';
|
import Builder from '@next-deploy/aws-lambda-builder';
|
||||||
import AwsS3 from '@next-deploy/aws-s3';
|
import AwsS3 from '@next-deploy/aws-s3';
|
||||||
import AwsCloudFront from '@next-deploy/aws-cloudfront';
|
import AwsCloudFront from '@next-deploy/aws-cloudfront';
|
||||||
import { getDomains } from './utils';
|
import { OriginRequestHandlerManifest as BuildManifest } from '@next-deploy/aws-lambda-builder/types';
|
||||||
import {
|
|
||||||
OriginRequestDefaultHandlerManifest as BuildManifest,
|
|
||||||
OriginRequestApiHandlerManifest,
|
|
||||||
} from '@next-deploy/aws-lambda-builder/types';
|
|
||||||
import { PathPatternConfig } from '@next-deploy/aws-cloudfront/types';
|
import { PathPatternConfig } from '@next-deploy/aws-cloudfront/types';
|
||||||
import { Origin } from '@next-deploy/aws-cloudfront/types';
|
import { Origin } from '@next-deploy/aws-cloudfront/types';
|
||||||
|
import { getDomains } from './utils';
|
||||||
import { DeploymentResult, AwsComponentInputs, LambdaType, LambdaInput } from '../types';
|
import { DeploymentResult, AwsComponentInputs, LambdaType, LambdaInput } from '../types';
|
||||||
|
|
||||||
export const BUILD_DIR = '.next-deploy-build';
|
export const BUILD_DIR = '.next-deploy-build';
|
||||||
export const DEFAULT_LAMBDA_CODE_DIR = `${BUILD_DIR}/default-lambda`;
|
export const REQUEST_LAMBDA_CODE_DIR = `${BUILD_DIR}/request-lambda`;
|
||||||
export const API_LAMBDA_CODE_DIR = `${BUILD_DIR}/api-lambda`;
|
|
||||||
|
|
||||||
class AwsComponent extends Component {
|
class AwsComponent extends Component {
|
||||||
async default(inputs: AwsComponentInputs = {}): Promise<DeploymentResult> {
|
async default(inputs: AwsComponentInputs = {}): Promise<DeploymentResult> {
|
||||||
if (inputs.build !== false) {
|
if (inputs.buildOptions !== false) {
|
||||||
await this.build(inputs);
|
await this.build(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.deploy(inputs);
|
return this.deploy(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
readDefaultBuildManifest(nextConfigPath: string): Promise<BuildManifest> {
|
readRequestLambdaBuildManifest(nextConfigPath: string): Promise<BuildManifest> {
|
||||||
return readJSON(join(nextConfigPath, `${DEFAULT_LAMBDA_CODE_DIR}/manifest.json`));
|
return readJSON(join(nextConfigPath, `${REQUEST_LAMBDA_CODE_DIR}/manifest.json`));
|
||||||
}
|
}
|
||||||
|
|
||||||
validatePathPatterns(pathPatterns: string[], buildManifest: BuildManifest): void {
|
validatePathPatterns(pathPatterns: string[], buildManifest: BuildManifest): void {
|
||||||
|
|
@ -109,26 +105,22 @@ class AwsComponent extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async readApiBuildManifest(nextConfigPath: string): Promise<OriginRequestApiHandlerManifest> {
|
|
||||||
const path = join(nextConfigPath, `${API_LAMBDA_CODE_DIR}/manifest.json`);
|
|
||||||
|
|
||||||
return (await pathExists(path)) ? readJSON(path) : Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
async build(inputs: AwsComponentInputs = {}): Promise<void> {
|
async build(inputs: AwsComponentInputs = {}): Promise<void> {
|
||||||
const nextConfigPath = inputs.nextConfigDir ? resolve(inputs.nextConfigDir) : process.cwd();
|
const nextConfigPath = inputs.nextConfigDir ? resolve(inputs.nextConfigDir) : process.cwd();
|
||||||
const buildCwd =
|
const buildCwd =
|
||||||
typeof inputs.build === 'boolean' || typeof inputs.build === 'undefined' || !inputs.build.cwd
|
typeof inputs.buildOptions === 'boolean' ||
|
||||||
|
typeof inputs.buildOptions === 'undefined' ||
|
||||||
|
!inputs.buildOptions.cwd
|
||||||
? nextConfigPath
|
? nextConfigPath
|
||||||
: resolve(inputs.build.cwd);
|
: resolve(inputs.buildOptions.cwd);
|
||||||
const buildConfig: BuildOptions = {
|
const buildConfig: BuildOptions = {
|
||||||
enabled: inputs.build
|
enabled: inputs.buildOptions
|
||||||
? // @ts-ignore
|
? // @ts-ignore
|
||||||
inputs.build !== false && inputs.build.enabled !== false
|
inputs.buildOptions !== false && inputs.buildOptions.enabled !== false
|
||||||
: true,
|
: true,
|
||||||
cmd: 'node_modules/.bin/next',
|
cmd: 'node_modules/.bin/next',
|
||||||
args: ['build'],
|
args: ['build'],
|
||||||
...(typeof inputs.build === 'object' ? inputs.build : {}),
|
...(typeof inputs.buildOptions === 'object' ? inputs.buildOptions : {}),
|
||||||
cwd: buildCwd,
|
cwd: buildCwd,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -148,24 +140,23 @@ class AwsComponent extends Component {
|
||||||
|
|
||||||
const nextStaticPath = inputs.nextStaticDir ? resolve(inputs.nextStaticDir) : nextConfigPath;
|
const nextStaticPath = inputs.nextStaticDir ? resolve(inputs.nextStaticDir) : nextConfigPath;
|
||||||
|
|
||||||
const customCloudFrontConfig: Record<string, any> = inputs.cloudfront || {};
|
const customCloudFrontConfig: Record<string, any> = inputs.cloudfrontOptions || {};
|
||||||
|
const bucketRegion = inputs.bucketRegion || 'us-east-1';
|
||||||
|
|
||||||
const [defaultBuildManifest, apiBuildManifest] = await Promise.all([
|
const [defaultBuildManifest] = await Promise.all([
|
||||||
this.readDefaultBuildManifest(nextConfigPath),
|
this.readRequestLambdaBuildManifest(nextConfigPath),
|
||||||
this.readApiBuildManifest(nextConfigPath),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [bucket, cloudFront, defaultEdgeLambda, apiEdgeLambda] = await Promise.all([
|
const [bucket, cloudFront, requestEdgeLambda] = await Promise.all([
|
||||||
this.load('@next-deploy/aws-s3'),
|
this.load('@next-deploy/aws-s3'),
|
||||||
this.load('@next-deploy/aws-cloudfront'),
|
this.load('@next-deploy/aws-cloudfront'),
|
||||||
this.load('@next-deploy/aws-lambda', 'defaultEdgeLambda'),
|
this.load('@next-deploy/aws-lambda', 'requestEdgeLambda'),
|
||||||
this.load('@next-deploy/aws-lambda', 'apiEdgeLambda'),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const bucketOutputs = await bucket({
|
const bucketOutputs = await bucket({
|
||||||
accelerated: true,
|
accelerated: true,
|
||||||
name: inputs.bucketName,
|
name: inputs.bucketName,
|
||||||
region: inputs.bucketRegion || 'us-east-1',
|
region: bucketRegion,
|
||||||
});
|
});
|
||||||
|
|
||||||
await AwsS3.uploadStaticAssets({
|
await AwsS3.uploadStaticAssets({
|
||||||
|
|
@ -176,7 +167,7 @@ class AwsComponent extends Component {
|
||||||
publicDirectoryCache: inputs.publicDirectoryCache,
|
publicDirectoryCache: inputs.publicDirectoryCache,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bucketUrl = `http://${bucketOutputs.name}.s3.amazonaws.com`;
|
const bucketUrl = `http://${bucketOutputs.name}.s3.${bucketRegion}.amazonaws.com`;
|
||||||
|
|
||||||
// If origin is relative path then prepend the bucketUrl
|
// If origin is relative path then prepend the bucketUrl
|
||||||
// e.g. /path => http://bucket.s3.aws.com/path
|
// e.g. /path => http://bucket.s3.aws.com/path
|
||||||
|
|
@ -196,10 +187,10 @@ class AwsComponent extends Component {
|
||||||
|
|
||||||
// parse origins from inputs
|
// parse origins from inputs
|
||||||
let inputOrigins: any = [];
|
let inputOrigins: any = [];
|
||||||
if (inputs.cloudfront && inputs.cloudfront.origins) {
|
if (inputs.cloudfrontOptions && inputs.cloudfrontOptions.origins) {
|
||||||
const origins = inputs.cloudfront.origins as string[];
|
const origins = inputs.cloudfrontOptions.origins as string[];
|
||||||
inputOrigins = origins.map(expandRelativeUrls);
|
inputOrigins = origins.map(expandRelativeUrls);
|
||||||
delete inputs.cloudfront.origins;
|
delete inputs.cloudfrontOptions.origins;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloudFrontOrigins = [
|
const cloudFrontOrigins = [
|
||||||
|
|
@ -228,13 +219,8 @@ class AwsComponent extends Component {
|
||||||
...inputOrigins,
|
...inputOrigins,
|
||||||
];
|
];
|
||||||
|
|
||||||
const hasAPIPages =
|
|
||||||
apiBuildManifest &&
|
|
||||||
(Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 ||
|
|
||||||
Object.keys(apiBuildManifest.apis.dynamic).length > 0);
|
|
||||||
|
|
||||||
const getLambdaInputValue = (
|
const getLambdaInputValue = (
|
||||||
inputKey: 'memory' | 'timeout' | 'name' | 'runtime',
|
inputKey: 'memory' | 'timeout' | 'name' | 'runtime' | 'description',
|
||||||
lambdaType: LambdaType,
|
lambdaType: LambdaType,
|
||||||
defaultValue: string | number | undefined
|
defaultValue: string | number | undefined
|
||||||
): string | number | undefined => {
|
): string | number | undefined => {
|
||||||
|
|
@ -246,68 +232,39 @@ class AwsComponent extends Component {
|
||||||
if (!inputValue) {
|
if (!inputValue) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return inputValue[lambdaType] || defaultValue;
|
return inputValue[lambdaType] || defaultValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasAPIPages) {
|
|
||||||
const apiEdgeLambdaInput: LambdaInput = {
|
|
||||||
description: inputs.description
|
|
||||||
? `${inputs.description} (API)`
|
|
||||||
: 'API Lambda@Edge for Next CloudFront distribution',
|
|
||||||
handler: 'index.handler',
|
|
||||||
code: join(nextConfigPath, API_LAMBDA_CODE_DIR),
|
|
||||||
role: {
|
|
||||||
service: ['lambda.amazonaws.com', 'edgelambda.amazonaws.com'],
|
|
||||||
policy: {
|
|
||||||
arn:
|
|
||||||
inputs.policy || 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
memory: getLambdaInputValue('memory', 'apiLambda', 512) as number,
|
|
||||||
timeout: getLambdaInputValue('timeout', 'apiLambda', 10) as number,
|
|
||||||
runtime: getLambdaInputValue('runtime', 'apiLambda', 'nodejs12.x') as string,
|
|
||||||
name: getLambdaInputValue('name', 'apiLambda', undefined) as string | undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const apiEdgeLambdaOutputs = await apiEdgeLambda(apiEdgeLambdaInput);
|
|
||||||
|
|
||||||
const apiEdgeLambdaPublishOutputs = await apiEdgeLambda.publishVersion();
|
|
||||||
|
|
||||||
cloudFrontOrigins[0].pathPatterns['api/*'] = {
|
|
||||||
ttl: 0,
|
|
||||||
allowedHttpMethods: ['HEAD', 'DELETE', 'POST', 'GET', 'OPTIONS', 'PUT', 'PATCH'],
|
|
||||||
// lambda@edge key is last and therefore cannot be overridden
|
|
||||||
'lambda@edge': {
|
|
||||||
'origin-request': `${apiEdgeLambdaOutputs.arn}:${apiEdgeLambdaPublishOutputs.version}`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultEdgeLambdaInput: LambdaInput = {
|
const defaultEdgeLambdaInput: LambdaInput = {
|
||||||
description: inputs.description || 'Default Lambda@Edge for Next CloudFront distribution',
|
|
||||||
handler: 'index.handler',
|
handler: 'index.handler',
|
||||||
code: join(nextConfigPath, DEFAULT_LAMBDA_CODE_DIR),
|
code: join(nextConfigPath, REQUEST_LAMBDA_CODE_DIR),
|
||||||
role: {
|
role: {
|
||||||
service: ['lambda.amazonaws.com', 'edgelambda.amazonaws.com'],
|
service: ['lambda.amazonaws.com', 'edgelambda.amazonaws.com'],
|
||||||
policy: {
|
policy: {
|
||||||
arn: inputs.policy || 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole',
|
arn: inputs.policy || 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
memory: getLambdaInputValue('memory', 'defaultLambda', 512) as number,
|
memory: getLambdaInputValue('memory', 'requestLambda', 512) as number,
|
||||||
timeout: getLambdaInputValue('timeout', 'defaultLambda', 10) as number,
|
timeout: getLambdaInputValue('timeout', 'requestLambda', 10) as number,
|
||||||
runtime: getLambdaInputValue('runtime', 'defaultLambda', 'nodejs12.x') as string,
|
runtime: getLambdaInputValue('runtime', 'requestLambda', 'nodejs12.x') as string,
|
||||||
name: getLambdaInputValue('name', 'defaultLambda', undefined) as string | undefined,
|
name: getLambdaInputValue('name', 'requestLambda', undefined) as string | undefined,
|
||||||
|
description: getLambdaInputValue(
|
||||||
|
'description',
|
||||||
|
'requestLambda',
|
||||||
|
'Request handler for the Next CloudFront distribution.'
|
||||||
|
) as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultEdgeLambdaOutputs = await defaultEdgeLambda(defaultEdgeLambdaInput);
|
const requestEdgeLambdaOutputs = await requestEdgeLambda(defaultEdgeLambdaInput);
|
||||||
|
|
||||||
const defaultEdgeLambdaPublishOutputs = await defaultEdgeLambda.publishVersion();
|
const requestEdgeLambdaPublishOutputs = await requestEdgeLambda.publishVersion();
|
||||||
|
|
||||||
let defaultCloudfrontInputs = {} as Partial<PathPatternConfig>;
|
let defaultCloudfrontInputs = {} as PathPatternConfig;
|
||||||
|
|
||||||
if (inputs.cloudfront && inputs.cloudfront.defaults) {
|
if (inputs.cloudfrontOptions && inputs.cloudfrontOptions.defaults) {
|
||||||
defaultCloudfrontInputs = inputs.cloudfront.defaults;
|
defaultCloudfrontInputs = inputs.cloudfrontOptions.defaults;
|
||||||
delete inputs.cloudfront.defaults;
|
delete inputs.cloudfrontOptions.defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate that the custom config paths match generated paths in the manifest
|
// validate that the custom config paths match generated paths in the manifest
|
||||||
|
|
@ -329,7 +286,7 @@ class AwsComponent extends Component {
|
||||||
// for everything but static/* and _next/* we want to ensure that they are pointing at our lambda
|
// for everything but static/* and _next/* we want to ensure that they are pointing at our lambda
|
||||||
edgeConfig[
|
edgeConfig[
|
||||||
'origin-request'
|
'origin-request'
|
||||||
] = `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`;
|
] = `${requestEdgeLambdaOutputs.arn}:${requestEdgeLambdaPublishOutputs.version}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
cloudFrontOrigins[0].pathPatterns[path] = {
|
cloudFrontOrigins[0].pathPatterns[path] = {
|
||||||
|
|
@ -349,9 +306,14 @@ class AwsComponent extends Component {
|
||||||
|
|
||||||
cloudFrontOrigins[0].pathPatterns['_next/data/*'] = {
|
cloudFrontOrigins[0].pathPatterns['_next/data/*'] = {
|
||||||
ttl: 0,
|
ttl: 0,
|
||||||
|
forward: {
|
||||||
|
headers: 'none',
|
||||||
|
cookies: 'none',
|
||||||
|
queryString: false,
|
||||||
|
},
|
||||||
allowedHttpMethods: ['HEAD', 'GET'],
|
allowedHttpMethods: ['HEAD', 'GET'],
|
||||||
'lambda@edge': {
|
'lambda@edge': {
|
||||||
'origin-request': `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`,
|
'origin-request': `${requestEdgeLambdaOutputs.arn}:${requestEdgeLambdaPublishOutputs.version}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -372,10 +334,10 @@ class AwsComponent extends Component {
|
||||||
...defaultCloudfrontInputs.forward,
|
...defaultCloudfrontInputs.forward,
|
||||||
},
|
},
|
||||||
// everything after here cant be overridden
|
// everything after here cant be overridden
|
||||||
allowedHttpMethods: ['HEAD', 'GET'],
|
allowedHttpMethods: ['HEAD', 'DELETE', 'POST', 'GET', 'OPTIONS', 'PUT', 'PATCH'],
|
||||||
'lambda@edge': {
|
'lambda@edge': {
|
||||||
...defaultLambdaAtEdgeConfig,
|
...defaultLambdaAtEdgeConfig,
|
||||||
'origin-request': `${defaultEdgeLambdaOutputs.arn}:${defaultEdgeLambdaPublishOutputs.version}`,
|
'origin-request': `${requestEdgeLambdaOutputs.arn}:${requestEdgeLambdaPublishOutputs.version}`,
|
||||||
},
|
},
|
||||||
compress: true,
|
compress: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
14
packages/aws-component/types.d.ts
vendored
14
packages/aws-component/types.d.ts
vendored
|
|
@ -7,17 +7,17 @@ type AwsComponentInputs = BaseDeploymentOptions & {
|
||||||
bucketName?: string;
|
bucketName?: string;
|
||||||
bucketRegion?: string;
|
bucketRegion?: string;
|
||||||
publicDirectoryCache?: PublicDirectoryCache;
|
publicDirectoryCache?: PublicDirectoryCache;
|
||||||
memory?: number | { defaultLambda?: number; apiLambda?: number };
|
memory?: number | { [key in LambdaType]: number };
|
||||||
timeout?: number | { defaultLambda?: number; apiLambda?: number };
|
timeout?: number | { [key in LambdaType]: number };
|
||||||
name?: string | { defaultLambda?: string; apiLambda?: string };
|
name?: string | { [key in LambdaType]: string };
|
||||||
runtime?: string | { defaultLambda?: string; apiLambda?: string };
|
runtime?: string | { [key in LambdaType]: string };
|
||||||
description?: string;
|
description?: string | { [key in LambdaType]: string };
|
||||||
policy?: string;
|
policy?: string;
|
||||||
domainType?: DomainType;
|
domainType?: DomainType;
|
||||||
cloudfront?: CloudFrontInputs;
|
cloudfrontOptions?: CloudFrontInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
type LambdaType = 'defaultLambda' | 'apiLambda';
|
type LambdaType = 'requestLambda' | 'responseLambda';
|
||||||
|
|
||||||
type LambdaInput = {
|
type LambdaInput = {
|
||||||
description: string;
|
description: string;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ export const addDomainToCloudfrontDistribution = async (
|
||||||
subdomain: SubDomain,
|
subdomain: SubDomain,
|
||||||
certificateArn: string,
|
certificateArn: string,
|
||||||
domainType: DomainType,
|
domainType: DomainType,
|
||||||
defaultCloudfrontInputs: Partial<PathPatternConfig>
|
{ viewerCertificate }: PathPatternConfig
|
||||||
): Promise<{
|
): Promise<{
|
||||||
id?: string;
|
id?: string;
|
||||||
arn?: string;
|
arn?: string;
|
||||||
|
|
@ -389,12 +389,12 @@ export const addDomainToCloudfrontDistribution = async (
|
||||||
Items: [subdomain.domain],
|
Items: [subdomain.domain],
|
||||||
},
|
},
|
||||||
ViewerCertificate: {
|
ViewerCertificate: {
|
||||||
ACMCertificateArn: certificateArn,
|
ACMCertificateArn: viewerCertificate?.ACMCertificateArn || certificateArn,
|
||||||
SSLSupportMethod: 'sni-only',
|
SSLSupportMethod: viewerCertificate?.SSLSupportMethod || 'sni-only',
|
||||||
MinimumProtocolVersion: DEFAULT_MINIMUM_PROTOCOL_VERSION,
|
MinimumProtocolVersion:
|
||||||
Certificate: certificateArn,
|
viewerCertificate?.minimumProtocolVersion || DEFAULT_MINIMUM_PROTOCOL_VERSION,
|
||||||
CertificateSource: 'acm',
|
Certificate: viewerCertificate?.certificate || certificateArn,
|
||||||
...defaultCloudfrontInputs.viewerCertificate,
|
CertificateSource: viewerCertificate?.certificateSource || 'acm',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
packages/aws-domain/types.d.ts
vendored
2
packages/aws-domain/types.d.ts
vendored
|
|
@ -5,7 +5,7 @@ type AwsDomainInputs = {
|
||||||
region?: string;
|
region?: string;
|
||||||
privateZone?: boolean;
|
privateZone?: boolean;
|
||||||
domainType?: DomainType;
|
domainType?: DomainType;
|
||||||
defaultCloudfrontInputs?: Partial<PathPatternConfig>;
|
defaultCloudfrontInputs?: PathPatternConfig;
|
||||||
subdomains: SubDomain[];
|
subdomains: SubDomain[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
// @ts-ignore
|
|
||||||
import manifest from './manifest.json';
|
|
||||||
import { CloudFrontResultResponse, CloudFrontRequest } from 'aws-lambda';
|
|
||||||
import lambdaAtEdgeCompat from './compat';
|
|
||||||
import { OriginRequestApiHandlerManifest, OriginRequestEvent } from '../types';
|
|
||||||
|
|
||||||
const normaliseUri = (uri: string): string => (uri === '/' ? '/index' : uri);
|
|
||||||
|
|
||||||
const router = (manifest: OriginRequestApiHandlerManifest): ((path: string) => string | null) => {
|
|
||||||
const {
|
|
||||||
apis: { dynamic, nonDynamic },
|
|
||||||
} = manifest;
|
|
||||||
|
|
||||||
return (path: string): string | null => {
|
|
||||||
if (nonDynamic[path]) {
|
|
||||||
return nonDynamic[path];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const route in dynamic) {
|
|
||||||
const { file, regex } = dynamic[route];
|
|
||||||
|
|
||||||
const re = new RegExp(regex, 'i');
|
|
||||||
const pathMatchesRoute = re.test(path);
|
|
||||||
|
|
||||||
if (pathMatchesRoute) {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handler = async (
|
|
||||||
event: OriginRequestEvent
|
|
||||||
): Promise<CloudFrontResultResponse | CloudFrontRequest> => {
|
|
||||||
const request = event.Records[0].cf.request;
|
|
||||||
const uri = normaliseUri(request.uri);
|
|
||||||
|
|
||||||
const pagePath = router(manifest)(uri);
|
|
||||||
|
|
||||||
if (!pagePath) {
|
|
||||||
return {
|
|
||||||
status: '404',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const page = require(`./${pagePath}`);
|
|
||||||
const { req, res, responsePromise } = lambdaAtEdgeCompat(event.Records[0].cf);
|
|
||||||
|
|
||||||
page.default(req, res);
|
|
||||||
|
|
||||||
return responsePromise;
|
|
||||||
};
|
|
||||||
|
|
@ -15,12 +15,11 @@ import { pathToRegexp } from 'path-to-regexp';
|
||||||
|
|
||||||
import getAllFiles from './lib/getAllFilesInDirectory';
|
import getAllFiles from './lib/getAllFilesInDirectory';
|
||||||
import { getSortedRoutes } from './lib/sortedRoutes';
|
import { getSortedRoutes } from './lib/sortedRoutes';
|
||||||
import { OriginRequestDefaultHandlerManifest, OriginRequestApiHandlerManifest } from '../types';
|
import { OriginRequestHandlerManifest } from '../types';
|
||||||
import expressifyDynamicRoute from './lib/expressifyDynamicRoute';
|
import expressifyDynamicRoute from './lib/expressifyDynamicRoute';
|
||||||
import createServerlessConfig from './lib/createServerlessConfig';
|
import createServerlessConfig from './lib/createServerlessConfig';
|
||||||
|
|
||||||
export const DEFAULT_LAMBDA_CODE_DIR = 'default-lambda';
|
export const REQUEST_LAMBDA_CODE_DIR = 'request-lambda';
|
||||||
export const API_LAMBDA_CODE_DIR = 'api-lambda';
|
|
||||||
|
|
||||||
const pathToPosix = (path: string): string => path.replace(/\\/g, '/');
|
const pathToPosix = (path: string): string => path.replace(/\\/g, '/');
|
||||||
const normalizeNodeModules = (path: string): string => path.substring(path.indexOf('node_modules'));
|
const normalizeNodeModules = (path: string): string => path.substring(path.indexOf('node_modules'));
|
||||||
|
|
@ -49,6 +48,7 @@ class Builder {
|
||||||
this.dotNextDir = join(this.nextConfigDir, '.next');
|
this.dotNextDir = join(this.nextConfigDir, '.next');
|
||||||
this.serverlessDir = join(this.dotNextDir, 'serverless');
|
this.serverlessDir = join(this.dotNextDir, 'serverless');
|
||||||
this.outputDir = outputDir;
|
this.outputDir = outputDir;
|
||||||
|
|
||||||
if (buildOptions) {
|
if (buildOptions) {
|
||||||
this.buildOptions = buildOptions;
|
this.buildOptions = buildOptions;
|
||||||
}
|
}
|
||||||
|
|
@ -92,9 +92,9 @@ class Builder {
|
||||||
const sortedDynamicRoutedPages = getSortedRoutes(dynamicRoutedPages);
|
const sortedDynamicRoutedPages = getSortedRoutes(dynamicRoutedPages);
|
||||||
const sortedPagesManifest = pagesManifestWithoutDynamicRoutes;
|
const sortedPagesManifest = pagesManifestWithoutDynamicRoutes;
|
||||||
|
|
||||||
sortedDynamicRoutedPages.forEach((route) => {
|
sortedDynamicRoutedPages.forEach(
|
||||||
sortedPagesManifest[route] = pagesManifest[route];
|
(route) => (sortedPagesManifest[route] = pagesManifest[route])
|
||||||
});
|
);
|
||||||
|
|
||||||
return sortedPagesManifest;
|
return sortedPagesManifest;
|
||||||
}
|
}
|
||||||
|
|
@ -118,18 +118,20 @@ class Builder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildDefaultLambda(buildManifest: OriginRequestDefaultHandlerManifest): Promise<void[]> {
|
async buildRequestLambda(buildManifest: OriginRequestHandlerManifest): Promise<void[]> {
|
||||||
const ignoreAppAndDocumentPages = (page: string): boolean => {
|
const ignoreAppAndDocumentPages = (page: string): boolean => {
|
||||||
const pageBasename = basename(page);
|
const pageBasename = basename(page);
|
||||||
return pageBasename !== '_app.js' && pageBasename !== '_document.js';
|
return pageBasename !== '_app.js' && pageBasename !== '_document.js';
|
||||||
};
|
};
|
||||||
|
|
||||||
const allSsrPages = [
|
const allServerProcessablePages = [
|
||||||
...Object.values(buildManifest.pages.ssr.nonDynamic),
|
...Object.values(buildManifest.pages.ssr.nonDynamic),
|
||||||
...Object.values(buildManifest.pages.ssr.dynamic).map((entry) => entry.file),
|
...Object.values(buildManifest.pages.ssr.dynamic).map((entry) => entry.file),
|
||||||
|
...Object.values(buildManifest.pages.apis.nonDynamic),
|
||||||
|
...Object.values(buildManifest.pages.apis.dynamic).map((entry) => entry.file),
|
||||||
].filter(ignoreAppAndDocumentPages);
|
].filter(ignoreAppAndDocumentPages);
|
||||||
|
|
||||||
const ssrPages = Object.values(allSsrPages).map((pageFile) =>
|
const ssrPages = Object.values(allServerProcessablePages).map((pageFile) =>
|
||||||
join(this.serverlessDir, pageFile)
|
join(this.serverlessDir, pageFile)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -140,81 +142,38 @@ class Builder {
|
||||||
const copyTraces = this.copyLambdaHandlerDependencies(
|
const copyTraces = this.copyLambdaHandlerDependencies(
|
||||||
fileList,
|
fileList,
|
||||||
reasons,
|
reasons,
|
||||||
DEFAULT_LAMBDA_CODE_DIR
|
REQUEST_LAMBDA_CODE_DIR
|
||||||
);
|
);
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
...copyTraces,
|
...copyTraces,
|
||||||
copy(
|
copy(
|
||||||
require.resolve('@next-deploy/aws-lambda-builder/dist/default-handler.js'),
|
require.resolve('@next-deploy/aws-lambda-builder/dist/request-handler.js'),
|
||||||
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, 'index.js')
|
join(this.outputDir, REQUEST_LAMBDA_CODE_DIR, 'index.js')
|
||||||
),
|
),
|
||||||
copy(
|
copy(
|
||||||
require.resolve('@next-deploy/aws-lambda-builder/dist/compat.js'),
|
require.resolve('@next-deploy/aws-lambda-builder/dist/compat.js'),
|
||||||
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, 'compat.js')
|
join(this.outputDir, REQUEST_LAMBDA_CODE_DIR, 'compat.js')
|
||||||
),
|
),
|
||||||
writeJson(join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, 'manifest.json'), buildManifest),
|
writeJson(join(this.outputDir, REQUEST_LAMBDA_CODE_DIR, 'manifest.json'), buildManifest),
|
||||||
copy(
|
copy(
|
||||||
join(this.serverlessDir, 'pages'),
|
join(this.serverlessDir, 'pages'),
|
||||||
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, 'pages'),
|
join(this.outputDir, REQUEST_LAMBDA_CODE_DIR, 'pages'),
|
||||||
{
|
{
|
||||||
filter: (file: string) => {
|
filter: (file: string) => extname(file) !== '.html' && extname(file) !== '.json',
|
||||||
const isNotPrerenderedHTMLPage = extname(file) !== '.html';
|
|
||||||
const isNotStaticPropsJSONFile = extname(file) !== '.json';
|
|
||||||
const isNotApiPage = pathToPosix(file).indexOf('pages/api') === -1;
|
|
||||||
|
|
||||||
return isNotApiPage && isNotPrerenderedHTMLPage && isNotStaticPropsJSONFile;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
copy(
|
copy(
|
||||||
join(this.dotNextDir, 'prerender-manifest.json'),
|
join(this.dotNextDir, 'prerender-manifest.json'),
|
||||||
join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR, 'prerender-manifest.json')
|
join(this.outputDir, REQUEST_LAMBDA_CODE_DIR, 'prerender-manifest.json')
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildApiLambda(apiBuildManifest: OriginRequestApiHandlerManifest): Promise<void[]> {
|
async prepareBuildManifest(): Promise<OriginRequestHandlerManifest> {
|
||||||
const allApiPages = [
|
|
||||||
...Object.values(apiBuildManifest.apis.nonDynamic),
|
|
||||||
...Object.values(apiBuildManifest.apis.dynamic).map((entry) => entry.file),
|
|
||||||
];
|
|
||||||
|
|
||||||
const apiPages = Object.values(allApiPages).map((pageFile) =>
|
|
||||||
join(this.serverlessDir, pageFile)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { fileList, reasons } = await nodeFileTrace(apiPages, {
|
|
||||||
base: process.cwd(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const copyTraces = this.copyLambdaHandlerDependencies(fileList, reasons, API_LAMBDA_CODE_DIR);
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
...copyTraces,
|
|
||||||
copy(
|
|
||||||
require.resolve('@next-deploy/aws-lambda-builder/dist/api-handler.js'),
|
|
||||||
join(this.outputDir, API_LAMBDA_CODE_DIR, 'index.js')
|
|
||||||
),
|
|
||||||
copy(
|
|
||||||
require.resolve('@next-deploy/aws-lambda-builder/dist/compat.js'),
|
|
||||||
join(this.outputDir, API_LAMBDA_CODE_DIR, 'compat.js')
|
|
||||||
),
|
|
||||||
copy(
|
|
||||||
join(this.serverlessDir, 'pages/api'),
|
|
||||||
join(this.outputDir, API_LAMBDA_CODE_DIR, 'pages/api')
|
|
||||||
),
|
|
||||||
writeJson(join(this.outputDir, API_LAMBDA_CODE_DIR, 'manifest.json'), apiBuildManifest),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async prepareBuildManifests(): Promise<{
|
|
||||||
defaultBuildManifest: OriginRequestDefaultHandlerManifest;
|
|
||||||
apiBuildManifest: OriginRequestApiHandlerManifest;
|
|
||||||
}> {
|
|
||||||
const pagesManifest = await this.readPagesManifest();
|
const pagesManifest = await this.readPagesManifest();
|
||||||
const buildId = await readFile(join(this.dotNextDir, 'BUILD_ID'), 'utf-8');
|
const buildId = await readFile(join(this.dotNextDir, 'BUILD_ID'), 'utf-8');
|
||||||
const defaultBuildManifest: OriginRequestDefaultHandlerManifest = {
|
const defaultBuildManifest: OriginRequestHandlerManifest = {
|
||||||
buildId,
|
buildId,
|
||||||
pages: {
|
pages: {
|
||||||
ssr: {
|
ssr: {
|
||||||
|
|
@ -225,19 +184,17 @@ class Builder {
|
||||||
dynamic: {},
|
dynamic: {},
|
||||||
nonDynamic: {},
|
nonDynamic: {},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
publicFiles: {},
|
|
||||||
};
|
|
||||||
const apiBuildManifest: OriginRequestApiHandlerManifest = {
|
|
||||||
apis: {
|
apis: {
|
||||||
dynamic: {},
|
dynamic: {},
|
||||||
nonDynamic: {},
|
nonDynamic: {},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
publicFiles: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ssrPages = defaultBuildManifest.pages.ssr;
|
const ssrPages = defaultBuildManifest.pages.ssr;
|
||||||
const htmlPages = defaultBuildManifest.pages.html;
|
const htmlPages = defaultBuildManifest.pages.html;
|
||||||
const apiPages = apiBuildManifest.apis;
|
const apiPages = defaultBuildManifest.pages.apis;
|
||||||
|
|
||||||
const isHtmlPage = (path: string): boolean => path.endsWith('.html');
|
const isHtmlPage = (path: string): boolean => path.endsWith('.html');
|
||||||
const isApiPage = (path: string): boolean => path.startsWith('pages/api');
|
const isApiPage = (path: string): boolean => path.startsWith('pages/api');
|
||||||
|
|
@ -281,10 +238,7 @@ class Builder {
|
||||||
|
|
||||||
publicFiles.forEach((pf) => (defaultBuildManifest.publicFiles[`/${pf}`] = pf));
|
publicFiles.forEach((pf) => (defaultBuildManifest.publicFiles[`/${pf}`] = pf));
|
||||||
|
|
||||||
return {
|
return defaultBuildManifest;
|
||||||
defaultBuildManifest,
|
|
||||||
apiBuildManifest,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanupDotNext(): Promise<void> {
|
async cleanupDotNext(): Promise<void> {
|
||||||
|
|
@ -308,8 +262,7 @@ class Builder {
|
||||||
|
|
||||||
await this.cleanupDotNext();
|
await this.cleanupDotNext();
|
||||||
|
|
||||||
await emptyDir(join(this.outputDir, DEFAULT_LAMBDA_CODE_DIR));
|
await emptyDir(join(this.outputDir, REQUEST_LAMBDA_CODE_DIR));
|
||||||
await emptyDir(join(this.outputDir, API_LAMBDA_CODE_DIR));
|
|
||||||
|
|
||||||
const { restoreUserConfig } = await createServerlessConfig(cwd, join(this.nextConfigDir));
|
const { restoreUserConfig } = await createServerlessConfig(cwd, join(this.nextConfigDir));
|
||||||
|
|
||||||
|
|
@ -337,17 +290,9 @@ class Builder {
|
||||||
await restoreUserConfig();
|
await restoreUserConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { defaultBuildManifest, apiBuildManifest } = await this.prepareBuildManifests();
|
const defaultBuildManifest = await this.prepareBuildManifest();
|
||||||
|
|
||||||
await this.buildDefaultLambda(defaultBuildManifest);
|
await this.buildRequestLambda(defaultBuildManifest);
|
||||||
|
|
||||||
const hasAPIPages =
|
|
||||||
Object.keys(apiBuildManifest.apis.nonDynamic).length > 0 ||
|
|
||||||
Object.keys(apiBuildManifest.apis.dynamic).length > 0;
|
|
||||||
|
|
||||||
if (hasAPIPages) {
|
|
||||||
await this.buildApiLambda(apiBuildManifest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ import {
|
||||||
} from 'aws-lambda';
|
} from 'aws-lambda';
|
||||||
import { PrerenderManifest as PrerenderManifestType } from 'next/dist/build/index';
|
import { PrerenderManifest as PrerenderManifestType } from 'next/dist/build/index';
|
||||||
|
|
||||||
import { OriginRequestEvent, OriginRequestDefaultHandlerManifest } from '../types';
|
import { OriginRequestEvent, OriginRequestHandlerManifest } from '../types';
|
||||||
|
|
||||||
const addS3HostHeader = (req: CloudFrontRequest, s3DomainName: string): void => {
|
const addS3HostHeader = (req: CloudFrontRequest, s3DomainName: string): void => {
|
||||||
req.headers['host'] = [{ key: 'host', value: s3DomainName }];
|
req.headers['host'] = [{ key: 'host', value: s3DomainName }];
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDataRequest = (uri: string): boolean => uri.startsWith('/_next/data');
|
const isDataRequest = (uri: string): boolean => uri.startsWith('/_next/data');
|
||||||
|
const isApiRequest = (uri: string): boolean => uri.startsWith('/api');
|
||||||
const normaliseUri = (uri: string): string => (uri === '/' ? '/index' : uri);
|
const normaliseUri = (uri: string): string => (uri === '/' ? '/index' : uri);
|
||||||
|
|
||||||
const normaliseS3OriginDomain = (s3Origin: CloudFrontS3Origin): string => {
|
const normaliseS3OriginDomain = (s3Origin: CloudFrontS3Origin): string => {
|
||||||
|
|
@ -38,14 +38,13 @@ const normaliseS3OriginDomain = (s3Origin: CloudFrontS3Origin): string => {
|
||||||
return s3Origin.domainName;
|
return s3Origin.domainName;
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = (manifest: OriginRequestDefaultHandlerManifest): ((uri: string) => string) => {
|
const router = (manifest: OriginRequestHandlerManifest): ((uri: string) => string | null) => {
|
||||||
const {
|
const {
|
||||||
pages: { ssr, html },
|
pages: { ssr, html, apis },
|
||||||
} = manifest;
|
} = manifest;
|
||||||
|
const allDynamicRoutes = { ...ssr.dynamic, ...html.dynamic, ...apis.dynamic };
|
||||||
|
|
||||||
const allDynamicRoutes = { ...ssr.dynamic, ...html.dynamic };
|
return (uri: string): string | null => {
|
||||||
|
|
||||||
return (uri: string): string => {
|
|
||||||
let normalizedUri = uri;
|
let normalizedUri = uri;
|
||||||
|
|
||||||
if (isDataRequest(uri)) {
|
if (isDataRequest(uri)) {
|
||||||
|
|
@ -67,6 +66,10 @@ const router = (manifest: OriginRequestDefaultHandlerManifest): ((uri: string) =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isApiRequest(uri)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// only use the 404 page if the project exports it
|
// only use the 404 page if the project exports it
|
||||||
if (html.nonDynamic['/404'] !== undefined) {
|
if (html.nonDynamic['/404'] !== undefined) {
|
||||||
return 'pages/404.html';
|
return 'pages/404.html';
|
||||||
|
|
@ -81,7 +84,7 @@ export const handler = async (
|
||||||
): Promise<CloudFrontResultResponse | CloudFrontRequest> => {
|
): Promise<CloudFrontResultResponse | CloudFrontRequest> => {
|
||||||
const { request } = event.Records[0].cf;
|
const { request } = event.Records[0].cf;
|
||||||
const uri = normaliseUri(request.uri);
|
const uri = normaliseUri(request.uri);
|
||||||
const manifest: OriginRequestDefaultHandlerManifest = Manifest;
|
const manifest: OriginRequestHandlerManifest = Manifest;
|
||||||
const prerenderManifest: PrerenderManifestType = PrerenderManifest;
|
const prerenderManifest: PrerenderManifestType = PrerenderManifest;
|
||||||
const { pages, publicFiles } = manifest;
|
const { pages, publicFiles } = manifest;
|
||||||
const isStaticPage = pages.html.nonDynamic[uri];
|
const isStaticPage = pages.html.nonDynamic[uri];
|
||||||
|
|
@ -108,20 +111,28 @@ export const handler = async (
|
||||||
|
|
||||||
const pagePath = router(manifest)(uri);
|
const pagePath = router(manifest)(uri);
|
||||||
|
|
||||||
|
if (!pagePath) {
|
||||||
|
return {
|
||||||
|
status: '404',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (pagePath.endsWith('.html')) {
|
if (pagePath.endsWith('.html')) {
|
||||||
s3Origin.path = '/static-pages';
|
s3Origin.path = '/static-pages';
|
||||||
request.uri = pagePath.replace('pages', '');
|
request.uri = pagePath.replace('pages', '');
|
||||||
addS3HostHeader(request, normalizedS3DomainName);
|
addS3HostHeader(request, normalizedS3DomainName);
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const page = require(`./${pagePath}`);
|
const page = require(`./${pagePath}`);
|
||||||
|
|
||||||
const { req, res, responsePromise } = lambdaAtEdgeCompat(event.Records[0].cf);
|
const { req, res, responsePromise } = lambdaAtEdgeCompat(event.Records[0].cf);
|
||||||
|
|
||||||
if (isDataRequest(uri)) {
|
if (isApiRequest(uri)) {
|
||||||
|
page.default(req, res);
|
||||||
|
} else if (isDataRequest(uri)) {
|
||||||
const { renderOpts } = await page.renderReqToHTML(req, res, 'passthrough');
|
const { renderOpts } = await page.renderReqToHTML(req, res, 'passthrough');
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.setHeader('Content-Type', 'application/json');
|
||||||
res.end(JSON.stringify(renderOpts.pageData));
|
res.end(JSON.stringify(renderOpts.pageData));
|
||||||
} else {
|
} else {
|
||||||
17
packages/aws-lambda-builder/types.d.ts
vendored
17
packages/aws-lambda-builder/types.d.ts
vendored
|
|
@ -12,16 +12,7 @@ type DynamicPageKeyValue = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type OriginRequestApiHandlerManifest = {
|
type OriginRequestHandlerManifest = {
|
||||||
apis: {
|
|
||||||
dynamic: DynamicPageKeyValue;
|
|
||||||
nonDynamic: {
|
|
||||||
[key: string]: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type OriginRequestDefaultHandlerManifest = {
|
|
||||||
buildId: string;
|
buildId: string;
|
||||||
pages: {
|
pages: {
|
||||||
ssr: {
|
ssr: {
|
||||||
|
|
@ -31,10 +22,16 @@ type OriginRequestDefaultHandlerManifest = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
html: {
|
html: {
|
||||||
|
dynamic: DynamicPageKeyValue;
|
||||||
nonDynamic: {
|
nonDynamic: {
|
||||||
[path: string]: string;
|
[path: string]: string;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
apis: {
|
||||||
dynamic: DynamicPageKeyValue;
|
dynamic: DynamicPageKeyValue;
|
||||||
|
nonDynamic: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
publicFiles: {
|
publicFiles: {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ class Context {
|
||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(reason: string, message?: string): void {
|
close(reason: string, error?: Error): void {
|
||||||
// Skip if not active
|
// Skip if not active
|
||||||
process.stdout.write(ansiEscapes.cursorShow);
|
process.stdout.write(ansiEscapes.cursorShow);
|
||||||
if (!this.isStatusEngineActive()) {
|
if (!this.isStatusEngineActive()) {
|
||||||
|
|
@ -138,7 +138,7 @@ class Context {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.statusEngineStop(reason, message);
|
return this.statusEngineStop(reason, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRelativeVerticalCursorPosition(contentString: string): number {
|
getRelativeVerticalCursorPosition(contentString: string): number {
|
||||||
|
|
@ -172,11 +172,12 @@ class Context {
|
||||||
return this.statusEngine();
|
return this.statusEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
statusEngineStop(reason: string, message?: string): void {
|
statusEngineStop(reason: string, error?: Error): void {
|
||||||
this.metrics.status.running = false;
|
this.metrics.status.running = false;
|
||||||
|
let message = '';
|
||||||
|
|
||||||
if (reason === 'error' && message) {
|
if (reason === 'error' && error) {
|
||||||
message = red(message);
|
message = red(`❌ ${error.stack || error.message}`);
|
||||||
} else if (reason === 'cancel') {
|
} else if (reason === 'cancel') {
|
||||||
message = red('Cancelled ❌');
|
message = red('Cancelled ❌');
|
||||||
} else if (reason === 'done') {
|
} else if (reason === 'done') {
|
||||||
|
|
@ -325,7 +326,7 @@ class Context {
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
// Write Error
|
// Write Error
|
||||||
console.log(`${red('error:')}`);
|
console.log(`${red('Error:')}`);
|
||||||
|
|
||||||
console.log(` `, error);
|
console.log(` `, error);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ const deploy = async (deployConfigPath: string, methodName = 'default'): Promise
|
||||||
context.close('done');
|
context.close('done');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
context.renderError(e);
|
|
||||||
context.close('error', e);
|
context.close('error', e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
const component = require('./dist/component.js');
|
module.exports = require('./dist/component.js').default;
|
||||||
|
|
||||||
module.exports = component.default;
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { GithubInputs, DeploymentResult } from '../types';
|
||||||
|
|
||||||
class GithubComponent extends Component {
|
class GithubComponent extends Component {
|
||||||
async default(inputs: GithubInputs = {}): Promise<DeploymentResult> {
|
async default(inputs: GithubInputs = {}): Promise<DeploymentResult> {
|
||||||
if (inputs.build !== false) {
|
if (inputs.buildOptions !== false) {
|
||||||
await this.build(inputs);
|
await this.build(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,17 +19,19 @@ class GithubComponent extends Component {
|
||||||
async build(inputs: GithubInputs = {}): Promise<void> {
|
async build(inputs: GithubInputs = {}): Promise<void> {
|
||||||
const nextConfigPath = inputs.nextConfigDir ? resolve(inputs.nextConfigDir) : process.cwd();
|
const nextConfigPath = inputs.nextConfigDir ? resolve(inputs.nextConfigDir) : process.cwd();
|
||||||
const buildCwd =
|
const buildCwd =
|
||||||
typeof inputs.build === 'boolean' || typeof inputs.build === 'undefined' || !inputs.build.cwd
|
typeof inputs.buildOptions === 'boolean' ||
|
||||||
|
typeof inputs.buildOptions === 'undefined' ||
|
||||||
|
!inputs.buildOptions.cwd
|
||||||
? nextConfigPath
|
? nextConfigPath
|
||||||
: resolve(inputs.build.cwd);
|
: resolve(inputs.buildOptions.cwd);
|
||||||
const buildConfig: BuildOptions = {
|
const buildConfig: BuildOptions = {
|
||||||
enabled: inputs.build
|
enabled: inputs.buildOptions
|
||||||
? // @ts-ignore
|
? // @ts-ignore
|
||||||
inputs.build !== false && inputs.build.enabled !== false
|
inputs.buildOptions !== false && inputs.buildOptions.enabled !== false
|
||||||
: true,
|
: true,
|
||||||
cmd: 'node_modules/.bin/next',
|
cmd: 'node_modules/.bin/next',
|
||||||
args: ['build'],
|
args: ['build'],
|
||||||
...(typeof inputs.build === 'object' ? inputs.build : {}),
|
...(typeof inputs.buildOptions === 'object' ? inputs.buildOptions : {}),
|
||||||
cwd: buildCwd,
|
cwd: buildCwd,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,7 +59,7 @@ class GithubComponent extends Component {
|
||||||
const publishPromise = new Promise((resolve, reject) => {
|
const publishPromise = new Promise((resolve, reject) => {
|
||||||
publish(
|
publish(
|
||||||
'out',
|
'out',
|
||||||
{ message: 'Next Deployment Update', ...inputs.publishOptions, dotfiles: true },
|
{ message: 'Next Deployment Update', dotfiles: true, ...inputs.publishOptions },
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return reject(err);
|
return reject(err);
|
||||||
|
|
|
||||||
99
yarn.lock
99
yarn.lock
|
|
@ -2206,10 +2206,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6"
|
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6"
|
||||||
integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=
|
integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>= 8", "@types/node@^14.0.22":
|
"@types/node@*", "@types/node@>= 8", "@types/node@^14.0.23":
|
||||||
version "14.0.22"
|
version "14.0.23"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.22.tgz#23ea4d88189cec7d58f9e6b66f786b215eb61bdc"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806"
|
||||||
integrity sha512-emeGcJvdiZ4Z3ohbmw93E/64jRzUHAItSHt8nF7M4TGgQTiWqFVGB8KNpLGFmUHmHLvjvBgFwVlqNcq+VuGv9g==
|
integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
|
|
@ -2305,52 +2305,52 @@
|
||||||
"@types/webpack-sources" "*"
|
"@types/webpack-sources" "*"
|
||||||
source-map "^0.6.0"
|
source-map "^0.6.0"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^3.6.0":
|
"@typescript-eslint/eslint-plugin@^3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz#ba2b6cae478b8fca3f2e58ff1313e4198eea2d8a"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.1.tgz#5ced8fd2087fbb83a76973dea4a0d39d9cb4a642"
|
||||||
integrity sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA==
|
integrity sha512-06lfjo76naNeOMDl+mWG9Fh/a0UHKLGhin+mGaIw72FUMbMGBkdi/FEJmgEDzh4eE73KIYzHWvOCYJ0ak7nrJQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/experimental-utils" "3.6.0"
|
"@typescript-eslint/experimental-utils" "3.6.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
functional-red-black-tree "^1.0.1"
|
functional-red-black-tree "^1.0.1"
|
||||||
regexpp "^3.0.0"
|
regexpp "^3.0.0"
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
"@typescript-eslint/experimental-utils@3.6.0":
|
"@typescript-eslint/experimental-utils@3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz#0138152d66e3e53a6340f606793fb257bf2d76a1"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.1.tgz#b5a2738ebbceb3fa90c5b07d50bb1225403c4a54"
|
||||||
integrity sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ==
|
integrity sha512-oS+hihzQE5M84ewXrTlVx7eTgc52eu+sVmG7ayLfOhyZmJ8Unvf3osyFQNADHP26yoThFfbxcibbO0d2FjnYhg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.3"
|
"@types/json-schema" "^7.0.3"
|
||||||
"@typescript-eslint/types" "3.6.0"
|
"@typescript-eslint/types" "3.6.1"
|
||||||
"@typescript-eslint/typescript-estree" "3.6.0"
|
"@typescript-eslint/typescript-estree" "3.6.1"
|
||||||
eslint-scope "^5.0.0"
|
eslint-scope "^5.0.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.0.0"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^3.6.0":
|
"@typescript-eslint/parser@^3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.0.tgz#79b5232e1a2d06f1fc745942b690cd87aca7b60e"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.1.tgz#216e8adf4ee9c629f77c985476a2ea07fb80e1dc"
|
||||||
integrity sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw==
|
integrity sha512-SLihQU8RMe77YJ/jGTqOt0lMq7k3hlPVfp7v/cxMnXA9T0bQYoMDfTsNgHXpwSJM1Iq2aAJ8WqekxUwGv5F67Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/eslint-visitor-keys" "^1.0.0"
|
"@types/eslint-visitor-keys" "^1.0.0"
|
||||||
"@typescript-eslint/experimental-utils" "3.6.0"
|
"@typescript-eslint/experimental-utils" "3.6.1"
|
||||||
"@typescript-eslint/types" "3.6.0"
|
"@typescript-eslint/types" "3.6.1"
|
||||||
"@typescript-eslint/typescript-estree" "3.6.0"
|
"@typescript-eslint/typescript-estree" "3.6.1"
|
||||||
eslint-visitor-keys "^1.1.0"
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
"@typescript-eslint/types@3.6.0":
|
"@typescript-eslint/types@3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.0.tgz#4bd6eee55d2f9d35a4b36c4804be1880bf68f7bc"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.1.tgz#87600fe79a1874235d3cc1cf5c7e1a12eea69eee"
|
||||||
integrity sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg==
|
integrity sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@3.6.0":
|
"@typescript-eslint/typescript-estree@3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz#9b4cab43f1192b64ff51530815b8919f166ce177"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.1.tgz#a5c91fcc5497cce7922ff86bc37d5e5891dcdefa"
|
||||||
integrity sha512-G57NDSABHjvob7zVV09ehWyD1K6/YUKjz5+AufObFyjNO4DVmKejj47MHjVHHlZZKgmpJD2yyH9lfCXHrPITFg==
|
integrity sha512-G4XRe/ZbCZkL1fy09DPN3U0mR6SayIv1zSeBNquRFRk7CnVLgkC2ZPj8llEMJg5Y8dJ3T76SvTGtceytniaztQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "3.6.0"
|
"@typescript-eslint/types" "3.6.1"
|
||||||
"@typescript-eslint/visitor-keys" "3.6.0"
|
"@typescript-eslint/visitor-keys" "3.6.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
glob "^7.1.6"
|
glob "^7.1.6"
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
@ -2358,10 +2358,10 @@
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@3.6.0":
|
"@typescript-eslint/visitor-keys@3.6.1":
|
||||||
version "3.6.0"
|
version "3.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz#44185eb0cc47651034faa95c5e2e8b314ecebb26"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.1.tgz#5c57a7772f4dd623cfeacc219303e7d46f963b37"
|
||||||
integrity sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ==
|
integrity sha512-qC8Olwz5ZyMTZrh4Wl3K4U6tfms0R/mzU4/5W3XeUZptVraGVmbptJbn6h2Ey6Rb3hOs3zWoAUebZk8t47KGiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^1.1.0"
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
|
|
@ -4597,9 +4597,9 @@ ecc-jsbn@~0.1.1:
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
electron-to-chromium@^1.3.413, electron-to-chromium@^1.3.488:
|
electron-to-chromium@^1.3.413, electron-to-chromium@^1.3.488:
|
||||||
version "1.3.496"
|
version "1.3.497"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.496.tgz#3f43d32930481d82ad3663d79658e7c59a58af0b"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.497.tgz#de00f2f2f44c258c4577fbfbd5124b94c18bfa44"
|
||||||
integrity sha512-TXY4mwoyowwi4Lsrq9vcTUYBThyc1b2hXaTZI13p8/FRhY2CTaq5lK+DVjhYkKiTLsKt569Xes+0J5JsVXFurQ==
|
integrity sha512-sPdW5bUDZwiFtoonuZCUwRGzsZmKzcLM0bMVhp6SMCfUG+B3faENLx3cE+o+K0Jl+MPuNA9s9cScyFjOlixZpQ==
|
||||||
|
|
||||||
elliptic@^6.0.0, elliptic@^6.5.2:
|
elliptic@^6.0.0, elliptic@^6.5.2:
|
||||||
version "6.5.3"
|
version "6.5.3"
|
||||||
|
|
@ -8946,9 +8946,9 @@ regexpu-core@^4.7.0:
|
||||||
unicode-match-property-value-ecmascript "^1.2.0"
|
unicode-match-property-value-ecmascript "^1.2.0"
|
||||||
|
|
||||||
registry-auth-token@^4.0.0:
|
registry-auth-token@^4.0.0:
|
||||||
version "4.1.1"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479"
|
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da"
|
||||||
integrity sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==
|
integrity sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
rc "^1.2.8"
|
rc "^1.2.8"
|
||||||
|
|
||||||
|
|
@ -10211,9 +10211,9 @@ ts-pnp@^1.1.6:
|
||||||
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
|
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
|
||||||
|
|
||||||
ts-toolbelt@^6.3.3:
|
ts-toolbelt@^6.3.3:
|
||||||
version "6.12.1"
|
version "6.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.12.1.tgz#bfcfaf0e1cc7416bcfc412c9340233f57a1f86c4"
|
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.12.2.tgz#d9007906d903b89d7bf210f43fbefe2a2bfd5734"
|
||||||
integrity sha512-Y4ZMdw+CzHnzFp7yp8axTDl6G78ypeS1XcaarLoXpnW0McYR7kDLndU2tk7nIEdM99yoHjaRSbL09ULZMtN5RA==
|
integrity sha512-Z9VWXJ32UpLrjw5OqieJ944heNN5gkVm69VLvVf9GgrdxoxPiM4ughyYFip6pIDqRnrVuiegMTD48zxnOW5j/Q==
|
||||||
|
|
||||||
tslib@^1.8.1, tslib@^1.9.0:
|
tslib@^1.8.1, tslib@^1.9.0:
|
||||||
version "1.13.0"
|
version "1.13.0"
|
||||||
|
|
@ -10641,7 +10641,14 @@ which-pm-runs@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
||||||
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
|
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
|
||||||
|
|
||||||
which@^1.2.9, which@^1.3.1, which@^2.0.1:
|
which@^1.2.9, which@^1.3.1:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
|
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||||
|
dependencies:
|
||||||
|
isexe "^2.0.0"
|
||||||
|
|
||||||
|
which@^2.0.1:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||||
|
|
|
||||||
Reference in a new issue