DEV Community

loading...
Cover image for How to write a blog publish link CLI with Yarrrgs!

How to write a blog publish link CLI with Yarrrgs!

aexol profile image Artur Czemiel Originally published at blog.graphqleditor.com ・4 min read

Introduction

Hello, today I will write a little bit about tooling. 2 weeks ago I thought about starting this blog. I asked my friend who blogs what blog engine should I use. His answer was rather surprising as he told me I should have used static website and commit blog posts as pull requests to it :O.

Searching for the perfect solution

So, I started exploring the internet to find the best blog layout ( reactjs ) tool to write my static blog. I was able to dig out gatsby starter blog from hundreds of Chinese repos which GitHub is flooded with right now. At the moment I created this blog I had no experience with Gatsby ecosystem but it looked promising for me.

Running

Running this kind of Gatsby package required me only to install gatsby-cli and run the command gatsby develop. Pretty easy? Huh?

Modifications

I decided to add some tweaks to this simple blog package as it was a really pure blog with one author. So to add other authors( who I don't have yet :( ) I added authors folder, so to add you as an author you need to create a folder with your name and create index.js file with this kind of content inside it:

export const Artur = {
  photo: require('./Artur.jpeg'),
  desc:
    'GraphQL passionate. Code generation guru. Short code lover. Father. CTO. CEO.',
  name: 'Artur Czemiel',
  email: 'aexol@aexol.com',
}
Enter fullscreen mode Exit fullscreen mode

and add of course this line to authors/index.js :

import { Artur } from './Artur'
export const Authors = {
  Artur,
}
Enter fullscreen mode Exit fullscreen mode

Later on, you can use it inside your blog post.

How to add your blog post

Adding your blog post is pretty easy though. Again you have to create a folder inside pages folder with blog post slug-like my-very-interesting-article. Add an index.md file to it with this kind of header which is formatted by graymatter package then:

--------
title: My very interesting article
date: '2018-10-27T13:23:04.284Z'
author: Artur
--------
Enter fullscreen mode Exit fullscreen mode

ł
That's it. After writing the article you just submit pull request to your fork. I merge the pull request and publish your article to the website.

Publish tools

Sometimes I am kinda lazy person. I added small publish CLI to this project, which automatically using opn opens the browser with prefilled URL and title fields for: reddit LinkedIn twitter hackernews. So it is much easier to share your blog posts from this blog. It lives in bin/index.js folder of this blog and uses yargs and inquirer and graymatter which I mentioned before.

Here is the code

const yargs = require('yargs')
const fs = require('fs')
const inquirer = require('inquirer')
const opn = require('./opn')
const matter = require('gray-matter')

const HOSTNAME = 'https://blog.graphqleditor.com'
const pagesDirectory = __dirname + '/../src/pages'
const reddits = [
  'typescript',
  'javascript',
  'reactnative',
  'reactjs',
  'reactxp',
  'node',
  'webdev',
  'graphql',
  'programming',
  'technology',
  'startups',
  'learnprogramming',
  'marketing',
  'entrepreneur',
  'golang',
  'ruby',
  'dotnet',
  'java',
  'python'
].sort()
const voats = ['technology']
const mediums = [
  {
    name: 'voat',
    fn: ({ url, title }) =>
      inquirer
        .prompt([
          {
            type: 'list',
            name: 'voat',
            message: 'What voat you would like to publish to?',
            choices: voats,
          },
        ])
        .then(answers =>
          submit({
            medium: 'voat',
            reddit: answers.voat,
            title,
            url,
          })
        ),
  },
  {
    name: 'reddit',
    fn: ({ url, title }) =>
      inquirer
        .prompt([
          {
            type: 'list',
            name: 'reddit',
            message: 'What reddit you would like to publish to?',
            choices: reddits,
          },
        ])
        .then(answers =>
          submit({
            medium: 'reddit',
            reddit: answers.reddit,
            title,
            url,
          })
        ),
  },
  {
    name: 'hackerNews',
    fn: ({ url, title }) =>
      submit({
        medium: 'hackerNews',
        title,
        url,
      }),
  },
  {
    name: 'linkedIn',
    fn: ({ url, title }) =>
      submit({
        medium: 'linkedIn',
        title,
        url,
      }),
  },
  {
    name: 'twitter',
    fn: ({ url, title }) =>
      submit({
        medium: 'twitter',
        title,
        url,
      }),
  },
]
const submit = ({ medium, title, url, reddit }) =>
  opn(
    {
      voat: `https://voat.co/submit?linkpost=true&title=${title}&url=${url}&subverse=${reddit}`,
      hackerNews: `http://news.ycombinator.com/submitlink?u=${url}&t=${encodeURIComponent(
        title
      )}`,
      reddit: `https://www.reddit.com/r/${reddit}/submit?title=${encodeURIComponent(
        title
      )}&url=${url}`,
      linkedIn: `https://www.linkedin.com/shareArticle?mini=true&url=${url}&source=${encodeURIComponent(
        HOSTNAME
      )}`,
      twitter: `http://twitter.com/share?url=${url}&text=${encodeURIComponent(
        title
      )}`,
    }[medium],
    {
      wait: false,
    }
  )

const argv = () =>
  yargs
    .command(
      'publish',
      'Publish your blog post for different mediums',
      {},
      async argv => {
        const pageNames = fs
          .readdirSync(pagesDirectory)
          .filter(page =>
            fs.lstatSync(`${pagesDirectory}/${page}`).isDirectory()
          )
        const pages = pageNames.map(page => ({
          url: `${HOSTNAME}/${page}`,
          title: matter(
            fs.readFileSync([pagesDirectory, page, 'index.md'].join('/'))
          ).data.title,
        }))
        inquirer
          .prompt([
            {
              type: 'list',
              name: 'page',
              message: 'Which page would you like to publish',
              choices: pages.map(p => p.title),
            },
          ])
          .then(answers => pages.find(p => p.title === answers.page))
          .then(page =>
            inquirer
              .prompt([
                {
                  type: 'list',
                  name: 'medium',
                  message: 'What medium you would like to publish to?',
                  choices: mediums.map(m => m.name).concat('All mediums'),
                },
              ])
              .then(
                answers =>
                  answers.medium === 'All mediums'
                    ? Promise.all(
                        mediums
                          .filter(m => m.name !== 'reddit' && m.name !== 'voat')
                          .map(m => m.fn(page))
                      )
                    : mediums
                        .find(m => m.name === answers.medium)
                        .fn(page)
                        .then(proc =>
                          inquirer
                            .prompt([
                              {
                                type: 'list',
                                name: 'again',
                                message: 'What next?',
                                choices: ['Exit', 'Next Action'],
                              },
                            ])
                            .then(({ again }) => {
                              if (again === 'Exit') {
                                return
                              }
                              return argv()
                            })
                        )
              )
          )
      }
    )
    .help().argv
argv()

Enter fullscreen mode Exit fullscreen mode

After that, it opens a window so I can post on Reddit. Simple and beautiful!

Discussion (0)

pic
Editor guide