From 80a844cf736bc60caa0a3940881aa484d324b210 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 24 Mar 2025 19:35:51 -0400 Subject: [PATCH 1/8] add AWS logo to home page run anywhere section --- src/assets/aws.svg | 6 ++++++ src/components/run-anywhere/platforms.json | 5 +++++ src/components/run-anywhere/run-anywhere.js | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 src/assets/aws.svg diff --git a/src/assets/aws.svg b/src/assets/aws.svg new file mode 100644 index 00000000..3781796a --- /dev/null +++ b/src/assets/aws.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/components/run-anywhere/platforms.json b/src/components/run-anywhere/platforms.json index 3e246a4c..b51a4088 100644 --- a/src/components/run-anywhere/platforms.json +++ b/src/components/run-anywhere/platforms.json @@ -14,6 +14,11 @@ "icon": "nodejs", "link": "/guides/hosting/#self-hosting" }, + { + "label": "AWS", + "icon": "aws", + "link": "/guides/hosting/aws/" + }, { "label": "GitHub", "icon": "github", diff --git a/src/components/run-anywhere/run-anywhere.js b/src/components/run-anywhere/run-anywhere.js index 37f282af..e60c4bd6 100644 --- a/src/components/run-anywhere/run-anywhere.js +++ b/src/components/run-anywhere/run-anywhere.js @@ -1,4 +1,5 @@ import platforms from "./platforms.json" with { type: "json" }; +import awsLogo from "../../assets/aws.svg?type=raw"; import githubLogo from "../../assets/github.svg?type=raw"; import netlifyLogo from "../../assets/netlify.svg?type=raw"; import nodejsLogo from "../../assets/nodejs.svg?type=raw"; @@ -6,6 +7,7 @@ import vercelLogo from "../../assets/vercel.svg?type=raw"; import styles from "./run-anywhere.module.css"; const platformImageMapper = { + aws: awsLogo, github: githubLogo, netlify: netlifyLogo, nodejs: nodejsLogo, From d06e6f504096d6e80023e75b80d47f5613fd0c41 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 31 Mar 2025 18:25:09 -0400 Subject: [PATCH 2/8] initial IaC example with SST --- src/pages/guides/hosting/aws.md | 149 ++++++++++++++++++++++++++++---- 1 file changed, 134 insertions(+), 15 deletions(-) diff --git a/src/pages/guides/hosting/aws.md b/src/pages/guides/hosting/aws.md index 69cc13af..251ec69a 100644 --- a/src/pages/guides/hosting/aws.md +++ b/src/pages/guides/hosting/aws.md @@ -8,24 +8,24 @@ tocHeading: 2 # AWS -Greenwood can be automatically deployed to [**AWS**](https://aws.amazon.com/) for static hosting using [**S3**](https://aws.amazon.com/s3/) and [**Cloudfront**](https://aws.amazon.com/cloudfront/) with GitHub Actions. +Greenwood projects can be deployed to [**AWS**](https://aws.amazon.com/) for static hosting ([**S3**](https://aws.amazon.com/s3/) / [**CloudFront**](https://aws.amazon.com/cloudfront/)) and dynamic serverless hosting of SSR pages and API routes (on [**Lambda**](https://aws.amazon.com/lambda/)). Although static hosting is fairly trivial, for full-stack applications and when leveraging additional AWS services to compliment your application, we recommend leveraging [IaC (Infrastructure as Code)](https://en.wikipedia.org/wiki/Infrastructure_as_code) tools, as we will demonstrate later in this guide. > You can see a complete hybrid project example in our [demonstration repo](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws). ## Static Hosting -In this section, we'll share the steps for up S3 and Cloudfront together for static web hosting. +If you only need static hosting (SSG, SPA), then you may benefit from just a little manual configuration to set up an S3 bucket and CloudFront distribution for your project. -1. Configure S3 by following [these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html) -1. Once you have followed those steps, run `greenwood build` in your project and upload the contents of the _public/_ directory to the bucket -1. Finally, setup Cloudfront to use this bucket as an origin by [following these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html#GettingStartedCreateDistribution): - -> Keep an eye out for prompts from AWS to enable IAM rules for your function and make sure to invalidate the Cloudfront distribution between tests, since error pages / responses will get cached. +1. Configure S3 by following [this guide](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html) +1. Once you have followed those steps, run `greenwood build` in your project and upload the contents of the _public/_ directory to the bucket. +1. Finally, setup CloudFront to use the bucket as an origin by following [these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html#GettingStartedCreateDistribution) You should now be able to access your site at _http://{your-dist}.cloudfront.net/_! 🏆 Now at this point, if you have any routes like `/search/`, you'll notice they are not working unless _index.html_ is appended to the path. To enable routing (URL rewriting) for cleaner URLs, follow the _Configure Trigger_ section of [this guide](https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/) to attach the Lambda as a [**Lambda@Edge**](https://aws.amazon.com/lambda/edge/) function that will run on every incoming request. +> Keep an eye out for prompts from AWS to enable IAM rules for your function and make sure to invalidate the CloudFront distribution between tests, since error pages / responses will get cached. + Below is a sample Edge function for doing the rewrites: @@ -37,7 +37,7 @@ Below is a sample Edge function for doing the rewrites: const { request } = event.Records[0].cf; // re-write "clean" URLs to have index.html appended - // to support routing for Cloudfront <> S3 + // to support routing for CloudFront <> S3 if (request.uri.endsWith("/")) { request.uri = `${request.uri}index.html`; } @@ -50,21 +50,140 @@ Below is a sample Edge function for doing the rewrites: -> At this point, you'll probably want to use Route 53 to [put your domain in front of your Cloudfront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html). +> At this point, you'll probably want to use Route 53 to [put your domain in front of your CloudFront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html). ## Serverless -Coming soon! +If your Greenwood project has SSR pages and / or API routes that you would like to deploy to AWS Lambda functions, our recommendation is to install [our adapter plugin](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) and then add it to your _greenwood.config.js_, which at build time will generate Lambda compatible function code for all your dynamic pages and routes. + + + + + + ```js + import { greenwoodPluginAdapterAws } from "@greenwood/plugin-adapter-aws"; + + export default { + plugins: [greenwoodPluginAdapterAws()], + }; + ``` + + + + + +### Adapter Output + +Just like Greenwood has its own [standard build output](/docs/reference/appendix/#build-output), this plugin will also generate its own standard adapter output tailored for Lambda, so as to provide a consistent starting point to integrate with your preferred deployment tool of choice. + +The adapted functions will be output to a folder called _.aws-output_ with the following two folders: + +- `api/` - All API routes will be in this folder, with one folder per route +- `routes/` - All SSR pages will be in this folder, with one folder per route + +Here is an example from a directory listing perspective of what the structure of this folder looks like: + +```shell +.aws-output/ + api/ + event/ + event.js + index.js + package.json + search/ + ... + routes/ + admin/ + ... + products/ + index.js + package.json + products.route.chunk.jgsTuvlz.js + products.route.js +``` + +For **_each_** of the folders in the `api` or `routes` directories, it would be as simple as just creating a zip file for each folder / route, or just pointing your IaC tooling to those output folders, as we'll get into in the next section. + +### SST (IaC) Example + +Given the nature of AWS hosting and the plethora of related services that you can use to compliment your application, the Greenwood AWS adapter is specifically designed to output purely compatible Lambda functions, one per folder, that can be plugging into any IaC tool. (or zipped up and deployed manually, if you prefer) + +While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a very powerful tool which let's you entirely define your AWS infrastructure with TypeScript, combining as few or as many AWS service as you may need. Below is a simple + + + + + + ```ts + // 1) Configure SSR pages and API routes in API Gateway + const api = new sst.aws.ApiGatewayV2("MyApi"); + + // products page + api.route(`GET /routes/products`, { + bundle: `.aws-output/routes/products`, + handler: "index.handler", + }); + + // search API + api.route(`GET /api/search`, { + bundle: `.aws-output/api/search`, + handler: "index.handler", + }) + + // 2) Setup hosting for static content + const frontend = new sst.aws.StaticSite("MyStaticSite", { + path: "./", + build: { + output: "public" + }, + }) + + // 3) Configure CloudFront router with SSR pages, API routes, and static content + const router = new sst.aws.Router("MyRouter", { + routes: { + "/api/*": api.url, + { + url: api.url, + rewrite: { + regex: `^/products/$`, + to: `/routes/products` + } + }, + "/*": frontend.url + }, + invalidation: true, + }); + + // 4) Configure SST app + export default $config({ + app(input) { + return { + name: "my-app", + removal: input?.stage === "production" ? "retain" : "remove", + protect: ["production"].includes(input?.stage), + home: "aws", + }; + }, + async run() { + router + }, + }); + ``` + + + + + +Although the above example is hardcoded, you'll want use the build output manifest from Greenwood by following the [complete example repo we have](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws) for deploying a full-stack Greenwood application. -> There is no adapter plugin yet for serverless hosting, though it is on [our roadmap](https://github.com/ProjectEvergreen/greenwood/issues/1142). +> We also have an [**Architect**](https://arc.codes/) example [for reference](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws/tree/feature/arc-adapter) as well. ## GitHub Actions -If you're using GitHub, you can use GitHub Actions to automate the pushing of build files on commits to a GitHub repo. This action will also invalidate your Cloudfront cache on each publish. +If you're using GitHub, you can use GitHub Actions to automate the pushing of build files on commits to a GitHub repo. This can help automate the uploading of your static assets, or in the case of IaC, running your preferred IaC tool to deploy your application for you. -1. In your AWS account, create and / or add an AWS Secret Access Key (`AWS_SECRET_ACCESS_KEY`) and Access Key ID (`AWS_SECRET_ACCESS_KEY_ID`) and add them to your repository as [GitHub secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions). -1. We also recommend adding your bucket name as secret too, e.g. `AWS_BUCKET_NAME` -1. At the root of your repo add a GitHub Action called _.github/workflows/publish.yml_ and adapt as needed for your own branch, build commands, and package manager. +1. In your AWS account, create (or use) an AWS Secret Access Key (`AWS_SECRET_ACCESS_KEY`) and Access Key ID (`AWS_SECRET_ACCESS_KEY_ID`) and add them to your repository as [GitHub secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions). +1. At the root of your repo add a GitHub Action called _.github/workflows/publish.yml_ and adapt as needed for your own branch, build commands, package manager, and tooling. From c352b9ddfb05ea93f64faec9b6d771529d38f95b Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 31 Mar 2025 18:31:28 -0400 Subject: [PATCH 3/8] add AWS plugin to plugins landing page --- src/pages/docs/plugins/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/docs/plugins/index.md b/src/pages/docs/plugins/index.md index 5a87ce2b..13d87ea4 100644 --- a/src/pages/docs/plugins/index.md +++ b/src/pages/docs/plugins/index.md @@ -14,7 +14,7 @@ tocHeading: 2 - [Lit SSR](/docs/plugins/lit-ssr/) - For Lit users, a custom renderer plugin to support Lit+SSR - [PostCSS](/docs/plugins/postcss/) - Leverage PostCSS plugins, like [Tailwind](/guides/ecosystem/tailwind/) -- [CSS Modules](/docs/plugins/css-modules/) - Support for [CSS Modules](https://github.com/css-modules/css-modules) ™️ syntax +- [CSS Modules](/docs/plugins/css-modules/) - Support for [CSS Modules ™](https://github.com/css-modules/css-modules) syntax - [Raw Loader](/docs/plugins/raw/) - Import arbitrary text files as ESM ## All Plugins @@ -25,6 +25,7 @@ Below is the official list of supported first-party plugins available by the Gre | Name | Description | | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | --- | +| [AWS](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) | Deploy SSR pages and API routes to serverless functions on [**AWS**](https://aws.amazon.com/). | | [Babel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-babel) | Use [**Babel**](https://babeljs.io/) plugins, presets, and configuration in your project. | | [HTML Include](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-include-html) | Inspired by the original [HTML Imports spec](https://www.html5rocks.com/en/tutorials/webcomponents/imports/). | | [Import Raw](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-import-raw) | Enables usage of ESM syntax for loading arbitrary file contents as a string. | From cc4f6824307143f679cd458711e32d2f8e937819 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 12 Apr 2025 13:30:23 -0400 Subject: [PATCH 4/8] final draft and tweaks --- src/pages/guides/hosting/aws.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/pages/guides/hosting/aws.md b/src/pages/guides/hosting/aws.md index 251ec69a..a9abaeca 100644 --- a/src/pages/guides/hosting/aws.md +++ b/src/pages/guides/hosting/aws.md @@ -8,7 +8,7 @@ tocHeading: 2 # AWS -Greenwood projects can be deployed to [**AWS**](https://aws.amazon.com/) for static hosting ([**S3**](https://aws.amazon.com/s3/) / [**CloudFront**](https://aws.amazon.com/cloudfront/)) and dynamic serverless hosting of SSR pages and API routes (on [**Lambda**](https://aws.amazon.com/lambda/)). Although static hosting is fairly trivial, for full-stack applications and when leveraging additional AWS services to compliment your application, we recommend leveraging [IaC (Infrastructure as Code)](https://en.wikipedia.org/wiki/Infrastructure_as_code) tools, as we will demonstrate later in this guide. +Greenwood projects can be deployed to [**AWS**](https://aws.amazon.com/) for static hosting ([**S3**](https://aws.amazon.com/s3/) / [**CloudFront**](https://aws.amazon.com/cloudfront/)) and dynamic serverless hosting of SSR pages and API routes ([**Lambda**](https://aws.amazon.com/lambda/)). Although static hosting is fairly simple, for full-stack applications and when leveraging additional AWS services to compliment your application, we recommend leveraging [IaC (Infrastructure as Code)](https://en.wikipedia.org/wiki/Infrastructure_as_code) tools, as we will demonstrate later in this guide. > You can see a complete hybrid project example in our [demonstration repo](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws). @@ -50,11 +50,11 @@ Below is a sample Edge function for doing the rewrites: -> At this point, you'll probably want to use Route 53 to [put your domain in front of your CloudFront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html). +> At this point, you'll probably want to use Route 53 to [put a domain in front of your CloudFront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html). ## Serverless -If your Greenwood project has SSR pages and / or API routes that you would like to deploy to AWS Lambda functions, our recommendation is to install [our adapter plugin](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) and then add it to your _greenwood.config.js_, which at build time will generate Lambda compatible function code for all your dynamic pages and routes. +If your Greenwood project has SSR pages and / or API routes that you would like to deploy as AWS Lambda functions, our recommendation is to install [our AWS adapter plugin](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) and then add it to your _greenwood.config.js_. At build time it will generate Lambda compatible function code for all your dynamic pages and routes. @@ -78,10 +78,10 @@ Just like Greenwood has its own [standard build output](/docs/reference/appendix The adapted functions will be output to a folder called _.aws-output_ with the following two folders: -- `api/` - All API routes will be in this folder, with one folder per route -- `routes/` - All SSR pages will be in this folder, with one folder per route +- _api/_ - All API routes will be in this folder, with one folder per endpoint +- _routes/_ - All SSR pages will be in this folder, with one folder per route -Here is an example from a directory listing perspective of what the structure of this folder looks like: +Here is an example directory listing of what the structure of this folder might look like: ```shell .aws-output/ @@ -102,20 +102,22 @@ Here is an example from a directory listing perspective of what the structure of products.route.js ``` -For **_each_** of the folders in the `api` or `routes` directories, it would be as simple as just creating a zip file for each folder / route, or just pointing your IaC tooling to those output folders, as we'll get into in the next section. +For **_each_** of the folders in the _api/_ or _routes/_ directories, it would be as simple as just creating a zip file for each folder / route and uploading them, or just pointing your IaC tooling to those output folders, as we'll get into in the next section. -### SST (IaC) Example +### IaC Example (SST) Given the nature of AWS hosting and the plethora of related services that you can use to compliment your application, the Greenwood AWS adapter is specifically designed to output purely compatible Lambda functions, one per folder, that can be plugging into any IaC tool. (or zipped up and deployed manually, if you prefer) -While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a very powerful tool which let's you entirely define your AWS infrastructure with TypeScript, combining as few or as many AWS service as you may need. Below is a simple +While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a very powerful option which let's you entirely define your AWS infrastructure programmatically with TypeScript, combining as few or as many AWS service as you may need. + +Let's look at the below example: ```ts - // 1) Configure SSR pages and API routes in API Gateway + // 1) Configure an API Gateway for routing SSR pages and API routes const api = new sst.aws.ApiGatewayV2("MyApi"); // products page @@ -138,7 +140,7 @@ While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a v }, }) - // 3) Configure CloudFront router with SSR pages, API routes, and static content + // 3) Configure a CloudFront distribution with behaviors for SSR pages, API routes, and static content const router = new sst.aws.Router("MyRouter", { routes: { "/api/*": api.url, @@ -154,7 +156,7 @@ While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a v invalidation: true, }); - // 4) Configure SST app + // 4) Configure the SST app export default $config({ app(input) { return { @@ -174,7 +176,7 @@ While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a v -Although the above example is hardcoded, you'll want use the build output manifest from Greenwood by following the [complete example repo we have](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws) for deploying a full-stack Greenwood application. +Although the above example is hardcoded, you'll want to use the build output manifest from Greenwood by following the [complete example repo we have](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws) for deploying a full-stack Greenwood application. > We also have an [**Architect**](https://arc.codes/) example [for reference](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws/tree/feature/arc-adapter) as well. @@ -217,6 +219,7 @@ If you're using GitHub, you can use GitHub Actions to automate the pushing of bu run: | npm run build + # or run your IaC tool for adapter based builds - name: Upload to S3 and invalidate CDN uses: opspresso/action-s3-sync@master env: @@ -233,4 +236,4 @@ If you're using GitHub, you can use GitHub Actions to automate the pushing of bu -Now when you push changes to your repo, the action will run an the build files will automatically be uploaded. +Now when you push changes to your repo, the action will run and your build will automatically be deployed to your AWS account. From d460536e6cb5089e1dd36c46663453ba587433b8 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 12 Apr 2025 22:19:02 -0400 Subject: [PATCH 5/8] fix formatting From 398f3ce6f4e0f51dacc6ab1ae67cc6bec0214442 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 12 Apr 2025 22:19:15 -0400 Subject: [PATCH 6/8] fix formatting From e72baac9de5509bba28c09c3848fb21d104b1af2 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 12 Apr 2025 22:26:08 -0400 Subject: [PATCH 7/8] fix formatting --- src/pages/docs/plugins/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/docs/plugins/index.md b/src/pages/docs/plugins/index.md index 13d87ea4..9ccf0169 100644 --- a/src/pages/docs/plugins/index.md +++ b/src/pages/docs/plugins/index.md @@ -34,5 +34,5 @@ Below is the official list of supported first-party plugins available by the Gre | [Netlify](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-netlify) | Deploy serverless and edge functions to [**Netlify**](https://www.netlify.com/). | | [Polyfills](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-polyfills) | Web Component related polyfills for older browsers. | | [PostCSS](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-postcss) | Allows usage of [**PostCSS**](https://postcss.org/) plugins and configuration in your project. | -| [Puppeteer](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-renderer-puppeteer) | A rendering plugin to support prerendering a Greenwood project using Puppeteer. | | -| [Vercel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-vercel) | Deploy serverless and edge functions with [**Vercel**](https://vercel.com/). | +| [Puppeteer](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-renderer-puppeteer) | A rendering plugin to support prerendering a Greenwood project using Puppeteer. | +| [Vercel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-vercel) | Deploy serverless and edge functions with [**Vercel**](https://vercel.com/). | | From 4113d5ecd8dfdf7ecd2ad8f4bfc2ccddb5f1e981 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 12 Apr 2025 22:31:52 -0400 Subject: [PATCH 8/8] fix formatting --- src/pages/docs/plugins/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/docs/plugins/index.md b/src/pages/docs/plugins/index.md index 9ccf0169..1043ec89 100644 --- a/src/pages/docs/plugins/index.md +++ b/src/pages/docs/plugins/index.md @@ -24,7 +24,7 @@ Below is the official list of supported first-party plugins available by the Gre
| Name | Description | -| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | --- | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | [AWS](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) | Deploy SSR pages and API routes to serverless functions on [**AWS**](https://aws.amazon.com/). | | [Babel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-babel) | Use [**Babel**](https://babeljs.io/) plugins, presets, and configuration in your project. | | [HTML Include](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-include-html) | Inspired by the original [HTML Imports spec](https://www.html5rocks.com/en/tutorials/webcomponents/imports/). | @@ -35,4 +35,4 @@ Below is the official list of supported first-party plugins available by the Gre | [Polyfills](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-polyfills) | Web Component related polyfills for older browsers. | | [PostCSS](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-postcss) | Allows usage of [**PostCSS**](https://postcss.org/) plugins and configuration in your project. | | [Puppeteer](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-renderer-puppeteer) | A rendering plugin to support prerendering a Greenwood project using Puppeteer. | -| [Vercel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-vercel) | Deploy serverless and edge functions with [**Vercel**](https://vercel.com/). | | +| [Vercel](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-vercel) | Deploy serverless and edge functions with [**Vercel**](https://vercel.com/). |