Cookies are used for the best experience on my website.

Accept Cookie Policy

No internet detected

Check your connection and try again.

Logo Image

No match found

Buy a coffee

I launched this blog in 1995. Since then, we have published 1603 articles. It's all free and means a lot of work in my spare time. I enjoy sharing knowledge and experiences with you.

Your support

Have you learned something new by reading, listening, or watching my content? With your help, I can spend enough time to keep publishing great content in the future.

Or, select an option below:

A small slice of my data processing time each month

It's ongoing work running this site and what's really great is ongoing support. Here's a sense of what goes into this site: research topics for discussion. Manage the Tech stuff: website, SEO, graphics, email, back-end servers, DNS routing, edge servers. Create advertisements and load the campaigns in Google Ads. Manage the social media forums (Facebook, Reddit, Twitter). Write updates to the blog. Keep GitHub up-to-date.

$4.50 — A large cappuccino at my local

Things just work better with coffee! I like to take the kids to school then grab a cappuccino from my local on the way home before beginning the things that take mental energy.

$8.99 — A month of Netflix for some quiet nights in

A lot of the work on this happens after hours when I should be relaxing on the couch. Help me make it so and I promise to keep off the devices for a bit!

$11.50 — Fund a month of email delivery

This site sends out thousands of emails every month. For that volume and to ensure deliverability, I need to pay MailChimp.

$20 — Pay for one month of AWS storage fees

Websites are not free. The storage alone takes some cash. If you are willing to lighten the burden, we can keep this site up online.

$30 — One hour's pay for a graphics artist

Art doesn't create itself without a hand to guide it. I can't draw, so I need to pay others to help.

$45 — Pay a full-stack web developer for one hour

Much of the work on this site happens on weekends which means giving up time with the kids. Help me pay the developers so I can give my kids more time.

Setup Visual Studio Code for Hexo

Arguably one of the best (and free) coding editors is ⋯


Richie Bartlett, Jr.

  • 3818

  • 29575

  • 0

  • 0

  • 11

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.

Be sure that VScode has the install path defined for the command line.

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? 🔗

Hexo is a fast, simple and powerful blog framework. You write posts in Markdown (or other markup languages) and Hexo generates static files with a beautiful theme in seconds.

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.

The global config needs to be updated to use my custom theme. Here's what I use for version 2022.04:```yml _config.yml###################################################### HEXO CONFIGURATION## ## Docs: Source: ##################################################### Sitetitle: Blog.RichieBartlett.comsubtitle: 'Father | Entrepreneur | Solutions Architect'description: 'Richie is an Entrepreneur, Technologist and Cloud Architect. Loves how technology brings the promise of an easier life, but enjoys the challenge to make it work. Work achievements, play, & fatherhood are the things that drive Richie.'keywords: - Richie Bartlett - Technologist - Entrepreneur - BlockChain Developer - LEGO - AFOL - Solutions Architect - Cloud Architect - IT Managerauthor: 'Richie Bartlett, Jr.'## https://ogp.meOpenGraph: ## default values for site [can be overridden by `page.OpenGraph` values] image: imageAlt: 'Richie Bartlett' facebook: admins: false app_id: false ## twitter: ## image: card: 'summary_large_image' ## [“summary”, “summary_large_image”, “app”, or “player”] creator: '@LoreZyra' site: '@LoreZyra' domain: '' siteName: ''## ISO 639-1 Codelanguage: - en #English - ja #Japanese - ar #Arabic - zh-TW # Traditional Chinese - zh-CN # Simplified Chinese## IANA time zonetimezone: 'Asia/Tokyo'## URL## Set your site url here. For example, if you use GitHub Page, set url as ''url: https://blog.RichieBartlett.comroot: /## :title/permalink_defaults: lang: enpretty_urls: trailing_index: false # Set to false to remove trailing 'index.html' from permalinks trailing_html: false # Set to false to remove trailing '.html' from permalinks# Directorysource_dir: sourcepublic_dir: publiccode_dir: codei18n_dir: lang skip_render: - _posts/**/*.json - _posts/**/*.yml - _posts/**/*.txt - _posts/**/*.js - _posts/**/*.css - _posts/**/*.xml - _posts/**/*.svg - _posts/**/*.ai - _posts/**/*.pdf - _posts/**/*.zip - themes/**/*.yml - themes/**/*.json# Writingnew_post_name: # File name of new postsdefault_layout: posttitlecase: true # Transform title into TitleCaseexternal_link: enable: true # Open external links in new tab field: site # Apply to the whole site exclude: nullfilename_case: 0render_drafts: falsepost_asset_folder: truemarked: prependRoot: true postAsset: truerelative_link: falsefuture: truemarkdown: preset: default render: html: true xhtmlOut: true langPrefix: 'language-' breaks: true linkify: true typographer: true quotes: '“”‘’「」' enable_rules: disable_rules: plugins: null anchors: level: 2 collisionSuffix: null permalink: false permalinkClass: 'header-anchor' permalinkSide: 'left' permalinkSymbol: '¶' case: 0 separator: '-'# Code Highlightshighlight: enable: true line_number: true auto_detect: false tab_replace: ' ' wrap: true hljs: trueprismjs: enable: false preprocess: true line_number: true tab_replace: ' '# Home page setting# path: Root path for your blogs index page. (default = '')# per_page: Posts displayed per page. (0 = disable pagination)# order_by: Posts order. (Order by date descending by default)index_generator: path: '/' per_page: 15 ## <<<< This will be used as the home feed pagination order_by: -datearchive_generator: ## enabled: true per_page: 30 yearly: true monthly: true daily: false order_by: -datearchive_dir: archivecategory_generator: ## per_page: 75 order_by: namecategory_dir: categorycategory_map:# Generate categories from directory-tree# Dependencies: depth: the max_depth of directory-tree you want to generate, should > 0auto_category: enable: true depth: falsedefault_category: uncategorized## Set per_page to `false` to disable paginationtag_generator: ## per_page: 20 order_by: nametag_dir: tagtag_map:# Paginationpagination_dir: page# Metadata elements## true# Date / Time format## Hexo uses Moment.js to parse and display date## You can customize the date format as defined in## YYYY-MMM-DDtime_format: HH:mm:ssupdated_option: mtime ## updated_option supports: mtime | date | empty# Include / Exclude file(s)## include:/exclude: options only apply to the 'source/' folderinclude: nullexclude: nullignore: null## Themes: <<<<<<<<< Extensions >>>>>>>>>>>>>>>>## Plugins: # # # # # # # path: sitemap.xml tag: true category: true## path: search.xml field: post content: true root: / # template: ./search.xmldatabase: path: db.xml fields: [post]## enable: true type: ['atom', 'rss2'] path: [atom.xml, rss2.xml] limit: 20 hub: content: content_limit: 140 content_limit_delim: ' ' order_by: -date icon: icon.png autodiscovery: true template:## tags: all # none # or 'ams' or 'all' single_dollars: true # enable single dollar signs as in-line math delimiters cjk_width: 0.9 # relative CJK char width normal_width: 0.6 # relative normal (monospace) width append_css: true # add CSS to pages rendered by MathJax every_page: false # if true, every page will be rendered by MathJax regardless the `mathjax` setting in Front-matter## location_target_class: FootnoteTargetadvertiser: ```
Be sure to update the blog domain to your site.

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 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.

```md /scaffolds/ Setup Visual Studio Code for Hexo ### Review: Work_仕事/Setup-VScode-for-Hexodate: 1670254048000 ## PUBLISHED dateupdated: 1670254048000 ## when you last updated this posttags: ## feature to link posts across multiple categories - draft# categories: {folder name} ## this is replaced by the HEXO plug-in 999 ## Sticks a post below the recommended section in order specified <#>; "1" will be at the very topcomments: true #allow comments integration? See theme/_config.ymllang: en ## 2-digit language code [+ 2-digit country code]draft: drafttoc: true ## display the table of contents?tocOpen: true ## display the ToC open?indexing: false ## post is searchable?display_tag_onHome: true # display tags for this post?swiper: false ## show on home page swiper?# swiperDesc: '我是文章在轮播图中的摘要' ## typewriter text under post.title# swiperImg: '/img/1.jpg' ## Carousel background imagerecommendedSection: false ## post appears in Recommended articles section on home pagedonate: false ## display the tip button?geolocation: Planet Earthmathjax: false ## post has LaTeX code?share: false ## display social media connection at end of article### when copy&paste from other source: be sure to credit the original Author!##,community%2C%20provided%20it%20remains%20Libre.## right: true ## display copyright notice on post left: false ## Unicode Character 'COPYLEFT SYMBOL' (U+1F12F) License: type: false ## declare a different license than the global default set in the _config.yml url: false ## link to the online license definitions desc: false ## text to briefly describe the licenseorigin: url: false ## if not `false`, point to SOURCE where post was copied author: name: false ## if `false`, defaults to global config for `author` img: false ## if `false`, defaults to global config for `image` published: false ## date the original article was postedexcerpt: '' ## this blurb is what's displayed on the article summary from the home page/feed. # img: /img/1.jpg ## article card image @ page bannerimg: ''openGraph_img: false #[PNG/JPG]type: HEXO/postsitemap: false ## set to `false` to exclude from search engine sitemap---```

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 - “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": "", "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](", "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]( }", "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", "": [{ "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, "": "HEXO", "frontMatter.framework.startCommand": "hexo clean && hexo server", "": "", "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.{{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
Git buffer requirements will depend on the amount of RAM available to your machine.

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=1

git gc
git fsck

Basic blog workflow 🔗

  1. 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"
  2. 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.
  3. 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.
  4. Build the site and make sure no errors are found.
  5. Commit the updates to git and push to my repo.
  6. Assuming my Ci/Cd automation is working, then I should see the page on my live website within 10 minutes.

Reference 🔗

This license allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, so long as attribution is given to the creator. The license allows for commercial use. If you remix, adapt, or build upon the material, you must license the modified material under identical terms.