Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Commit 7bfa1a9

Browse files
committed
feat(admin): add Netlify CMS files
1 parent 308ca01 commit 7bfa1a9

File tree

12 files changed

+266
-39
lines changed

12 files changed

+266
-39
lines changed

LICENSE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ MIT License
22

33
Copyright (c) 2016 NUXT
44

5+
Additions not coming from the upstream repository and made for running this with Netlify CMS:
6+
Copyright (c) 2018 René Stalder
7+
58
Permission is hereby granted, free of charge, to any person obtaining a copy
69
of this software and associated documentation files (the "Software"), to deal
710
in the Software without restriction, including without limitation the rights

README.md

Lines changed: 112 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
# Starter
1+
# Nuxt.js + Netlify CMS starter template
22

3-
A [Nuxt.js](https://github.com/nuxt/nuxt.js) starter project template without the distraction of a complicated development environment.
3+
> Build server-less, static websites with Vue.js and Netlify CMS.
44
5-
Live demo: https://starter.nuxtjs.org
5+
This is a fork of the [Nuxt.js starter project](https://github.com/nuxt-community/starter-template) with the goal, to extend it with [Netlify CMS](https://www.netlifycms.org) for building pre-rendered, server-less websites. See the [Further explanation](#further-explanation) section for more information.
6+
7+
Live demo: Coming soon.
68

79
## Prerequisites
810

9-
Make sure to have `node 8.0+` and `npm 5.0+` installed
11+
* Make sure to have `node 8.0+` and `npm 5.0+` installed
12+
* You know what Netlify CMS and Nuxt.js is.
1013

1114
## Installation
1215

1316
This is a project template for [vue-cli](https://github.com/vuejs/vue-cli).
1417

1518
``` bash
16-
$ vue init nuxt-community/starter-template my-project
19+
$ vue init renestalder/nuxt-netlify-cms-starter-template my-project
1720
$ cd my-project
1821
# install dependencies
1922
$ npm install # Or yarn install
@@ -23,6 +26,11 @@ $ npm install # Or yarn install
2326
2427
## Usage
2528

29+
### Configuration
30+
31+
* `nuxt.config.js` contains the default parameters pls an additional variable to define the route matching between Netlify CMS and Nuxt.js.
32+
* **Netlify CMS**: Please follow the documentation of Netlify CMS to get it up and running with your repository.
33+
2634
### Development
2735

2836
``` bash
@@ -34,15 +42,106 @@ Go to [http://localhost:3000](http://localhost:3000)
3442

3543
### Production
3644

37-
``` bash
38-
# build for production and launch the server
39-
$ npm run build
40-
$ npm start
41-
```
42-
43-
### Generate
44-
4545
``` bash
4646
# generate a static project
4747
$ npm run generate
4848
```
49+
50+
While the other commands from Nuxt.js remain in this starter kit, we clearly focus on serving static HTML files, thus the `generate` command is our task to create the website for production.
51+
52+
## Further explanation
53+
54+
The goal of this boilerplate is to allow creating websites server-less and enable content management
55+
with a CMS at the same time. It's all about saving time, saving money and especially keep to front-end work, while allowing people to edit content.
56+
57+
### What it does
58+
59+
* [x] **Pre-rendering:** Uses Nuxt.js to pre-render your Vuejs website to static HTML files.
60+
* [x] **Easy dynamic route rendering**: Allows to map glob patterns to your Nuxt.js page paths.
61+
This (partially) removes the need to define all dynamic routes by hand.
62+
[ ] **Automatic mapping dynamic routes for pre-render:**: No more need to define dynamic routes manually.
63+
Netlify CMS and Nuxt.js sync their routes automatically.
64+
* [x] **CMS:** Allows to edit content without the need of a server, thanks to Netlify CMS.
65+
66+
### How it works
67+
68+
For this particular example we use following libraries and frameworks:
69+
70+
* **Vue.js with Nuxt.js**: Vue.js for building our application and Nuxt.js for pre-rendering it.
71+
In general, this could be anything here as long it can read json or markdown files and is can be
72+
pre-rendered.
73+
* **Netlify CMS**: A CMS which is built on JavaScript and connects to the Git repository.
74+
It defines the content models and edits the content files in the repository.
75+
76+
Basically, we use the default Nuxt.js boilerplate, add Netlify CMS files and extend the Nuxt.js config
77+
to collect all content files and add those routes automatically to your Nuxt.js files.
78+
79+
Nuxt.js doesn't pre-render dynamic routes, e.g. blog/post/my-blog-post-2. But it allows to add the urls to those
80+
pages manually to your config. Since you don't want to add a route to your config everytime you publish a blog post,
81+
we allow to set dynamic paths with a glob in the Nuxt.js config. Those define glob folders are grabbed and
82+
all files are added as routes to the Nuxt.js generate.route config_.
83+
84+
#### Principles
85+
86+
A pragmatic overview what this does.
87+
88+
1. You have a plain Vue.js and Nuxt.js website. That's fine and it works, but you want your pages to
89+
be SEO optimized. So you have two choices: Getting a node server for rendering your website server-side or
90+
you use pre-rendering, which converts all your pages to static HTML pages. This boilerplate does the second one.
91+
2. You add Netlify CMS and connect it to your repository. Now you can create content as json that is stored in
92+
a content folder in your repository.
93+
3. Through webpack, you import the content into your pages and show it to the users.
94+
4. In addition, you run Nuxt.js' generate command to make all this rendered as static HTML files.
95+
5. Everytime a file changes in the repository, the generate command has to be run so the static files of the
96+
website are up to date. _This is not part of this boilerplate. I assume you know how this works with your
97+
favourite CI.
98+
99+
### Why?
100+
101+
Tired of the time investment needed to setup a website that needs to be progressive enhanced,
102+
SEO friendly, accessible and allows to access all content on client-side to benefit from doing additional
103+
UX improvements like page transitions, this is an attempt to make the process for building small or medium sizes
104+
CMS websites fast, easy to edit and easy to use.
105+
106+
You can do all this with CMS like WordPress if you're willing to invest your time:
107+
You have to get an Apache server with PHP, setup WordPress, somehow setup the custom post types
108+
and custom fields (probably by installing ACF), setup the WordPress JSON API, write your templates
109+
in PHP + probably even a second time so you can render them with client-side technology. And then,
110+
at the end, having a website that maybe changes once in a couple of weeks.
111+
112+
This looks like a lot of work for sharing content with the internet.
113+
114+
Second, plain old HTML is still the best. The technology stack in this boilerplate exactly
115+
does that, but gives you the additional tools for content editing and for building advanced web experiences.
116+
117+
In the end, you don't need Vue.js or Nuxt.js. You can get rid of it. You can pre-render your vanilla JavaScript
118+
with Webpack, for sure. You don't need Netlify CMS. Authors could edit Markdown and JSON files directly in the repository.
119+
BUT this is not what we want. We don't want people to get to know Git, we don't want them to edit JSON or Markdown files
120+
without validate their entries.
121+
122+
### Uncertain
123+
124+
#### Build performance with a big amount of pages (e.g. 20'000)
125+
126+
While I've read a while ago that the Nuxt.js documentation is built pre-rendered with up to 20'000 pages.
127+
It's not clear at this point how long such a build takes and how a partial build process could look like.
128+
129+
#### Building different sizes of images
130+
131+
This certainly is an easy one since you could check folders of images and could create a task in webpack or your
132+
CI system to create additional image sizes. Also here, this would have to be a task that partially resizes images
133+
only if they changed.
134+
135+
#### No error check for missing globs
136+
137+
The build might fail all over if you map routes to content that doesn't exist. This will be easy to fix.
138+
139+
### When not to use a static server-less system like this
140+
141+
When your website changes a lot. For example, if you're building news websites whose content changes all 5 minutes,
142+
this would mean, that the whole website is re-generated all 5 minutes. Certainly not the best idea. In this case
143+
you might be better with something more dynamic. But the good part is: If you're building your website on a framework like
144+
Nuxt.js, you can simply switch to server-rendering as soon
145+
as the static, server-less behaviour does not longer fit
146+
your needs. It's as easy as switching two commands in your
147+
build system (and probably changing those window instances that don't work server-side).

meta.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
helpers: {
3-
raw: function(options) {
3+
raw: function (options) {
44
return options.fn(this)
55
}
66
},
@@ -14,7 +14,7 @@ module.exports = {
1414
'type': 'string',
1515
'required': false,
1616
'message': 'Project description',
17-
'default': 'Nuxt.js project'
17+
'default': 'Nuxt.js + Netlify CMS project'
1818
},
1919
author: {
2020
'type': 'string',

template/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,16 @@ $ npm run generate
2020
```
2121

2222
For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js).
23+
24+
## Edit content
25+
26+
Access `yourwebsite.com/admin`, e.g. `localhost:3000/admin`.
27+
28+
## Manage dynamic routes
29+
30+
When you use Netlify CMS' `folder` type, you actually create dynamic routes. For example, when creating a blog,
31+
you render different content files with the same template. And for the blog to know which content to render,
32+
it looks at the url and gets the specific content file. That's a dynamic route.
33+
34+
So If you create a `folder` type with Netlify CMS, add the folder as glob to the `dynamicRoutes` variable
35+
in the `nuxt.config.js`.

template/app.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html {{ HTML_ATTRS }}>
3+
4+
<head>
5+
{{ HEAD }}
6+
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
7+
</head>
8+
9+
<body {{ BODY_ATTRS }}>
10+
{{ APP }}
11+
</body>
12+
<script>
13+
if (window.netlifyIdentity) {
14+
window.netlifyIdentity.on("init", user => {
15+
if (!user) {
16+
window.netlifyIdentity.on("login", () => {
17+
document.location.href = "/admin/";
18+
});
19+
}
20+
});
21+
}
22+
</script>
23+
24+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"title": "Hello world",
3+
"date": "2018-02-13T19:32:44.767Z",
4+
"body": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea **takimata sanctus** est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
5+
}

template/nuxt.config.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
var glob = require('glob');
2+
var path = require('path');
3+
4+
// Enhance Nuxt's generate process by gathering all content files from Netifly CMS
5+
// automatically and match it to the path of your Nuxt routes.
6+
// The Nuxt routes are generate by Nuxt automatically based on the pages folder.
7+
var dynamicRoutes = getDynamicPaths({
8+
'/blog': 'blog/posts/*.json'
9+
});
10+
11+
112
module.exports = {
213
/*
314
** Headers of the page
@@ -24,7 +35,7 @@ module.exports = {
2435
/*
2536
** Run ESLint on save
2637
*/
27-
extend (config, { isDev, isClient }) {
38+
extend(config, { isDev, isClient }) {
2839
if (isDev && isClient) {
2940
config.module.rules.push({
3041
enforce: 'pre',
@@ -36,3 +47,18 @@ module.exports = {
3647
}
3748
}
3849
}
50+
51+
/**
52+
* Create an array of URLs from a list of files
53+
* @param {*} urlFilepathTable
54+
*/
55+
function getDynamicPaths(urlFilepathTable) {
56+
return [].concat(
57+
...Object.keys(urlFilepathTable).map(url => {
58+
var filepathGlob = urlFilepathTable[url];
59+
return glob
60+
.sync(filepathGlob, { cwd: 'content' })
61+
.map(filepath => `${url}/${path.basename(filepath, '.json')}`);
62+
})
63+
);
64+
}

template/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"private": true,
77
"scripts": {
88
"dev": "nuxt",
9+
"serve": "nuxt",
910
"build": "nuxt build",
1011
"start": "nuxt start",
1112
"generate": "nuxt generate",
@@ -20,6 +21,7 @@
2021
"eslint": "^4.15.0",
2122
"eslint-friendly-formatter": "^3.0.0",
2223
"eslint-loader": "^1.7.1",
23-
"eslint-plugin-vue": "^4.0.0"
24+
"eslint-plugin-vue": "^4.0.0",
25+
"glob": "^7.1.2"
2426
}
2527
}

template/pages/blog/_slug.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<template>
2+
<article>
3+
<h1>{{ title }}</h1>
4+
</article>
5+
</template>
6+
7+
<script>
8+
export default {
9+
async asyncData({ params }) {
10+
// const postPromise = process.BROWSER_BUILD
11+
// ? import('~/content/blog/posts/' + params.slug + '.json')
12+
// : Promise.resolve(
13+
// require('~/content/blog/posts/' + params.slug + '.json')
14+
// );
15+
16+
let post = await import('~/content/blog/posts/' + params.slug + '.json');
17+
return post;
18+
}
19+
};
20+
</script>

template/pages/index.vue

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
<template>
22
<section class="container">
3-
<div>
4-
<app-logo/>
5-
<h1 class="title">
6-
{{ name }}
7-
</h1>
8-
<h2 class="subtitle">
9-
{{ description }}
10-
</h2>
11-
<div class="links">
12-
<a
13-
href="https://nuxtjs.org/"
14-
target="_blank"
15-
class="button--green">Documentation</a>
16-
<a
17-
href="https://github.com/nuxt/nuxt.js"
18-
target="_blank"
19-
class="button--grey">GitHub</a>
20-
</div>
21-
</div>
3+
<h2>Blog</h2>
4+
<ul>
5+
<li v-for="post in posts" :key="post.date">
6+
<nuxt-link :to="post._path">
7+
{{ post.title }}
8+
</nuxt-link>
9+
</li>
10+
</ul>
2211
</section>
2312
</template>
2413

2514
<script>
26-
import AppLogo from '~/components/AppLogo.vue'
15+
import AppLogo from '~/components/AppLogo.vue';
2716
2817
export default {
2918
components: {
3019
AppLogo
20+
},
21+
data() {
22+
// Using webpacks context to gather all files from a folder
23+
const context = require.context('~/content/blog/posts/', false, /\.json$/);
24+
25+
const posts = context.keys().map(key => ({
26+
...context(key),
27+
_path: `/blog/${key.replace('.json', '').replace('./', '')}`
28+
}));
29+
30+
return { posts };
3131
}
32-
}
32+
};
3333
</script>
3434

3535
<style>
@@ -42,7 +42,8 @@ export default {
4242
}
4343
4444
.title {
45-
font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* 1 */
45+
font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
46+
'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; /* 1 */
4647
display: block;
4748
font-weight: 300;
4849
font-size: 100px;

0 commit comments

Comments
 (0)