";
67 |
68 | browserSync.addMiddleware("*", (_, res) => {
69 | // Provides the 404 content without redirect.
70 | res.write(content_404);
71 | res.end();
72 | });
73 | }
74 | },
75 | // scripts in body conflict with Turbolinks
76 | snippetOptions: {
77 | rule: {
78 | match: /<\/head>/i,
79 | fn: function(snippet, match) {
80 | return snippet + match;
81 | }
82 | }
83 | }
84 | });
85 |
86 | return {
87 | dir: { input: "site", output: "dist", data: "_data", includes: "includes" },
88 | passthroughFileCopy: true,
89 | templateFormats: ["njk", "md", "css", "html", "yml"],
90 | htmlTemplateEngine: "njk"
91 | };
92 | };
93 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | .netlify/
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Scott Watermasysk @scottw
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Eleventy Origin
2 |
3 | 
4 |
5 | Origin is an opinionated starter template for [Eleventy](https://www.11ty.io). It was assembled using many of the tools and libraries I use often on Rails projects.
6 |
7 | ## Features
8 |
9 | It is preconfigured with the following:
10 |
11 | - [Webpack](https://webpack.js.org/) - for managing all of the assets
12 | - [Tailwind](https://tailwindcss.com/) - baked in for utility-first CSS
13 | - [Tailwind Forms](https://tailwindcss-custom-forms.netlify.com/) - a better starting point for form elements that looks good out of the box.
14 | - [Stimulus](https://stimulusjs.org/) - as a lightweight javascript framework
15 | - [PurgeCSS](https://www.purgecss.com/) - removes all unused CSS classes
16 | - [Turbolinks](https://github.com/turbolinks/turbolinks) - used to make navigating from page to page more efficient. No need to host a router/etc.
17 | - [Syntax Highlighting](https://github.com/11ty/eleventy-plugin-syntaxhighlight) - preconfigured syntax highlights
18 | - SEO - Under src/utils/seo.js is the basic starting point for a SEO plugin (similar to Jekyll SEO). It pulls data from the site.json file, but can be overridden with a seo.json file when want settings for bots.
19 | - Excerpt short code - extract an excerpt from your frontmatter or document body
20 | - Easily deploy to Netlify & Now
21 |
22 | ## UI
23 |
24 | There is no true default template. However, the default layout is configured to have both a sticky header and footer.
25 |
26 | ## Setup
27 |
28 | 1. `npm install`
29 |
30 | ## Directory Structure
31 |
32 | ### `src`
33 |
34 | All of the CSS, JS and images are stored in the `src` directory, which is
35 | managed by Webpack.
36 |
37 | - controllers - any stimulus controllers will automatically be picked up from the folder
38 | - images - all of your site's images. These will be copied to dist/assets/images when you build
39 | - styles
40 | - styles.scss - imports all other style sheets & sets up Tailwind CSS
41 | - main.scss - some minor styles to provide basic margins for markdown content.
42 | - sytax.css - the default CSS for code
43 | - templates - for now, a single template which contains the JavaScript and CSS packaged by webpack.
44 | - utils - JavaScript used to help build the site (such as the SEO custom tag)
45 |
46 | ### `site`
47 |
48 | All content and templates in in the `site` directory. This is managed and processed by Eleventy.
49 |
50 | ### `dist`
51 |
52 | Both Webpack and Eleventy push content and assets here.
53 |
54 | ## Webpack and Eleventy
55 |
56 | Webpack generates a main.js file and main.css file. Both saved to a file called `site/layouts/pack.njk`. This file ignored in Git and based on the template src/templates/pack.html.
57 |
58 | ## Usage
59 |
60 | ### Development
61 |
62 | You need to have both Webpack and Eleventy running.
63 |
64 | `npm run dev`
65 |
66 | _The first time you run this on a clean `dist` folder you may see an error about a missing pack.njk file. There is a bit of a chicken and egg. This file is generated by webpack (with hashed file names in production) may not exist when both are running concurrently. Longer term, I think we can drop this necessity by using Netlify build plugins to add hashes to assets._
67 |
68 | You can also run them separately:
69 |
70 | 1. `npm run package` (I recommend starting this one first)
71 | 2. `npm run serve`
72 |
73 | ### Production
74 |
75 | This starter is also preconfigured to be easily deployable to Netlify and Now. If you need to deploy somewhere else:
76 |
77 | 1. `npm run build`
78 | 2. Point your webserver and/or deploy/etc the `/dist` folder.
79 |
80 | ## Prior Art
81 |
82 | - [Eleventy Base Blog](https://github.com/11ty/eleventy-base-blog) - good starting point. Borrowed lots from here.
83 | - [Jekyll-fun](https://github.com/joeybeninghove/jekyll-fun) - the core workflow (especially Webpack) is based off of Joey's original project.
84 | - [Skeleventy](https://skeleventy.netlify.com/) - A good boilerplate for Eleventy and Tailwind. Having something simple to refer back to was a big help.
85 | - [Deventy](https://github.com/ianrose/deventy) - A minimal 11ty starting point for building static websites with modern tools.
86 |
87 | ## Thanks
88 |
89 | Thanks to everyone who contributes to Eleventy, the numerous packages it depends on.
90 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "dist"
3 | command = "npm run build"
4 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [
4 | {
5 | "src": "package.json",
6 | "use": "@now/static-build",
7 | "config": {
8 | "distDir": "/dist"
9 | }
10 | }
11 | ],
12 | "build": {
13 | "env": {}
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "license": "MIT",
3 | "name": "origin-eleventy",
4 | "devDependencies": {
5 | "@11ty/eleventy": "^0.11.0",
6 | "@11ty/eleventy-plugin-rss": "^1.0.9",
7 | "@11ty/eleventy-plugin-syntaxhighlight": "^3.0.1",
8 | "@babel/core": "^7.11.4",
9 | "@babel/preset-env": "^7.11.0",
10 | "babel-loader": "^8.1.0",
11 | "babel-plugin-transform-class-properties": "^6.24.1",
12 | "copy-webpack-plugin": "^6.0.3",
13 | "css-loader": "^4.2.1",
14 | "cssnano": "^4.1.10",
15 | "file-loader": "^6.0.0",
16 | "luxon": "^1.25.0",
17 | "markdown-it-emoji": "^1.4.0",
18 | "markdown-it-footnote": "^3.0.2",
19 | "mini-css-extract-plugin": "^0.10.0",
20 | "node-sass": "^4.14.1",
21 | "npm-run-all": "^4.1.5",
22 | "postcss": "^7.0.32",
23 | "postcss-import": "^12.0.1",
24 | "postcss-loader": "^3.0.0",
25 | "postcss-preset-env": "^6.7.0",
26 | "purgecss": "^2.3.0",
27 | "sass-loader": "^9.0.3",
28 | "stimulus": "^1.1.1",
29 | "style-loader": "^1.2.1",
30 | "tailwindcss": "^1.7",
31 | "@tailwindcss/ui": "^0.5.0",
32 | "turbolinks": "^5.2.0",
33 | "typeface-inter": "^3.15.0",
34 | "webpack": "^4.44.1",
35 | "webpack-cli": "^3.3.12",
36 | "webpack-dev-server": "^3.11.0"
37 | },
38 | "scripts": {
39 | "build": "run-s -l clean generate package:prod",
40 | "build:serve": "run-s -l clean generate package:prod serve:dist",
41 | "clean": "rm -rf dist",
42 | "clean:assets": "rm -rf dist/assets",
43 | "dev": "run-p -l package serve",
44 | "generate": "eleventy",
45 | "package": "webpack --mode development --watch --env.NODE_ENV=development",
46 | "package:prod": "webpack --mode production --env.NODE_ENV=production",
47 | "serve": "eleventy --serve",
48 | "serve:dist": "npx http-server dist"
49 | },
50 | "dependencies": {
51 | "alpinejs": "^2.6.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = ({ env }) => {
2 | let environment = {
3 | plugins: [
4 | require("postcss-import"),
5 | require("tailwindcss")("./tailwind.config.js"),
6 | require("postcss-preset-env"),
7 | ],
8 | };
9 |
10 | if (env === "production") {
11 | environment.plugins.push(
12 | require("cssnano")({
13 | preset: "default",
14 | })
15 | );
16 | }
17 |
18 | return environment;
19 | };
20 |
--------------------------------------------------------------------------------
/site/.eleventyignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 |
--------------------------------------------------------------------------------
/site/404.njk:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | permalink: 404.html
4 | eleventyExcludeFromCollections: true
5 | ---
6 |
`;
46 | });
47 | };
48 |
--------------------------------------------------------------------------------
/src/utils/excerpts.js:
--------------------------------------------------------------------------------
1 | // Based code found here: https://github.com/11ty/eleventy/issues/179#issuecomment-413119342
2 |
3 | module.exports = function(eleventyConfig, options = {}) {
4 | options.stripTags = options.stripTags || false;
5 | options.excerptMinimumLength = options.excerptMinimumLength || 140;
6 | options.excerptSeparator = options.excerptSeparator || "";
7 | options.frontMatterKey = options.frontMatterKey || "excerpt";
8 | eleventyConfig.addShortcode("excerpt", post => findAndCleanExcerpt(post));
9 |
10 | function findAndCleanExcerpt(post) {
11 | const rawExcerpt = extractExcerpt(post);
12 | if (options.stripTags) {
13 | return stripHTML(rawExcerpt);
14 | }
15 | return rawExcerpt;
16 | }
17 |
18 | /**
19 | * Extracts the excerpt from a document.
20 | *
21 | * @param {*} doc A real big object full of all sorts of information about a document.
22 | * @returns {String} the excerpt.
23 | */
24 | function extractExcerpt(doc) {
25 | if (doc.data[options.frontMatterKey]) {
26 | return doc.data[options.frontMatterKey];
27 | }
28 | if (!doc.hasOwnProperty("templateContent")) {
29 | console.warn(
30 | "Failed to extract excerpt: Document has no property `templateContent`."
31 | );
32 | return "";
33 | }
34 |
35 | const content = doc.templateContent.replace(/
/, "");
36 | const excerptIndex = content.indexOf(options.excerptSeparator);
37 | if (excerptIndex > -1) {
38 | return content.substring(0, excerptIndex).trim();
39 | } else if (content.length <= options.excerptMinimumLength) {
40 | return content.trim();
41 | }
42 |
43 | const excerptEnd = findExcerptEnd(content);
44 | return content.substring(0, excerptEnd).trim();
45 | }
46 |
47 | /**
48 | * Finds the end position of the excerpt of a given piece of content.
49 | * This should only be used when there is no excerpt marker in the content (e.g. no ``).
50 | *
51 | * @param {String} content The full text of a piece of content (e.g. a blog post)
52 | * @param {Number?} skipLength Amount of characters to skip before starting to look for a `