├── netlify.toml ├── markdown ├── index.md ├── 404.md ├── about.md ├── articles │ ├── second.md │ └── markdown-tutorial.md └── privacy-policy.md ├── src ├── static │ ├── robots.txt │ ├── favicon-48x48.png │ ├── uploads │ │ ├── image.jpg │ │ ├── image.webp │ │ ├── image2.jpg │ │ ├── image3.jpg │ │ ├── image2.webp │ │ └── image3.webp │ └── admin │ │ ├── index.html │ │ └── config.yml ├── index.html ├── templates │ ├── missing.vue │ ├── home.vue │ ├── index.vue │ ├── page.vue │ └── post.vue ├── partials │ ├── foot.vue │ ├── comment.vue │ └── navigation.vue ├── js │ └── index.js └── css │ └── index.styl ├── .editorconfig ├── the-magic ├── build │ ├── get-time.js │ ├── webpack.client.config.js │ ├── webpack.server.config.js │ ├── folders.js │ ├── gulpfile.js │ ├── optimize-images.js │ ├── setup-dev-server.js │ ├── webpack.base.config.js │ ├── build.js │ ├── server.js │ └── render-markdown.js └── boot │ ├── async-data.js │ ├── store-reconciliation.js │ ├── meta-tags.js │ ├── index.js │ ├── router.js │ ├── entry-server.js │ ├── facebook-social.js │ └── analytics.js ├── .npmignore ├── LICENSE ├── .gitignore ├── package.json ├── site.config.js └── README.md /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "dist" 3 | command = "yarn build" -------------------------------------------------------------------------------- /markdown/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Home Page' 3 | --- 4 |

Welcome to Your Site

-------------------------------------------------------------------------------- /src/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | 4 | Sitemap: http://yoursite.netlify.com/sitemap.xml -------------------------------------------------------------------------------- /src/static/favicon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/favicon-48x48.png -------------------------------------------------------------------------------- /src/static/uploads/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image.jpg -------------------------------------------------------------------------------- /src/static/uploads/image.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image.webp -------------------------------------------------------------------------------- /src/static/uploads/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image2.jpg -------------------------------------------------------------------------------- /src/static/uploads/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image3.jpg -------------------------------------------------------------------------------- /src/static/uploads/image2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image2.webp -------------------------------------------------------------------------------- /src/static/uploads/image3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingfriend1/vue-static/HEAD/src/static/uploads/image3.webp -------------------------------------------------------------------------------- /markdown/404.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: '404' 3 | --- 4 |

Sorry but that page couldn't be found

5 |

Try finding a link on the home page

-------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Matches multiple files with brace expansion notation 2 | [*.{js,html,sass,stylus,styl,vue}] 3 | charset = utf-8 4 | indent_style = tab 5 | indent_size = 2 6 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /markdown/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'About Me' 3 | --- 4 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nisi tempora fugiat maiores pariatur omnis blanditiis impedit id a molestiae recusandae quas adipisci voluptates, culpa, quaerat saepe, deleniti labore ex esse. -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /the-magic/build/get-time.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prints the current time in ISO format for use in setting the date in your markdown articles 3 | * --- 4 | * created: "2019-06-24T13:29:45.280Z" 5 | * --- 6 | */ 7 | 8 | console.log(`\nThe current time is:`) 9 | console.log(new Date().toISOString()) 10 | console.log('\n') -------------------------------------------------------------------------------- /src/templates/missing.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /src/partials/foot.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /the-magic/build/webpack.client.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const base = require('./webpack.base.config') 4 | const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') 5 | const folders = require('./folders.js') 6 | 7 | const config = merge(base, { 8 | entry: { 9 | app: folders.entry_client 10 | }, 11 | plugins: [ 12 | // strip dev-only code in Vue source 13 | new webpack.DefinePlugin({ 14 | 'process.env.VUE_ENV': '"client"' 15 | }), 16 | new VueSSRClientPlugin() 17 | ] 18 | }) 19 | 20 | module.exports = config -------------------------------------------------------------------------------- /markdown/articles/second.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Sample Post #2' 3 | thumbnail: /uploads/image2.jpg 4 | silent: true 5 | --- 6 | 7 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci laborum, doloremque. Repellat sapiente incidunt voluptas, placeat. Nam consectetur maxime eaque magnam nostrum, iste voluptates facilis! Quidem sequi itaque eveniet nesciunt. 8 | 9 | 10 | 11 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci corrupti commodi temporibus quaerat tempora sunt ducimus tempore provident similique error, veniam quia perferendis, libero repudiandae quo pariatur ut accusantium quibusdam. -------------------------------------------------------------------------------- /markdown/privacy-policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Privacy Policy' 3 | --- 4 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is included in the production.js build file 3 | * To know exactly at what point this file is inserted, view `the-magic/boot/index.js` and look for `main_js` 4 | * `store`, `router`, `config`, `app` are global variables for use in this file, keep in mind this script is run just before the root component is mounted 5 | */ 6 | 7 | import Vue from 'vue' 8 | 9 | Vue.filter('prettifyDate', function (value) { 10 | var months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; 11 | var date = new Date(value); 12 | return months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear(); 13 | }) -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | dist 23 | 24 | # Dependency directory 25 | # Commenting this out is preferred by some people, see 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Users Environment Variables 30 | .lock-wscript 31 | 32 | data/ 33 | environment.env/ 34 | environment-dev.env/ 35 | -------------------------------------------------------------------------------- /the-magic/boot/async-data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Runs client side only 3 | * Before the component mounts or updates it's route, we want to finish running any requied AJAX requests that the component may depend on to load 4 | */ 5 | 6 | import Vue from "vue"; 7 | 8 | Vue.mixin({ 9 | beforeMount() { 10 | const { asyncData } = this.$options; 11 | if (asyncData) { 12 | this.dataPromise = asyncData({ 13 | store: this.$store, 14 | route: this.$route 15 | }); 16 | } 17 | } 18 | }); 19 | 20 | Vue.mixin({ 21 | beforeRouteUpdate(to, from, next) { 22 | const { asyncData } = this.$options; 23 | if (asyncData) { 24 | asyncData({ 25 | store: this.$store, 26 | route: to 27 | }) 28 | .then(next) 29 | .catch(next); 30 | } else { 31 | next(); 32 | } 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /src/static/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Content Manager 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 28 | 29 | -------------------------------------------------------------------------------- /the-magic/build/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const base = require('./webpack.base.config') 4 | const nodeExternals = require('webpack-node-externals') 5 | const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') 6 | const folders = require('./folders.js') 7 | 8 | module.exports = merge(base, { 9 | target: 'node', 10 | devtool: '#source-map', 11 | entry: folders.entry_server, 12 | output: { 13 | path: folders.output_folder, 14 | filename: "[name].js", 15 | libraryTarget: 'commonjs2' 16 | }, 17 | // https://webpack.js.org/configuration/externals/#externals 18 | // https://github.com/liady/webpack-node-externals 19 | externals: nodeExternals({ 20 | whitelist: /(\.css$|\.less$|\.sass$|\.scss$|\.styl$|\.stylus$|\.(png|jpe?g|gif|svg)(\?.*)?$|\.(woff2?|eot|ttf|otf)(\?.*)?$)/ 21 | }), 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env.VUE_ENV': '"server"' 25 | }), 26 | new VueSSRServerPlugin() 27 | ] 28 | }) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jon Paul Miles 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 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | unoptimized-images/* 6 | 7 | # exception to the rule 8 | !unoptimized-images/.gitkeep 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | _index.html 16 | 17 | dist 18 | app/_index.html 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directory 33 | # Commenting this out is preferred by some people, see 34 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 35 | node_modules 36 | 37 | # Users Environment Variables 38 | .lock-wscript 39 | environment.env 40 | data/ 41 | public/admin/ 42 | public/app/ 43 | public/**/* 44 | !public/favicon.ico 45 | !public/images/**/* 46 | lib/email-templates/ 47 | lib/hooks/ 48 | lib/services/ 49 | lib/middleware/ 50 | lib/app.js 51 | lib/index.js 52 | lib/routes.js 53 | lib/renderer.js 54 | lib/seed 55 | server/compiled-ssr.js 56 | client-tests/test.js 57 | server/style.css 58 | platforms/ 59 | www/ 60 | environment-dev.env 61 | .env 62 | # .gitlab-ci.yml 63 | -------------------------------------------------------------------------------- /src/static/admin/config.yml: -------------------------------------------------------------------------------- 1 | backend: 2 | name: git-gateway 3 | branch: master # Branch to update (optional; defaults to master) 4 | 5 | media_folder: "unoptimized-images/" # Media files will be stored in the repo under public/uploads 6 | public_folder: "/" # Folder path where uploaded files will be accessed, relative to the base of the built site 7 | 8 | collections: 9 | - name: "articles" # Used in routes, e.g., /admin/collections/blog 10 | label: "Article" # Used in the UI 11 | folder: "markdown/articles" # The path to the folder where the documents are stored 12 | create: true # Allow users to create new documents in this collection 13 | slug: "{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md 14 | fields: # The fields for each document, usually in front matter. 15 | # Remove any that aren't needed for posts 16 | - {label: "Layout", name: "layout", widget: "hidden", default: "post", required: false} 17 | - {label: "Title", name: "title", widget: "string"} 18 | - {label: "Author", name: "author", widget: "string", required: false} 19 | - {label: "Published", name: "created", widget: "datetime", required: false} 20 | - {label: "Featured Image", name: "thumbnail", widget: "image", required: false} 21 | - {label: "Draft", name: "draft", widget: "boolean", required: false} 22 | - {label: "Body", name: "body", widget: "markdown"} 23 | -------------------------------------------------------------------------------- /the-magic/build/folders.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const config = require('../../site.config') 3 | 4 | const root = path.resolve(__dirname, "..", "..") 5 | 6 | const folders = { 7 | root, 8 | node_modules: path.resolve(root, "node_modules"), 9 | the_magic: path.resolve(root, 'the-magic'), 10 | boot: path.resolve(root, "the-magic", "boot"), 11 | src: path.resolve(root, "src"), 12 | entry_client: path.resolve(root, "the-magic", "boot", "index.js"), 13 | entry_server: path.resolve(root, "the-magic", "boot", "entry-server.js"), 14 | markdown_folder: path.resolve(root, config.folderStructure.markdown), 15 | template_html_path: path.resolve(root, config.folderStructure.html), 16 | css_path: path.resolve(root, config.folderStructure.css), 17 | vue_path: path.resolve(root, config.folderStructure.vue), 18 | js_path: path.resolve(root, config.folderStructure.js), 19 | config_path: path.resolve(root, 'site.config.js'), 20 | components_folder: path.resolve(root, config.folderStructure.components), 21 | partials_folder: path.resolve(root, config.folderStructure.partials), 22 | static_folder: path.resolve(root, config.folderStructure.static), 23 | images_folder: path.resolve(root, config.folderStructure.images), 24 | html_template: path.resolve(root, config.folderStructure.html), 25 | output_folder: path.resolve(root, config.folderStructure.output), 26 | }; 27 | 28 | module.exports = folders -------------------------------------------------------------------------------- /src/partials/comment.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /the-magic/build/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require("gulp"); 2 | const path = require("path"); 3 | const inject = require("gulp-inject"); 4 | const camelCase = require("lodash.camelcase"); 5 | const config = require("../../site.config.js"); 6 | const folders = require('./folders.js') 7 | 8 | const globalize_components_file = path.resolve(folders.boot, "router.js") 9 | 10 | const globalize_components_folder = globalize_components_file.replace( 11 | path.basename(globalize_components_file), "" 12 | ); 13 | 14 | gulp.task("default", function(done) { 15 | return gulp 16 | .src(globalize_components_file) 17 | .pipe( 18 | inject( 19 | gulp.src([ 20 | folders.components_folder + "/**/*.vue", 21 | folders.partials_folder + "/**/*.vue", 22 | "!" + folders.components_folder + "/**/index.vue" 23 | ], { 24 | read: false 25 | }), 26 | { 27 | relative: false, 28 | starttag: "// globalize vue components", 29 | endtag: "// end globalize vue components", 30 | transform: function(filepath, file, i, length) { 31 | let title = filepath.replace(/^.*[\\\/]/, ""); 32 | title = camelCase(title.substr(0, title.lastIndexOf("."))); 33 | let fp = filepath.replace("/", ""); 34 | return `${title}: Vue.component('${title}', require("${fp}").default),`; 35 | } 36 | } 37 | ) 38 | ) 39 | .pipe(gulp.dest(globalize_components_folder)); 40 | }); 41 | -------------------------------------------------------------------------------- /src/partials/navigation.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 47 | -------------------------------------------------------------------------------- /src/templates/home.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 48 | 49 | 52 | -------------------------------------------------------------------------------- /the-magic/build/optimize-images.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gathers fullsized images in the designated images folder and reduces their filesize for use on the web and saves them ot the designated static folder using the same structure they had in the images folder 3 | */ 4 | 5 | const path = require("path") 6 | const imagemin = require("imagemin-keep-folder") 7 | const imageminJpegRecompress = require("imagemin-jpeg-recompress") 8 | const webp = require("imagemin-webp") 9 | const imageminOptipng = require("imagemin-optipng") 10 | const folders = require("./folders.js") 11 | 12 | const PNG = path.join(folders.images_folder, `**`, `*.png`) 13 | const JPG = path.join(folders.images_folder, `**`, `*.jpg`) 14 | const JPEG = path.join(folders.images_folder, `**`, `*.jpeg`) 15 | 16 | imagemin([PNG], { 17 | use: [ 18 | webp({ 19 | lossless: true, 20 | }), 21 | ], 22 | replaceOutputDir: (output) => { 23 | return path.join(folders.static_folder, path.basename(output)) 24 | }, 25 | }) 26 | 27 | imagemin([PNG], { 28 | use: [ 29 | imageminOptipng({ 30 | optimizationLevel: 3, 31 | }), 32 | ], 33 | replaceOutputDir: (output) => { 34 | return path.join(folders.static_folder, path.basename(output)) 35 | }, 36 | }) 37 | 38 | imagemin([JPG, JPEG], { 39 | use: [ 40 | webp({ 41 | quality: 80, // Quality setting from 0 to 100 42 | }), 43 | ], 44 | replaceOutputDir: (output) => { 45 | return path.join(folders.static_folder, path.basename(output)) 46 | }, 47 | }) 48 | 49 | imagemin([JPG, JPEG], { 50 | use: [ 51 | imageminJpegRecompress({ 52 | accurate: true, 53 | quality: "high", 54 | min: 50, 55 | max: 90, 56 | loops: 6, 57 | progressive: true, 58 | strip: true, 59 | target: 0.9, 60 | }), 61 | ], 62 | replaceOutputDir: (output) => { 63 | return path.join(folders.static_folder, path.basename(output)) 64 | }, 65 | }) 66 | -------------------------------------------------------------------------------- /the-magic/boot/store-reconciliation.js: -------------------------------------------------------------------------------- 1 | // The server may have made changes to the store before rendering the initial html. It writes those changes to window.__INITIAL_STATE__. We need to set out local store to be the same as the server so vue does not throw an hydration error (server and client html out of sync) 2 | import Vue from "vue"; 3 | import VueStash from "vue-stash"; 4 | import router from "./router"; 5 | import config from "config"; 6 | import './facebook-social'; 7 | Vue.use(VueStash); 8 | const defaultStore = config.store; 9 | 10 | let store = defaultStore; 11 | 12 | /** 13 | * When our Vue and Markdown files are rendered into HTML, Server-Side Rendering will save the server-side rendered state of the store to the variable `window.__INITIAL_STATE__` 14 | * We want to set our client-side store to initialize with whatever is in this variable, so the server and client store will be in sync 15 | */ 16 | try { 17 | if (typeof window !== "undefined" && window.__INITIAL_STATE__) { 18 | store = window.__INITIAL_STATE__; 19 | } 20 | } catch (err) {} 21 | 22 | 23 | /** 24 | * Before loading the URL, finding the markdown file matching that URL and set the markdown's rendered HTML and metadata object as the `file` property of the store. If a markdown file cannot be found, then load a 404 page 25 | */ 26 | router.beforeEach((to, from, next) => { 27 | let path = to.path.replace('.html', '').replace('.htm', ''); 28 | const found = store.files.find(post => post.url === path); 29 | 30 | /** 31 | * Setting `postBodyEl` will enable scroll tracking with Google Analytics on a page. 32 | * Scrolling tracking is how we determine if the article was completely read (the user reaches the bottom of the article content). 33 | * We want to reset this value between pages in case there's a page that we don't want to track 34 | */ 35 | if(typeof postBodyEl !== 'undefined') { 36 | global.postBodyEl = null; 37 | } 38 | 39 | if (!found) { 40 | router.replace("/404"); 41 | } else { 42 | 43 | store.file = found; 44 | 45 | /** 46 | * `social_url` is used for matching Facebook Comments and Likes to the current page in the `:data-href="$store.social_url"` attribute 47 | */ 48 | store.social_url = config.site_url + store.file.url; 49 | } 50 | next(); 51 | }); 52 | 53 | export default store; 54 | -------------------------------------------------------------------------------- /src/templates/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 47 | 48 | 96 | -------------------------------------------------------------------------------- /the-magic/boot/meta-tags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * To save a lot of typing in the developer's templates, we set the default meta tag configuration for the root object 3 | * These properties can be overridden in subroutes 4 | */ 5 | 6 | import config from 'config'; 7 | 8 | export default { 9 | title: config.site_title, 10 | link: [ 11 | { rel: 'alternate', type: 'application/rss+xml', title: `${config.site_url} > Feed`, href: `${config.site_url + '/feed.xml'}` } 12 | ], 13 | meta: [ 14 | { 15 | name: "viewport", 16 | content: "width=device-width, initial-scale=1" 17 | }, 18 | { "data-vmid": 'url', name: "url", content: config.site_url }, 19 | { "data-vmid": 'identifier-URL', name: "identifier-URL", content: config.site_url }, 20 | { "data-vmid": 'og:url', name: "og:url", content: config.site_url }, 21 | { "data-vmid": 'owner', name: "owner", content: config.author }, 22 | { "data-vmid": "author", name: "author", content: config.author }, 23 | { "data-vmid": 'copyright', name: "copyright", content: config.author }, 24 | { "data-vmid": 'medium', name: "medium", content: "blog" }, 25 | { "data-vmid": 'robots', name: "robots", content: "index,follow" }, 26 | { "data-vmid": 'description', name: "description", content: config.description }, 27 | { "data-vmid": 'keywords', name: "keywords", content: config.keywords }, 28 | { "data-vmid": 'language', name: "language", content: config.language }, 29 | { "data-vmid": 'fb:app_id', property:"fb:app_id", content: config.facebook_id }, 30 | { "data-vmid": 'og:locale', property:"og:locale", content: config.locale }, 31 | { "data-vmid": 'og:site_name', property:"og:site_name", content: config.site_title }, 32 | { "data-vmid": 'og:title', property: "og:title", content: config.site_title }, 33 | { "data-vmid": 'og:description', property: "og:description", content: config.description }, 34 | { "data-vmid": 'og:image', property: "og:image", content: config.site_url + config.logo }, 35 | { "data-vmid": 'twitter:image', name: "twitter:image", content: config.site_url + config.logo }, 36 | { "data-vmid": 'twitter:card', name: "twitter:card", content: config.twitterCard }, 37 | { "data-vmid": 'twitter:creator', name: "twitter:creator", content: config.twitterCreator }, 38 | { "data-vmid": 'twitter:title', name: "twitter:title", content: config.site_title }, 39 | { 40 | "data-vmid": 'twitter:description', 41 | name: "twitter:description", 42 | content: config.description 43 | }, 44 | { "data-vmid": 'pagename', name: "pagename", content: config.site_title } 45 | ] 46 | } -------------------------------------------------------------------------------- /src/templates/page.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 69 | -------------------------------------------------------------------------------- /the-magic/boot/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client Side SSR file 3 | * Here we make the router, store, the site config, and the app global variables 4 | */ 5 | 6 | import Vue from "vue"; 7 | import path from 'path'; 8 | import store from "./store-reconciliation"; 9 | import router from "./router"; 10 | import config from "../../site.config.js"; 11 | 12 | import meta_tags from './meta-tags.js'; 13 | 14 | Vue.config.productionTip = false; 15 | 16 | /** 17 | * Make our store, router, and site.config.js (`config`) global so it can be used instantly in any of our templates, js files or components 18 | */ 19 | global.store = store 20 | global.router = router 21 | global.config = config 22 | 23 | /** 24 | * This is our config.folderStructure.css entry file in `/site.config.js` 25 | */ 26 | require(main_css); 27 | 28 | /** 29 | * This is our `config.folderStructure.vue` entry file in `/site.config.js` 30 | */ 31 | let Root = require(main_vue).default; 32 | 33 | Root = Object.assign({}, { 34 | 35 | /** 36 | * We assign our default meta tags in `./meta-tags.js` to our root component 37 | */ 38 | metaInfo() { 39 | return meta_tags 40 | }, 41 | }, Root); 42 | 43 | /** 44 | * Here we construct the root component and connect the Vue-Stash Store and Vue-Router 45 | */ 46 | global.app = new Vue( 47 | Object.assign( 48 | { 49 | router: global.router, 50 | data: { store: global.store }, 51 | }, 52 | Root 53 | ) 54 | ); 55 | 56 | /** 57 | * This is our config.folderStructure.js entry file from `/site.config.js` 58 | */ 59 | require(main_js); 60 | 61 | 62 | if (Vue.prototype.$isServer) { 63 | /** 64 | * If we are rendering the component on the server we simply want to mount the root component without loading any server-side scripts 65 | */ 66 | app.$mount("#app"); 67 | } else { 68 | 69 | /** 70 | * If we are rendering the root component on the client, we want to first complete any ajax requests and include their data in our rendered templates 71 | */ 72 | require("./async-data"); 73 | 74 | /** 75 | * If the user has set `googleAnalyticsId` in `/site.config.js` and we are rendering client-side we want to load our `./analytics.js` script 76 | */ 77 | if(global.config.googleAnalyticsId) { 78 | require("./analytics.js") 79 | } 80 | 81 | /** 82 | * If the user has set a `facebook_id` in `/site.config.js` then we want to initialize facebook comments and likes 83 | * The `facebook_id` is the App ID you would normally use in the meta tag 84 | * ```html 85 | * 86 | * ```` 87 | */ 88 | if(global.config.facebook_id) { 89 | require("./facebook-social.js") 90 | } 91 | 92 | app.$mount("#app"); 93 | } 94 | 95 | /** 96 | * It's important that we export the our root component, router, and store 97 | */ 98 | export default { app, router, store }; 99 | -------------------------------------------------------------------------------- /the-magic/build/setup-dev-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Retrieved from 3 | * https://github.com/vuejs/vue-hackernews-2.0/blob/master/build/setup-dev-server.js 4 | * By Evan You 5 | */ 6 | 7 | const fs = require('fs') 8 | const path = require('path') 9 | const MFS = require('memory-fs') 10 | const webpack = require('webpack') 11 | const chokidar = require('chokidar') 12 | const clientConfig = require('./webpack.client.config') 13 | const serverConfig = require('./webpack.server.config') 14 | 15 | const readFile = (fs, file) => { 16 | try { 17 | return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8') 18 | } catch (e) {} 19 | } 20 | 21 | module.exports = function setupDevServer (app, templatePath, cb) { 22 | let bundle 23 | let template 24 | let clientManifest 25 | 26 | let ready 27 | const readyPromise = new Promise(r => { ready = r }) 28 | const update = () => { 29 | if (bundle && clientManifest) { 30 | ready() 31 | cb(bundle, { 32 | template, 33 | clientManifest 34 | }) 35 | } 36 | } 37 | 38 | // read template from disk and watch 39 | template = fs.readFileSync(templatePath, 'utf-8') 40 | chokidar.watch(templatePath).on('change', () => { 41 | template = fs.readFileSync(templatePath, 'utf-8') 42 | console.log('index.html template updated.') 43 | update() 44 | }) 45 | 46 | // modify client config to work with hot middleware 47 | clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app] 48 | clientConfig.output.filename = '[name].js' 49 | clientConfig.plugins.push( 50 | new webpack.HotModuleReplacementPlugin(), 51 | new webpack.NoEmitOnErrorsPlugin() 52 | ) 53 | 54 | // dev middleware 55 | const clientCompiler = webpack(clientConfig) 56 | const devMiddleware = require('webpack-dev-middleware')(clientCompiler, { 57 | publicPath: clientConfig.output.publicPath, 58 | noInfo: true 59 | }) 60 | app.use(devMiddleware) 61 | clientCompiler.plugin('done', stats => { 62 | stats = stats.toJson() 63 | stats.errors.forEach(err => console.error(err)) 64 | stats.warnings.forEach(err => console.warn(err)) 65 | if (stats.errors.length) return 66 | clientManifest = JSON.parse(readFile( 67 | devMiddleware.fileSystem, 68 | 'vue-ssr-client-manifest.json' 69 | )) 70 | update() 71 | }) 72 | 73 | // hot middleware 74 | app.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 })) 75 | 76 | // watch and update server renderer 77 | const serverCompiler = webpack(serverConfig) 78 | const mfs = new MFS() 79 | serverCompiler.outputFileSystem = mfs 80 | serverCompiler.watch({}, (err, stats) => { 81 | if (err) throw err 82 | stats = stats.toJson() 83 | if (stats.errors.length) return 84 | 85 | // read bundle generated by vue-ssr-webpack-plugin 86 | bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json')) 87 | update() 88 | }) 89 | 90 | return readyPromise 91 | } -------------------------------------------------------------------------------- /the-magic/boot/router.js: -------------------------------------------------------------------------------- 1 | import Vue from"vue"; 2 | import Meta from "vue-meta"; 3 | import VueRouter from 'vue-router' 4 | import config from "config"; 5 | import camelCase from "lodash.camelcase"; 6 | 7 | /** 8 | * `the-magic/build/gulpfile.js` will automatically inject `.vue` files located in the specified `components` and `partials` folder paths (See `/site.config.js`) into this `components` constant between the comments: 9 | * // globalize vue components 10 | comment: Vue.component('comment', require("../../src/partials/comment.vue").default), 11 | foot: Vue.component('foot', require("../../src/partials/foot.vue").default), 12 | navigation: Vue.component('navigation', require("../../src/partials/navigation.vue").default), 13 | home: Vue.component('home', require("../../src/templates/home.vue").default), 14 | missing: Vue.component('missing', require("../../src/templates/missing.vue").default), 15 | page: Vue.component('page', require("../../src/templates/page.vue").default), 16 | post: Vue.component('post', require("../../src/templates/post.vue").default), 17 | // end globalize vue components 18 | * This makes our components available globally under the camelCase version of the file-name (without the extension) 19 | */ 20 | const components = { 21 | // globalize vue components 22 | comment: Vue.component('comment', require("../../src/partials/comment.vue").default), 23 | foot: Vue.component('foot', require("../../src/partials/foot.vue").default), 24 | navigation: Vue.component('navigation', require("../../src/partials/navigation.vue").default), 25 | home: Vue.component('home', require("../../src/templates/home.vue").default), 26 | missing: Vue.component('missing', require("../../src/templates/missing.vue").default), 27 | page: Vue.component('page', require("../../src/templates/page.vue").default), 28 | post: Vue.component('post', require("../../src/templates/post.vue").default), 29 | // end globalize vue components 30 | }; 31 | 32 | /** 33 | * Takes the routes and their matching `string` component names in `/site.config.js`` and replaces with string with the component using the same name 34 | */ 35 | const routes = config.routes 36 | .map(route => { 37 | route.component = camelCase(route.component); 38 | return components[route.component] 39 | ? Object.assign({}, route, { component: components[route.component] }) 40 | : null; 41 | }) 42 | .filter(a => a); 43 | 44 | Vue.use(Meta, { 45 | tagIDKeyName: 'data-vmid' 46 | }); 47 | Vue.use(VueRouter); 48 | 49 | const router = new VueRouter({ 50 | mode: "history", 51 | hashbang: false, 52 | routes, 53 | scrollBehavior (to, from, savedPosition) { 54 | return new Promise((resolve, reject) => { 55 | 56 | /** 57 | * Here is where you determine scroll position for the page you navigate to 58 | * We are returning the scroll position to the top of the page between scrolls 59 | */ 60 | resolve({x: 0, y: 0}) 61 | }) 62 | } 63 | }); 64 | 65 | export default router; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-static", 3 | "description": "A static site generator in Vue.js", 4 | "version": "0.0.0", 5 | "homepage": "", 6 | "main": "server", 7 | "keywords": [ 8 | "Vue.js", 9 | "static-site-generator" 10 | ], 11 | "author": { 12 | "name": "Jon Paul Miles", 13 | "email": "codingfriend1@gmail.com" 14 | }, 15 | "contributors": [], 16 | "bugs": {}, 17 | "directories": { 18 | "lib": "server" 19 | }, 20 | "engines": { 21 | "node": ">=7.6.0", 22 | "yarn": ">= 0.18.0" 23 | }, 24 | "scripts": { 25 | "serve": "http-server --silent -p8080 dist", 26 | "gulp": "gulp --gulpfile the-magic/build/gulpfile.js", 27 | "optimize": "node the-magic/build/optimize-images.js", 28 | "time": "node the-magic/build/get-time.js", 29 | "start": "npm run gulp && node the-magic/build/server", 30 | "build:client": "cross-env NODE_ENV=production webpack --config the-magic/build/webpack.client.config.js --no-info --hide-modules", 31 | "build:server": "cross-env NODE_ENV=production webpack --config the-magic/build/webpack.server.config.js --no-info --hide-modules", 32 | "build": "rimraf dist && npm run gulp && npm run build:client && npm run build:server && cross-env NODE_ENV=production node the-magic/build/build.js" 33 | }, 34 | "dependencies": { 35 | "compression": "^1.7.4", 36 | "express": "^4.17.1", 37 | "gray-matter": "^4.0.2", 38 | "lodash.camelcase": "^4.3.0", 39 | "lodash.throttle": "^4.1.1", 40 | "node-fetch": "^2.6.0", 41 | "route-cache": "^0.4.4", 42 | "serve-favicon": "^2.5.0", 43 | "vue": "^2.6.10", 44 | "vue-analytics": "^5.17.0", 45 | "vue-meta": "git+https://github.com/codingfriend1/vue-meta.git#dev", 46 | "vue-router": "^3.0.6", 47 | "vue-server-renderer": "^2.6.10", 48 | "vue-stash": "^2.0.1-beta" 49 | }, 50 | "devDependencies": { 51 | "colors": "^1.3.3", 52 | "compression-webpack-plugin": "^3.0.0", 53 | "copy-webpack-plugin": "^5.0.3", 54 | "cross-env": "^5.2.0", 55 | "css-loader": "^3.0.0", 56 | "file-loader": "^4.0.0", 57 | "friendly-errors-webpack-plugin": "^1.7.0", 58 | "gulp": "^4.0.2", 59 | "gulp-inject": "^5.0.5", 60 | "http-server": "^0.11.1", 61 | "imagemin": "^6.1.0", 62 | "imagemin-jpeg-recompress": "^6.0.0", 63 | "imagemin-keep-folder": "^5.3.2", 64 | "imagemin-optipng": "^7.0.0", 65 | "imagemin-webp": "^5.1.0", 66 | "json-loader": "^0.5.7", 67 | "klaw": "^3.0.0", 68 | "markdown-it": "^8.4.2", 69 | "markdown-it-attrs": "^2.4.1", 70 | "markdown-it-gallery": "^0.1.6", 71 | "markdown-it-modify-token": "^1.0.2", 72 | "mini-css-extract-plugin": "^0.7.0", 73 | "npm-run-all": "^4.1.5", 74 | "progress-bar-webpack-plugin": "^1.12.1", 75 | "pug": "^2.0.4", 76 | "pug-plain-loader": "^1.0.0", 77 | "raw-loader": "^3.0.0", 78 | "rimraf": "^2.6.3", 79 | "stylus": "^0.54.5", 80 | "stylus-loader": "^3.0.2", 81 | "url-loader": "^2.0.1", 82 | "vue-loader": "15.7.0", 83 | "vue-template-compiler": "^2.6.10", 84 | "webpack": "^4.35.0", 85 | "webpack-cli": "^3.3.5", 86 | "webpack-dev-middleware": "^3.7.0", 87 | "webpack-hot-middleware": "^2.25.0", 88 | "webpack-merge": "^4.2.1", 89 | "webpack-node-externals": "^1.7.2" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /markdown/articles/markdown-tutorial.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Markdown Tutorial' 3 | thumbnail: /uploads/image.jpg 4 | draft: false 5 | description: "A quick and simple markdown tutorial" 6 | --- 7 | 8 | Guide from [Markdown syntax guide by Atlassian](https://confluence.atlassian.com/bitbucketserver/markdown-syntax-guide-776639995.html) 9 | 10 | ### Headings 11 | 12 | ```md 13 | # This is an H1 14 | ## This is an H2 15 | ###### This is an H6 16 | 17 | This is also an H1 18 | ================== 19 | 20 | This is also an H2 21 | ------------------ 22 | ``` 23 | 24 | ### Paragraphs 25 | Paragraphs are separated by empty lines. To create a new paragraph, press twice. 26 | 27 | ```md 28 | Paragraph 1 29 | 30 | Paragraph 2 31 | ``` 32 | 33 | ### Character styles 34 | 35 | ```md 36 | *Italic characters* 37 | _Italic characters_ 38 | **bold characters** 39 | __bold characters__ 40 | ~~strikethrough text~~ 41 | ``` 42 | 43 | ### Unordered list 44 | 45 | ```md 46 | * Item 1 47 | * Item 2 48 | * Item 3 49 | * Item 3a 50 | * Item 3b 51 | * Item 3c 52 | Ordered list 53 | 1. Step 1 54 | 2. Step 2 55 | 3. Step 3 56 | 1. Step 3.1 57 | 2. Step 3.2 58 | 3. Step 3.3 59 | List in list 60 | 1. Step 1 61 | 2. Step 2 62 | 3. Step 3 63 | * Item 3a 64 | * Item 3b 65 | * Item 3c 66 | ``` 67 | 68 | ### Quotes or citations 69 | Introducing my quote: 70 | 71 | ``` 72 | > Neque porro quisquam est qui 73 | > dolorem ipsum quia dolor sit amet, 74 | > consectetur, adipisci velit... 75 | ``` 76 | 77 | ### Inline code characters 78 | 79 | ```md 80 | Use the backtick to refer to a `function()`. 81 | 82 | There is a literal ``backtick (`)`` here. 83 | ``` 84 | 85 | ### Code blocks 86 | 87 | ````md 88 | Indent every line of the block by at least 4 spaces. 89 | 90 | This is a normal paragraph: 91 | 92 | This is a code block. 93 | With multiple lines. 94 | 95 | Alternatively, you can use 3 backtick quote marks before and after the block, like this: 96 | 97 | ``` 98 | This is a code block 99 | ``` 100 | 101 | To add syntax highlighting to a code block, add the name of the language immediately 102 | after the backticks: 103 | 104 | ```javascript 105 | var oldUnload = window.onbeforeunload; 106 | window.onbeforeunload = function() { 107 | saveCoverage(); 108 | if (oldUnload) { 109 | return oldUnload.apply(this, arguments); 110 | } 111 | }; 112 | ``` 113 | ```` 114 | 115 | Within a code block, ampersands (&) and angle brackets (< and >) are automatically converted into HTML entities. 116 | 117 | ### Links to external websites 118 | 119 | ```md 120 | This is [an example](http://www.example.com/) inline link. 121 | 122 | [This link](http://example.com/ "Title") has a title attribute. 123 | 124 | Links are also auto-detected in text: http://example.com/ 125 | ``` 126 | 127 | ### CSS classes {.text-center} 128 | 129 | This is a paragraph with a class of "text-center" added 130 | {.text-center} 131 | 132 | ```md 133 | ### This is a centered heading {.text-center} 134 | 135 | This is a paragraph with a class of "text-center" added 136 | {.text-center} 137 | ``` 138 | 139 | --- 140 | For more information visit: 141 | 142 | [www.markdowntutorial.com](https://www.markdowntutorial.com/) -------------------------------------------------------------------------------- /the-magic/boot/entry-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Runs on server only 3 | * When our `/the-magic/build/build.js` or `/the-magic/build/server.js` attempt to render our Vue and Markdown files into raw HTML files at `renderer.renderToString()` for better SEO, it critically depends on this file to determine which files to render for a given route, to load all necessary subcomponents for that route and render each, and add any data from our server side AJAX requests that need to be embedded in the raw HTML file. If the store is modified, we also need to deliver a copy of that state to the renderer so the client will initialize it's store in the same condition. 4 | */ 5 | 6 | import Vue from "vue"; 7 | import path from "path"; 8 | import colors from 'colors'; 9 | 10 | /** 11 | * For using fetch for async data server-side 12 | */ 13 | global.fetch = require("node-fetch") 14 | 15 | export default context => { 16 | 17 | /** 18 | * It's ok to return a promise to the renderer 19 | */ 20 | return new Promise((resolve, reject) => { 21 | 22 | let { store, app, router } = require(`./index.js`).default; 23 | 24 | /** 25 | * Initialize Vue-Meta 26 | */ 27 | const meta = app.$meta(); 28 | 29 | Object.assign(store, context); 30 | 31 | /** 32 | * The `context` argument provides the url the user is attempting to access in the browser with `context.file.url`. We tell the router to load this route and all it's necessary subcomponents 33 | */ 34 | router.push(context.file.url); 35 | 36 | /** 37 | * We wait for the router to finish loading all necessary components to this route 38 | */ 39 | router.onReady(() => { 40 | let matchedComponents = router.getMatchedComponents(); 41 | 42 | // no matched routes 43 | if (!matchedComponents.length) { 44 | return reject(colors.red(`${context.file.url}`) + colors.grey(` from `) + colors.red(context.file.path.replace(__dirname, '')) + colors.grey(` is not routed to any vue templates. Match a vue template to this url in the ` + colors.green(`site.config.js`) + ` routes.\n\r`) 45 | ); 46 | } 47 | 48 | /** 49 | * For every subcomponent for this route, we check if any of them require AJAX requests to be run and completed before we render. This is helpful in the case that you want to fetch some data at the moment the user or Search Engine Bot load the page and embed the results in the rendered HTML file 50 | * We provide each `asyncData()` function the store and current route in case that information is needed for making the AJAX request 51 | */ 52 | Promise.all( 53 | matchedComponents.map(Component => { 54 | if (Component.options.asyncData) { 55 | 56 | // call `asyncData()` on all matched route components 57 | return Component.options.asyncData({ 58 | store, 59 | route: router.currentRoute 60 | }); 61 | } 62 | }) 63 | ) 64 | .then(() => { 65 | /** 66 | * AJAX requests or loading the components may have modified our server-side store. It's critical that the server rendering fully matches the client-side rendering on the each's initial render and to have matching pages the store needs to be in the same state on the client and server. Therefore, since our basic store may have been modified by our server-side rendering process, we need to provide the modifications to the context so the client can initialize it's store with the state the server renderer left it in. 67 | */ 68 | context.state = store; 69 | 70 | /** 71 | * We also need to provide our meta data rendered by Vue-Meta to our context object so we can inject these tags into our rendered HTML 72 | */ 73 | context.meta = meta; 74 | resolve(app); 75 | }) 76 | .catch(reject); 77 | }); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /site.config.js: -------------------------------------------------------------------------------- 1 | const site_url = "https://yoursite.netlify.com"; 2 | 3 | module.exports = { 4 | site_title: `Your site title`, 5 | site_url, 6 | description: "Your site description", 7 | author: "You", 8 | keywords: "", 9 | medium: "blog", 10 | language: "English", 11 | locale: "en_US", 12 | logo: "/uploads/logo.png", 13 | 14 | twitterCard: "summary_large_image", 15 | twitterCreator: "@your_twitter_username", 16 | 17 | /** 18 | * If you set a `facebook_id` then we will initialize facebook comments and likes 19 | * The `facebook_id` is the App ID you would normally use in the meta tag 20 | * ```html 21 | * 22 | * ```` 23 | */ 24 | facebook_id: null, 25 | 26 | 27 | googleAnalyticsId: "", 28 | mailChimpUrl: "", 29 | 30 | /** 31 | * Tell us where your entry files are located relative to this repository root directory 32 | */ 33 | folderStructure: { 34 | 35 | /** 36 | * Show us where your lead js file is that imports all other scripts 37 | * @type {String} 38 | */ 39 | js: 'src/js/index.js', 40 | 41 | /** 42 | * Lead stylus file that imports all other stylesheets 43 | * @type {String} 44 | */ 45 | css: 'src/css/index.styl', 46 | 47 | /** 48 | * Tell us where your root vue component is 49 | * @type {String} 50 | */ 51 | vue: 'src/templates/index.vue', 52 | 53 | /** 54 | * Tell us where your main html template is 55 | * @type {String} 56 | */ 57 | html: 'src/index.html', 58 | 59 | /** 60 | * Tell us what folder we should search in for your larger vue components so we can globalize them 61 | * @type {String} 62 | */ 63 | components: 'src', 64 | 65 | /** 66 | * Tells us what folder your smaller vue components are in 67 | * @type {String} 68 | */ 69 | partials: 'src/partials', 70 | 71 | /** 72 | * Show us what folder we should copy your static assets from 73 | * This is also the folder that we will save optimized images to 74 | * @type {String} 75 | */ 76 | static: 'src/static', 77 | 78 | /** 79 | * Name of the folder where you will keep fullsized images to be optimized by `npm run optimize` 80 | * @type {String} 81 | */ 82 | images: 'unoptimized-images', 83 | 84 | /** 85 | * Show us what folder you want us to save your generated files in 86 | * @type {String} 87 | */ 88 | output: 'dist', 89 | 90 | /** 91 | * Tells us where your markdown files are 92 | * @type {String} 93 | */ 94 | markdown: 'markdown' 95 | }, 96 | 97 | /** 98 | * For each route, list the url and the template that should be used to render that route 99 | * 100 | * Write the name of the .vue file found in your templates folder as the component string 101 | * 102 | * The path will attempt to match the natural folder structure of the markdown folder unless you specifically override the path in the url metadata of the markdown file 103 | */ 104 | routes: [ 105 | { 106 | path: "/", 107 | component: "home" 108 | }, 109 | { 110 | path: "/404", 111 | component: "missing" 112 | }, 113 | { 114 | path: "/:page?", 115 | component: "page" 116 | }, 117 | { 118 | path: "/articles/:article?", 119 | component: "post" 120 | }, 121 | { path: "*", redirect: "/404" } 122 | ], 123 | 124 | /** 125 | * Data that will be available across all your templates 126 | */ 127 | store: { 128 | file: {}, 129 | files: [], 130 | social_url: site_url, 131 | commentsEnabled: false 132 | }, 133 | 134 | /** 135 | * Based on `markdown-it-gallery` 136 | */ 137 | markdown_gallery: { 138 | galleryClass: 'gallery', 139 | galleryTag: 'div', 140 | imgClass: 'gallery-image', 141 | wrapImagesInLinks: true, 142 | linkClass: 'gallery-link', 143 | linkTarget: '_blank', 144 | imgTokenType: 'image', 145 | linkTokenType: 'link', 146 | imageFilterFn: token => /galleries\//.test(token.attrGet('src')), 147 | // imageSrcFn: token => token.attrGet('src').replace(/(\.\w+$)/, '$1'), 148 | // linkHrefFn: token => token.attrGet('src').replace(/(\.\w+$)/, '$1'), 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /the-magic/boot/facebook-social.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import config from "config"; 3 | 4 | /** 5 | * We only want to enable facebook comments if we are client-side and we haven't already initialized this script 6 | */ 7 | if (!Vue.prototype.$isServer && !window.fbAsyncInit) { 8 | 9 | /** 10 | * If you run `enableComments()` in your code it will download the remote facebook script to enable facebook comments and likes on your pages 11 | */ 12 | window.enableComments = function() { 13 | (function(d, s, id) { 14 | var js, 15 | fjs = d.getElementsByTagName(s)[0]; 16 | js = d.createElement(s); 17 | js.id = id; 18 | js.src = "https://connect.facebook.net/en_US/all.js"; 19 | 20 | if (d.getElementById(id)) { 21 | //if 130 | -------------------------------------------------------------------------------- /the-magic/build/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Renders all markdown files into html files and injects webpack assets 3 | * Also renders sitemap.xml and feed.xml to the designated output folder 4 | */ 5 | 6 | const isProd = process.env.NODE_ENV === "production"; 7 | const path = require("path"); 8 | const url = require("url"); 9 | const fs = require("fs"); 10 | const { createBundleRenderer } = require("vue-server-renderer"); 11 | const mkdirp = require("mkdirp"); 12 | const config = require("../../site.config"); 13 | const webpack = require('webpack'); 14 | const { renderMarkdownFolder } = require("./render-markdown.js"); 15 | const folders = require('./folders.js'); 16 | 17 | const sitemap_template = ` 18 | 19 | 20 | `; 21 | 22 | const feed_template = ` 23 | 24 | 25 | ${config.site_title} 26 | ${config.author} 27 | ${config.site_url} 28 | ${config.description} 29 | 30 | 31 | 32 | `; 33 | 34 | function createFile(url, html) { 35 | url += !path.extname(url) ? ".html" : ""; 36 | 37 | url = path.join(folders.output_folder, url); 38 | 39 | mkdirp(path.dirname(url), function(err) { 40 | if (err) return cb(err); 41 | fs.writeFile(url, html, err => { 42 | if (err) { 43 | return console.log(err); 44 | } 45 | console.log(`${url.replace(__dirname, "")} was created.`); 46 | }); 47 | }); 48 | } 49 | 50 | const serverBundlePath = path.join( 51 | folders.output_folder, 52 | "vue-ssr-server-bundle.json" 53 | ); 54 | 55 | let ssrClientBundlePath = path.resolve( 56 | folders.output_folder, 'vue-ssr-client-manifest.json' 57 | ) 58 | 59 | if (!fs.existsSync(serverBundlePath)) { 60 | throw new Error(`vue-ssr-server-bundle.json should be in the designated output folder`) 61 | return false 62 | } 63 | 64 | const template = fs.readFileSync(folders.html_template, 'utf-8') 65 | const bundle = require(serverBundlePath) 66 | 67 | // The client manifests are optional, but it allows the renderer 68 | // to automatically infer preload/prefetch links and directly add