├── .gitignore ├── .nvmrc ├── README.md ├── eleventy.config.js ├── netlify.toml ├── netlify └── functions │ ├── dynamic │ └── index.js │ └── serverless │ └── index.js ├── package.json └── src ├── _data └── globaldata.js ├── _includes ├── include.njk └── layout.njk ├── other-build-template.md ├── sample-nunjucks.11tydata.js └── sample-nunjucks.njk /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | _site 4 | .cache 5 | netlify/functions/serverless/* 6 | !netlify/functions/serverless/index.js 7 | netlify/functions/dynamic/* 8 | !netlify/functions/dynamic/index.js -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eleventy Serverless Demo 2 | 3 | Running Eleventy inside of a Netlify serverless function. 4 | 5 | _[Read the documentation on 11ty.dev](https://www.11ty.dev/docs/plugins/serverless/)_ 6 | 7 | ## Run it 8 | 9 | ### Locally 10 | 11 | 1. Run `npm install` 12 | 1. Run `npm start` 13 | 1. Navigate to the demo URL at `http://localhost:8080/` 14 | 15 | ### Production 16 | 17 | 1. [View the demo on Netlify](https://demo-eleventy-serverless.netlify.app) 18 | 1. [Deploy your own to Netlify](https://app.netlify.com/start/deploy?repository=https://github.com/11ty/demo-eleventy-serverless) 19 | 20 | ## How it works 21 | 22 | _[Read the documentation on 11ty.dev](https://www.11ty.dev/docs/plugins/serverless/)_ 23 | 24 | _This requires Eleventy 1.0 or newer._ 25 | 26 | 1. Use Eleventy as normal. 27 | - In this demo `src` is the input directory. 28 | - For this demo we include one Nunjucks template (`./src/sample-nunjucks.njk`), a Global Data file, an include template, and an Eleventy layout. 29 | - To make any template file into a serverless template, modify your `permalink` object to include a `serverless` key. 30 | 31 | ``` 32 | --- 33 | permalink: 34 | build: "/" 35 | serverless: "/:slug/" 36 | --- 37 | ``` 38 | 39 | This makes `eleventy.path.slug` (the `slug` name matches `:slug`) available in global data for use in your serverless templates. 40 | 41 | 2. Add the bundler plugin to your Eleventy configuration file (probably `.eleventy.js`). The name you pass into the plugin (we use `serverless` in this example) should match the key inside of your template’s `permalink` object (`permalink.serverless`). 42 | 43 | ```js 44 | const { EleventyServerlessBundlerPlugin } = require("@11ty/eleventy"); 45 | 46 | module.exports = function(eleventyConfig) { 47 | eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, { 48 | name: "serverless", 49 | functionsDir: "./netlify/functions/", 50 | }); 51 | }; 52 | ``` 53 | 54 | 3. `./netlify/functions/serverless/index.js` is the boilerplate serverless function code. You’ll need to create this yourself. 55 | 56 | ```js 57 | const { EleventyServerless } = require("@11ty/eleventy"); 58 | const { builder } = require("@netlify/functions"); 59 | 60 | // For the bundler (auto-generated by the plugin) 61 | require("./eleventy-bundler-modules.js"); 62 | 63 | async function handler (event) { 64 | let elev = new EleventyServerless("serverless", event.path, { 65 | inputDir: "src", 66 | functionsDir: "netlify/functions/", 67 | query: event.queryStringParameters, 68 | }); 69 | 70 | try { 71 | return { 72 | statusCode: 200, 73 | headers: { 74 | "Content-Type": "text/html; charset=UTF-8" 75 | }, 76 | body: await elev.render() 77 | }; 78 | } catch (error) { 79 | return { 80 | statusCode: error.httpStatusCode || 500, 81 | body: JSON.stringify({ 82 | error: error.message 83 | }) 84 | }; 85 | } 86 | } 87 | 88 | // Netlify On-demand Builder (runs on first request only) 89 | exports.handler = builder(handler); 90 | ``` 91 | 92 | 4. Add entries to your `.gitignore` file so the bundles aren’t checked into your repository. 93 | 94 | ``` 95 | netlify/functions/serverless/** 96 | !netlify/functions/serverless/index.js 97 | ``` -------------------------------------------------------------------------------- /eleventy.config.js: -------------------------------------------------------------------------------- 1 | const { EleventyServerlessBundlerPlugin } = require("@11ty/eleventy"); 2 | const fs = require("fs"); 3 | 4 | module.exports = function(eleventyConfig) { 5 | // Render on first-request 6 | eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, { 7 | name: "serverless", 8 | functionsDir: "./netlify/functions/", 9 | redirects: "netlify-toml-builders", 10 | }); 11 | 12 | // Fully dynamic template for comparison 13 | eleventyConfig.addPlugin(EleventyServerlessBundlerPlugin, { 14 | name: "dynamic", 15 | functionsDir: "./netlify/functions/", 16 | redirects: "netlify-toml-functions", 17 | }); 18 | 19 | // testing 20 | eleventyConfig.addFilter("dateDebug", inputPath => { 21 | return fs.statSync(inputPath); 22 | }) 23 | 24 | return { 25 | dir: { 26 | input: "src" 27 | } 28 | }; 29 | }; -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "_site" 3 | command = "npm run build-production" 4 | 5 | [[redirects]] 6 | from = "/:slug/" 7 | to = "/.netlify/builders/serverless" 8 | status = 200 9 | force = true 10 | _generated_by_eleventy_serverless = "serverless" 11 | 12 | [[redirects]] 13 | from = "/:slug/dynamic/" 14 | to = "/.netlify/functions/dynamic" 15 | status = 200 16 | force = true 17 | _generated_by_eleventy_serverless = "dynamic" 18 | -------------------------------------------------------------------------------- /netlify/functions/dynamic/index.js: -------------------------------------------------------------------------------- 1 | const { EleventyServerless } = require("@11ty/eleventy"); 2 | 3 | // Explicit dependencies for the bundler from config file and global data. 4 | // The file is generated by the Eleventy Serverless Bundler Plugin. 5 | require("./eleventy-bundler-modules.js"); 6 | 7 | async function handler(event) { 8 | let elev = new EleventyServerless("dynamic", { 9 | path: new URL(event.rawUrl).pathname, 10 | query: event.multiValueQueryStringParameters || event.queryStringParameters, 11 | functionsDir: "./netlify/functions/", 12 | }); 13 | 14 | try { 15 | let [page] = await elev.getOutput(); 16 | 17 | // If you want some of the data cascade available in `page.data`, use `eleventyConfig.dataFilterSelectors`. 18 | // Read more: https://www.11ty.dev/docs/config/#data-filter-selectors 19 | 20 | return { 21 | statusCode: 200, 22 | headers: { 23 | "Content-Type": "text/html; charset=UTF-8", 24 | }, 25 | body: page.content, 26 | }; 27 | } catch (error) { 28 | // Only console log for matching serverless paths 29 | // (otherwise you’ll see a bunch of BrowserSync 404s for non-dynamic URLs during --serve) 30 | if (elev.isServerlessUrl(event.path)) { 31 | console.log("Serverless Error:", error); 32 | } 33 | 34 | return { 35 | statusCode: error.httpStatusCode || 500, 36 | body: JSON.stringify( 37 | { 38 | error: error.message, 39 | }, 40 | null, 41 | 2 42 | ), 43 | }; 44 | } 45 | } 46 | 47 | // Choose one: 48 | // * Runs on each request: AWS Lambda, Netlify Function 49 | // * Runs on first request only: Netlify On-demand Builder 50 | // 1. Don’t forget to `npm install @netlify/functions` 51 | // 2. Also use `redirects: "netlify-toml-builders"` in your config file’s serverless bundler options: 52 | // https://www.11ty.dev/docs/plugins/serverless/#bundler-options 53 | 54 | exports.handler = handler; 55 | 56 | //const { builder } = require("@netlify/functions"); 57 | //exports.handler = builder(handler); 58 | -------------------------------------------------------------------------------- /netlify/functions/serverless/index.js: -------------------------------------------------------------------------------- 1 | const { EleventyServerless } = require("@11ty/eleventy"); 2 | 3 | // Explicit dependencies for the bundler from config file and global data. 4 | // The file is generated by the Eleventy Serverless Bundler Plugin. 5 | require("./eleventy-bundler-modules.js"); 6 | 7 | async function handler(event) { 8 | let elev = new EleventyServerless("serverless", { 9 | path: new URL(event.rawUrl).pathname, 10 | singleTemplateScope: false, 11 | query: event.multiValueQueryStringParameters || event.queryStringParameters, 12 | functionsDir: "./netlify/functions/", 13 | }); 14 | 15 | try { 16 | let [page] = await elev.getOutput(); 17 | 18 | // If you want some of the data cascade available in `page.data`, use `eleventyConfig.dataFilterSelectors`. 19 | // Read more: https://www.11ty.dev/docs/config/#data-filter-selectors 20 | 21 | return { 22 | statusCode: 200, 23 | headers: { 24 | "Content-Type": "text/html; charset=UTF-8", 25 | }, 26 | body: page.content, 27 | }; 28 | } catch (error) { 29 | // Only console log for matching serverless paths 30 | // (otherwise you’ll see a bunch of BrowserSync 404s for non-dynamic URLs during --serve) 31 | if (elev.isServerlessUrl(event.path)) { 32 | console.log("Serverless Error:", error); 33 | } 34 | 35 | return { 36 | statusCode: error.httpStatusCode || 500, 37 | body: JSON.stringify( 38 | { 39 | error: error.message, 40 | }, 41 | null, 42 | 2 43 | ), 44 | }; 45 | } 46 | } 47 | 48 | // Choose one: 49 | // * Runs on each request: AWS Lambda, Netlify Function 50 | // * Runs on first request only: Netlify On-demand Builder 51 | // 1. Don’t forget to `npm install @netlify/functions` 52 | // 2. Also use `redirects: "netlify-toml-builders"` in your config file’s serverless bundler options: 53 | // https://www.11ty.dev/docs/plugins/serverless/#bundler-options 54 | 55 | // exports.handler = handler; 56 | 57 | const { builder } = require("@netlify/functions"); 58 | exports.handler = builder(handler); 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eleventy-cloud", 3 | "version": "1.0.0", 4 | "description": "Render some of your Eleventy templates in a serverless function.", 5 | "scripts": { 6 | "build": "npx @11ty/eleventy", 7 | "start": "npx @11ty/eleventy --serve", 8 | "build-production": "npm run build", 9 | "clean": "rm -rf _site && rm -rf netlify/functions/*" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@11ty/eleventy": "^2.0.1", 16 | "@netlify/functions": "^1.4.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/_data/globaldata.js: -------------------------------------------------------------------------------- 1 | module.exports = "I am Global Data"; -------------------------------------------------------------------------------- /src/_includes/include.njk: -------------------------------------------------------------------------------- 1 | I am an included template. -------------------------------------------------------------------------------- /src/_includes/layout.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |{{ localdata }}
22 |{{ hi }}
23 |{{ globaldata }}
24 |{% include "include.njk" %}
25 | 26 |new Date()
{{ now }}28 | 29 |
page
Data:{{ page | dump(2) }}31 | 32 |
eleventy
Data:{{ eleventy | dump(2) }}34 | 35 |
collections
{{ name }}
: {{ size }} file{% if size != 1 %}s{% endif %}Read more about collections in serverless.
45 | 46 |{{ page.inputPath | dateDebug | dump(2) }}49 |