Intro 🔗
This is part one of a mini series on my journey to build my own blogging site without using WordPress or any blogging site like Tumbler or Medium. I want full control of my published thoughts. This includes if (or how) I should share my data with advertisers and crawlers.
I assume you are comfortable with using git, VScode, and markdown. There are other sites that discuss how to get started with GitHub and programming.
Getting Started in VScode 🔗
Arguably one of the best (and free) coding editors is Microsoft’s Visual Studio Code. It has a huge marketplace for extensions (or plugins) to handle any popular framework and programming language.
Download & install vscode:
Install extensions 🔗
These are the extensions I use to create blog posts and update my blog’s theme.
Customize your VScode environment 🔗
I typically setup the editor with my favorite color theme and set the wrap length (based on my screen width) plus font sizes. You’ll want it comfortable to your eyes.
update your settings.json
for ejs 🔗
To support EJS troubleshooting and code highlighting, add the following to your .vscode/settings.json
configuration file.
"files.associations": {
"*.ejs": "html"
},
"emmet.includeLanguages": {
"ejs": "html",
},
"html.format.templating": true
This will enable code highlighting similar to JavaScript regardless if it’s inline or a separate file.
What is HEXO? 🔗
This entire site is built on the Hexo framework with a heavily customized theme of my own design. The reason I selected Hexo is that it’s built on NodeJS. Having both the frontend and backends written in JavaScript makes management of the website far easier. Plus, JavaScript remains a very popular scripting language with easy to find affordable help to enhance it further. I recall the pain of managing several programming languages for a single project. This blog only requires CSS, HTML, and JavaScript. Even the backend on AWS is simple JavaScript.
I’ve spent the past 18 months digging into the framework and making a few minor contributions to the project. I’m now very comfortable with how it works and how to enhance it with custom tags and scripting.
Hexo Tutorial 🔗
setup HEXO environment 🔗
After you download the Hexo package to your project folder, you’ll need to overwrite some files as I define below.
config 🔗
Need to setup the global config for HEXO and the FrontMatter CMS in VScode.
scaffolds 🔗
Scaffolds are essentially template files with default values defined in the front-matter section of the markdown file. When you specify hexo new draft ...
on the command line, you are telling Hexo to use the draft.md
file in the /scaffolds/
folder. My theme has some expected values and I define them well in the scaffolds. You’ll need to copy these files to your project folder.
basic commands 🔗
As your blog grows in size, you’ll notice that it starts to take longer to build the site. This is because Hexo is comparing the posts with the db.json
file it builds each run. I found it much faster to force a clean run by adding hexo clean
to the command before building or testing.
When I’m testing the site locally, I simply run:
hexo clean && hexo server
After confirming that everything looks as expected, I’ll build the site:
hexo clean && hexo gc -w=100 && hexo gen
This will clear the database file, extract the calendar data for 100 weeks, and generate the site.
Node heap memory 🔗
As more advanced features are added to the theme, the amount of memory required to process it will increase. Here’s the command to increase Node’s memory allocation.
export NODE_OPTIONS=--max-old-space-size=8192
This will increase the memory buffer to 8GB. For my project with over 1400 posts, I needed to increase it to 14GB before it would compile completely. Needless to say, your hardware will need to accommodate these requirements.
FrontMatter 🔗
Setup FrontMatter plugin:
As of FrontMatter version 8.1.2, it doesn’t support Hexo out-of-the-box, but it does a great job of providing a headless CMS. I requested the creator to add support - frontMatter.framework.id: “HEXO”. Hopefully, you are reading this after a newer version is published.
In the root of your project you’ll need to overwrite this configuration file.
🧑🏻💻 frontmatter.json
{ "$schema": "https://frontmatter.codes/frontmatter.schema.json", "frontMatter.taxonomy.contentTypes": [{ "name": "default", "pageBundle": false, "previewPath": null, "fields": [{ "title": "Title", "name": "title", "type": "string" }, { "title": "Description", "name": "description", "type": "string" }, { "title": "Publishing date", "name": "date", "type": "datetime", "default": "", "isPublishDate": true }, { "title": "Content preview", "name": "preview", "type": "image" }, { "title": "Is in draft", "name": "draft", "type": "choice", "choices": ["draft", "in progress", "published"] }, { "title": "Tags", "name": "tags", "type": "tags" }, { "title": "Categories", "name": "categories", "type": "categories", "hidden": true } ] }, { "name": "HEXO/post", "pageBundle": false, "previewPath": null, "fields": [{ "title": "Title", "name": "title", "type": "string", "editable": true, "required": true }, { "title": "Slug", "name": "slug", "type": "slug", "editable": true, "default": "Work_仕事/Setup-VScode-for-Hexo" }, { "title": "Description", "name": "excerpt", "type": "string", "single": false, "editable": true }, { "title": "Keywords", "name": "keywords", "type": "list", "description": "SEO phrase/words for finding this post", "default": false, "editable": true, "single": true, "hidden": true }, { "title": "Publish date", "name": "date", "type": "datetime", "default": "", "isPublishDate": true, "editable": false, "required": true }, { "title": "Updated time", "name": "updated", "type": "datetime", "default": "", "editable": false, "single": true, "isModifiedDate": true }, { "title": "Tags", "name": "tags", "type": "tags", "description": "Tags should be limited to a absolute max of 7; Ideally 3...", "taxonomyLimit": 10, "editable": true }, { "title": "Categories", "name": "categories", "type": "categories", "description": "this is replaced by the HEXO plug-in [hexo-auto-category](https://github.com/xu-song/hexo-auto-category)", "hidden": true }, { "title": "Related Posts", "name": "related", "type": "list", "description": "List of related articles. (Use {SLUG} only!) Ideally 2~4 links recommended.", "taxonomyLimit": 5, "editable": true }, { "title": "Post Image (PNG / JPG / SVG)", "name": "img", "editable": true, "type": "image", "description": "This is the main photo displayed on the website. SVG is recommended, but sometimes not possible if too complex.", "isPreviewImage": true }, { "title": "OpenGraph image (JPG / PNG)", "name": "openGraph_img", "type": "image", "description": "This is the image used by FB, Twitter, or any social media site. SVG _not_ supported.", "default": false, "editable": true }, { "title": "Post Language", "name": "lang", "type": "string", "description": "{ [ISO 639-1 Code](https://en.wikipedia.org/wiki/ISO_639-1) }", "default": "en", "editable": true, "single": true, "required": true }, { "name": "divider", "type": "divider" }, { "title": "Swiper Post?", "name": "swiper", "description": "Enable if you want to give PRIORITY to this post by displaying in the SWIPER section. Best to limit this to post that users will swipe left or right to view. Most post will be displayed by scrolling down.", "type": "boolean", "default": false, "editable": true, "single": true }, { "title": "Pin this post at top?", "name": "sticky", "description": "Select a priority between 1 and 98 (1 = very top) for this post to be displayed.", "type": "number", "default": 999, "editable": true, "single": true }, { "title": "Enable ToC?", "name": "toc", "type": "boolean", "description": "Display the Table of Contents?", "default": true, "editable": true, "single": true }, { "title": "Default ToC open?", "name": "tocOpen", "type": "boolean", "description": "Should the Table of Contents be open already?", "default": true, "editable": true, "single": true }, { "title": "Allow indexing and search for this post's contents?", "name": "indexing", "type": "boolean", "description": "Enable for internal site's search engine.", "default": true, "editable": true, "single": true }, { "title": "Add to sitemap?", "name": "sitemap", "type": "boolean", "description": "Enabling this allows search engines to be aware of your articles/content.", "default": true, "single": true, "editable": true }, { "title": "Display tags on Home?", "name": "display_tag_onHome", "type": "boolean", "description": "Show the tags for this post in the feed pages?", "default": true, "editable": true, "single": true }, { "title": "Show in top recommended section on home page?", "name": "recommendedSection", "type": "boolean", "description": "Display this post in the recommended section below the top 'HERO' banner?", "default": false, "editable": true, "single": true }, { "title": "Show donate button?", "name": "donate", "type": "boolean", "description": "This is great for crowd funding your work. However, don't enable this for copy/paste news reports.", "default": false, "editable": true, "single": true }, { "title": "Enable MathJax", "name": "mathjax", "type": "boolean", "description": "If using math formulas, good to enable this.", "default": false, "editable": true, "single": true }, { "title": "Show share icons?", "name": "share", "type": "boolean", "description": "Display the SNS icons or bot to share this post.", "default": true, "editable": true, "single": true }, { "title": "Enable comments?", "name": "comments", "type": "boolean", "description": "Enable DISQUS tool for comments?", "default": true, "editable": true, "single": true }, { "title": "COPY", "name": "copy", "type": "fields", "fields": [{ "title": "Display copyright notice on post?", "name": "right", "type": "boolean", "description": "Should enable this if copied from other _copyrighted_ source... Or, if you have copyrighted it, then enable.", "default": true, "editable": true, "single": true }, { "title": "Display copyleft notice on post?", "name": "left", "type": "boolean", "description": "_copyleft_? Unicode Character 'COPYLEFT SYMBOL' (U+1F12F)", "default": false, "editable": true, "single": true }, { "title": "License", "name": "License", "type": "fields", "fields": [{ "title": "License Type", "name": "type", "description": "Declare a different license than the global default set in the _config.yml", "type": "string", "default": false, "editable": true, "single": true }, { "title": "URL of License", "name": "url", "description": "Link to the online license definitions", "type": "string", "default": false, "editable": true, "single": true }, { "title": "Description of License", "name": "desc", "description": "Briefly describe the license", "type": "string", "default": false, "editable": true, "single": true } ] } ] }, { "title": "origin", "name": "origin", "description": "Original post where this was copy/pasted.", "type": "fields", "fields": [ { "title": "URL", "name": "url", "description": "Original post where this was copy/pasted.", "type": "string", "editable": true, "default": false, "single": true }, { "title": "Author", "name": "author", "type": "fields", "fields": [ { "title": "Name", "name": "name", "description": "{First_name} {LAST_NAME} -or- '@username'", "type": "string", "default": false, "editable": true, "single": true }, { "title": "Image", "name": "img", "description": "URL to the author's image", "type": "string", "default": false, "editable": true, "single": true } ] }, { "title": "Publish Date", "name": "published", "type": "datetime", "default": false, "editable": true, "single": true } ] }, { "title": "Location on Earth", "name": "geolocation", "type": "string", "description": "Provide either a coordinate or text string that Google Maps can display. Where did you write this post?", "default": "'Chiba, Japan'", "editable": true, "single": true }, { "title": "Read Time (autogenerated)", "name": "readTime", "type": "number", "editable": false, "default": false, "single": true, "hidden": true }, { "title": "Word Count (autogenerated)", "name": "wordCount", "type": "number", "default": false, "editable": false, "single": true, "hidden": true }, { "name": "divider", "type": "divider" }, { "title": "Publish it?", "name": "published", "type": "boolean", "description": "Display the post in feed?", "default": false, "editable": true, "single": true }, { "title": "Draft Status", "name": "draft", "type": "choice", "choices": ["draft", "in progress", "published"], "default": "draft", "single": true, "editable": true }, { "title": "type", "name": "type", "type": "string", "default": "HEXO/post", "hidden": true } ] }, { "name": "HEXO/page", "pageBundle": true, "fields": [{ "title": "Title", "name": "title", "type": "string", "editable": true, "required": true }, { "title": "Description", "name": "description", "type": "string", "editable": true }, { "title": "Page Image", "name": "img", "editable": true, "type": "string", "isPreviewImage": true }, { "title": "Publish date", "name": "date", "type": "datetime", "default": "", "isPublishDate": true, "editable": false, "required": true }, { "title": "layout", "name": "layout", "type": "string", "default": "page" }, { "title": "Allow indexing and search for this post's contents?", "name": "indexing", "type": "boolean", "default": true, "editable": true, "single": true } ] } ], "frontMatter.taxonomy.tags": [], "frontMatter.taxonomy.categories": [], "frontMatter.content.defaultFileType": "md", "frontMatter.content.fmHighlight": true, "frontMatter.content.hideFm": false, "frontMatter.content.pageFolders": [{ "title": "Page", "path": "[[workspace]]/source", "excludeSubdir": true }, { "title": "Post", "path": "[[workspace]]/source/_posts" }, { "title": "Draft", "path": "[[workspace]]/source/_drafts" } ], "frontMatter.content.publicFolder": "[[workspace]]/public", "frontMatter.data.folders": [{ "id": "HEXO/data", "title": "Custom data", "file": "[[workspace]]/source/data/*.json", "fileType": "json" }, { "id": "sponsors", "title": "Sponsors", "file": "[[workspace]]/source/data/sponsors.json", "fileType": "json", "labelField": "name", "schema": { "title": "Sponsors", "type": "object", "required": [ "name", "url" ], "properties": { "name": { "type": "string", "title": "Name" }, "url": { "type": "string", "title": "URL" }, "description": { "type": "string", "title": "Description" } } } } ], "frontMatter.file.preserveCasing": true, "frontMatter.framework.id": "HEXO", "frontMatter.framework.startCommand": "hexo clean && hexo server", "frontMatter.preview.host": "http://127.0.0.0:4000/", "frontMatter.preview.pathName": "Work_仕事/Setup-VScode-for-Hexo/", "frontMatter.taxonomy.dateFormat": "YYYY/MM/dd", "frontMatter.taxonomy.frontMatterType": "YAML", "frontMatter.taxonomy.seoContentLength": 2400, "frontMatter.taxonomy.indentArrays": true, "frontMatter.taxonomy.fieldGroups": [], "frontMatter.templates.enabled": true, "frontMatter.templates.folder": ".frontmatter/templates", "frontMatter.templates.prefix": "", "frontMatter.content.draftField": { "name": "draft", "type": "choice", "choices": ["draft", "in progress", "published"] }, "frontMatter.dashboard.content.pagination": true }
Once you’ve updated FrontMatter’s configuration, you can click on the FM tab in VScode to see the panel now supports HEXO with my custom theme.
Setup GitHub 🔗
This repo uses LFS 🔗
Run the following command to enable proper authentication with GitHub LFS servers:
git config lfs.https://github.com/{{username}}/{{blog_project_name}}.git/info/lfs.access basic
Update Git Buffers 🔗
It appears that the default git buffer is too small for sites with large posts or video files. Need to set it to the max:
git config --global http.postBuffer 2048M
git config --global http.maxRequestBuffer 1024M
git config --global core.compression 9
git config --global ssh.postBuffer 2048M
git config --global ssh.maxRequestBuffer 1024M
git config --global pack.windowMemory 512M
git config --global pack.packSizeLimit 512M
Support long filenames 🔗
This issue affects mostly Windows users. You may need to run as Administrator
for it to be effective.
git config --system core.longpaths true
git config --global core.longpaths true
Debug options 🔗
Sometimes you see issues with your push to remote and it helps to troubleshoot it with the following commands.
export GIT_TRACE_PACKET=1
export GIT_TRACE=1
export GIT_CURL_VERBOSE=1
git gc
git fsck
Basic blog workflow 🔗
- Find topic for discussion. I often see an image of something that sparks an idea to discuss. As I do everything on my MacBookPro, MacOS has case sensitive filenames. So, I try to be mindful of this when creating the blog title. If I start the title in all lowercase, then the filename will be lowercase too. From the command line, create a new draft:
hexo new draft "My new idea or thought"
- View the basic preview within VScode until I’m ready to preview it with the theme. FrontMatter can run the HEXO server for you and display the preview live within VScode.
- When I’m satisfied with the article, I’ll update the draft and publish status in FrontMatter panel. I’ll also review all the options for the article to ensure I have the best keywords, tags, related posts, description, etc.
- Build the site and make sure no errors are found.
- Commit the updates to git and push to my repo.
- Assuming my Ci/Cd automation is working, then I should see the page on my live website within 10 minutes.
Reference 🔗
- Running Visual Studio Code on macOS
- How can you export the Visual Studio Code extension list?
- How to export your VS Code extensions from terminal
- How to use Github and Vercel to Deploy a Hexo Blog and Config it
- https://support.snyk.io/hc/en-us/articles/360002046418-JavaScript-heap-out-of-memory
- https://stackoverflow.com/questions/22575662/filename-too-long-in-git-for-windows
- https://git-scm.com/docs/git-gc
- https://git-scm.com/docs/git-fsck