├── assets └── .gitignore ├── static ├── .gitignore ├── favicon.ico └── README.md ├── layouts ├── reveal.vue └── default.vue ├── jsconfig.json ├── .editorconfig ├── .eslintrc.js ├── plugins └── README.md ├── middleware └── README.md ├── store └── README.md ├── README.md ├── pages ├── posts │ ├── _slug.vue │ └── _y │ │ └── _mo │ │ └── _slug.vue ├── index.vue ├── _pageNumber.vue └── reveal │ └── _path.vue ├── express └── routes │ └── index.js ├── package.json ├── .gitignore ├── components ├── PostQuery.vue ├── PostTeaser.vue ├── Pagination.vue └── PostFull.vue ├── nuxt.config.js └── prebuild └── index.js /assets/.gitignore: -------------------------------------------------------------------------------- 1 | /data/ -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | /data/ 2 | /media/ -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patarapolw/blog-reveal.nuxt/master/static/favicon.ico -------------------------------------------------------------------------------- /layouts/reveal.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'plugin:nuxt/recommended' 13 | ], 14 | // add your custom rules here 15 | rules: { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blog-reveal.nuxt 2 | 3 | > Blog-reveal creator, powered by Nuxt 4 | 5 | ## Usage 6 | 7 | Create `.env` with 8 | 9 | ```env 10 | ROOT= 11 | ``` 12 | 13 | ## Build Setup 14 | 15 | ``` bash 16 | # install dependencies 17 | yarn install 18 | 19 | # serve with hot reload at localhost:3000 20 | yarn dev 21 | 22 | # build for production and launch server 23 | yarn build 24 | yarn start 25 | 26 | # generate static project 27 | yarn generate 28 | ``` 29 | 30 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 31 | -------------------------------------------------------------------------------- /pages/posts/_slug.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 29 | -------------------------------------------------------------------------------- /pages/posts/_y/_mo/_slug.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 29 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 29 | -------------------------------------------------------------------------------- /express/routes/index.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express' 2 | import bodyParser from 'body-parser' 3 | 4 | import { config, loadDb } from '../../prebuild' 5 | 6 | const app = Router() 7 | const db = loadDb() 8 | 9 | app.use(bodyParser.json()) 10 | 11 | app.get('/data/config.json', (req, res) => { 12 | res.send(config) 13 | }) 14 | 15 | app.post('/data/search.json', async (req, res, next) => { 16 | try { 17 | const { q, proj, sort, skip, limit } = req.body 18 | let c = db.find( 19 | q, 20 | proj 21 | ) 22 | if (sort) { 23 | c = c.sort(sort) 24 | } 25 | if (skip) { 26 | c = c.skip(skip) 27 | } 28 | if (limit) { 29 | c = c.limit(limit) 30 | } 31 | 32 | res.json(await c) 33 | } catch (e) { 34 | next(e) 35 | } 36 | }) 37 | 38 | module.exports = app 39 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 33 | -------------------------------------------------------------------------------- /pages/_pageNumber.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog-reveal.nuxt", 3 | "version": "1.0.0", 4 | "description": "Blog-reveal creator, powered by Nuxt", 5 | "author": "Pacharapol Withayasakpunt", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore ." 13 | }, 14 | "dependencies": { 15 | "@nuxtjs/axios": "^5.3.6", 16 | "@nuxtjs/dotenv": "^1.4.0", 17 | "cors": "^2.8.5", 18 | "dayjs": "^1.8.19", 19 | "dot-prop": "^5.2.0", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "fast-glob": "^3.1.1", 23 | "fs-extra": "^8.1.0", 24 | "gray-matter": "^4.0.2", 25 | "hyperscript": "^2.0.2", 26 | "js-yaml": "^3.13.1", 27 | "nedb-promises": "^4.0.1", 28 | "nuxt": "^2.0.0", 29 | "nuxt-buefy": "^0.3.2", 30 | "nuxt-express-module": "^0.0.11", 31 | "nuxt-payload-extractor": "^0.0.14", 32 | "scope-css": "^1.2.1", 33 | "showdown": "^1.9.1" 34 | }, 35 | "devDependencies": { 36 | "@nuxtjs/eslint-config": "^1.0.1", 37 | "@nuxtjs/eslint-module": "^1.0.0", 38 | "babel-eslint": "^10.0.1", 39 | "eslint": "^6.1.0", 40 | "eslint-plugin-nuxt": ">=0.4.2", 41 | "pug": "^2.0.4", 42 | "pug-plain-loader": "^1.0.0", 43 | "sass": "^1.25.0", 44 | "sass-loader": "^8.0.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # Mac OSX 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /components/PostQuery.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 58 | -------------------------------------------------------------------------------- /components/PostTeaser.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 55 | 56 | 61 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | import { config } from './prebuild' 2 | 3 | export default { 4 | mode: 'universal', 5 | /* 6 | ** Headers of the page 7 | */ 8 | head: { 9 | titleTemplate: config.title ? `%s - ${config.title}` : '%s', 10 | meta: [ 11 | { charset: 'utf-8' }, 12 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 13 | { hid: 'description', name: 'description', content: config.description } 14 | ], 15 | link: [ 16 | { rel: 'icon', type: 'image/x-icon', href: '/media/favicon.ico' } 17 | ] 18 | }, 19 | /* 20 | ** Customize the progress-bar color 21 | */ 22 | loading: { color: '#fff' }, 23 | /* 24 | ** Global CSS 25 | */ 26 | css: [ 27 | ], 28 | /* 29 | ** Plugins to load before mounting the App 30 | */ 31 | plugins: [ 32 | ], 33 | /* 34 | ** Nuxt.js dev-modules 35 | */ 36 | buildModules: [ 37 | // Doc: https://github.com/nuxt-community/eslint-module 38 | '@nuxtjs/eslint-module' 39 | ], 40 | /* 41 | ** Nuxt.js modules 42 | */ 43 | modules: [ 44 | // Doc: https://buefy.github.io/#/documentation 45 | 'nuxt-buefy', 46 | // Doc: https://axios.nuxtjs.org/usage 47 | '@nuxtjs/axios', 48 | // Doc: https://github.com/nuxt-community/dotenv-module 49 | '@nuxtjs/dotenv', 50 | 'nuxt-payload-extractor', 51 | 'nuxt-express-module' 52 | ], 53 | /* 54 | ** Axios module configuration 55 | ** See https://axios.nuxtjs.org/options 56 | */ 57 | axios: { 58 | }, 59 | /* 60 | ** Build configuration 61 | */ 62 | build: { 63 | /* 64 | ** You can extend webpack config here 65 | */ 66 | extend (config, ctx) { 67 | } 68 | }, 69 | hooks: { 70 | // build: { 71 | // done () { 72 | // rawServer.close() 73 | // } 74 | // }, 75 | // generate: { 76 | // done () { 77 | // rawServer.close() 78 | // } 79 | // } 80 | }, 81 | env: { 82 | config: JSON.stringify(config) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /components/Pagination.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 63 | -------------------------------------------------------------------------------- /components/PostFull.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 69 | 70 | 76 | -------------------------------------------------------------------------------- /prebuild/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | 3 | import fs from 'fs-extra' 4 | import dotenv from 'dotenv' 5 | import matter from 'gray-matter' 6 | import glob from 'fast-glob' 7 | import DataStore from 'nedb-promises' 8 | import yaml from 'js-yaml' 9 | import dayjs from 'dayjs' 10 | 11 | dotenv.config() 12 | 13 | export function loadDb () { 14 | const db = new DataStore() 15 | 16 | glob.sync('**/*.md', { 17 | cwd: path.join(process.env.ROOT, 'data') 18 | }).map((f) => { 19 | // eslint-disable-next-line prefer-const 20 | let { data, content, excerpt } = matter(fs.readFileSync(path.join(process.env.ROOT, 'data', f), 'utf8'), { 21 | engines: { 22 | yaml: s => yaml.safeLoad(s, { 23 | schema: yaml.JSON_SCHEMA 24 | }) 25 | }, 26 | excerpt_separator: '' 27 | }) 28 | 29 | const ps = f.split('/') 30 | let slug = ps[ps.length - 1].replace(/\.md$/, ''); 31 | (() => { 32 | const m = /^(\d{4}-\d{2}-\d{2})-(.+)$/.exec(slug) 33 | if (m) { 34 | data.date = data.date || m[1] 35 | slug = m[2] 36 | } 37 | })() 38 | 39 | const type = data.type || ps[0] 40 | 41 | if (!data.title) { 42 | const m = /^.*?# ([^\n]+)(.+)$/s.exec(content) 43 | if (m) { 44 | data.title = m[1] 45 | if (!['reveal', 'slides'].includes(type)) { 46 | content = m[2] 47 | } 48 | } 49 | } 50 | 51 | const epoch = data.date ? customDateStringToEpoch(data.date) : undefined 52 | const m = epoch ? dayjs(epoch) : undefined 53 | 54 | excerpt = (excerpt || content).replace(/<[^>]+>?/g, '').substr(0, 140) 55 | 56 | db.insert({ 57 | path: f, 58 | type, 59 | slug, 60 | excerpt, 61 | frontmatter: data, 62 | epoch, 63 | content, 64 | y: m ? m.format('YYYY') : undefined, 65 | mo: m ? m.format('MM') : undefined 66 | }) 67 | }) 68 | 69 | return db 70 | } 71 | 72 | // eslint-disable-next-line import/no-mutable-exports 73 | export let config = {} 74 | if (fs.existsSync(path.join(process.env.ROOT, 'config.json'))) { 75 | config = require(path.join(process.env.ROOT, 'config.json')) 76 | } 77 | 78 | if (fs.existsSync(path.join(process.env.ROOT, 'data/media'))) { 79 | fs.copySync( 80 | path.join(process.env.ROOT, 'data/media'), 81 | 'static/media' 82 | ) 83 | } 84 | 85 | function customDateStringToEpoch (date) { 86 | if (!date) { 87 | return undefined 88 | } 89 | 90 | /** 91 | * Moment will default timezone to local if not specified, unlike Date.parse 92 | * 93 | * https://momentjs.com/docs/#/parsing/ 94 | * 95 | * See #please-read 96 | */ 97 | let m = dayjs(date, [ 98 | 'YYYY-MM-DD HH:MM ZZ', 99 | 'YYYY-MM-DD ZZ', 100 | 'YYYY-MM-DD HH:MM', 101 | 'YYYY-MM-DD' 102 | ]) 103 | 104 | if (m.isValid()) { 105 | /** 106 | * moment().unix() is in seconds 107 | */ 108 | return m.unix() * 1000 109 | } 110 | 111 | m = dayjs(date) 112 | 113 | if (m.isValid()) { 114 | return m.unix() * 1000 115 | } 116 | 117 | return undefined 118 | } 119 | -------------------------------------------------------------------------------- /pages/reveal/_path.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 216 | 217 | 228 | --------------------------------------------------------------------------------