{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/a-handy-npm-script-for-creating-a-new-gatsby-blog-post",
    "result": {"data":{"site":{"siteMetadata":{"title":"your friend Joel's digital garden","description":"Articles and notes from a collaborator at egghead.io. Musings on software, business, and life from a skilled virtual assistant.","author":{"name":"Joel Hooks"},"keywords":["Video Blogger"]}},"mdx":{"excerpt":"This blog is built with Gatsby and uses MDX for the post content. It's a great setup, and so far I've enjoyed using it very much. One thing I missed from my old Octopress/Jekyll configuration was the ability to run a rake task to create a…","fields":{"github":"https://github.com/joelhooks/joelhooks-com/tree/master/content/blog/2019-01-30-a-handy-npm-script-for-creating-a-new-gatsby-blog-post/index.mdx"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"slug\": \"a-handy-npm-script-for-creating-a-new-gatsby-blog-post\",\n  \"date\": \"2019-01-30T00:00:00.000Z\",\n  \"title\": \"A handy npm script for creating a new Gatsby blog post\",\n  \"published\": true\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"This blog is built with Gatsby and uses MDX for the post content. It's a great setup, and so far I've enjoyed using it very much.\"), mdx(\"p\", null, \"One thing I missed from my old Octopress/Jekyll configuration was the ability to run a rake task to create a new post with all of the basic required frontmatter metadata filled in.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-md\"\n  }, \"---\\nslug: a-handy-npm-script-for-creating-a-new-gatsby-blog-post~M3MyBb6Fd\\nguid: M3MyBb6Fd\\ndate: 2019-01-30\\ntitle: 'A handy npm script for creating a new Gatsby blog post'\\npublished: false\\n---\\n\")), mdx(\"p\", null, \"Here's an example of markdown meta for this post. Gatsby uses this frontmatter when it builds the site to create the appropriate corresponding pages.\"), mdx(\"p\", null, \"It was kind of a pain in the ass to manually type it out every time, so I decided to solve my frustration via the power of code.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"$ npm run newPost \\\"This is the title of my blogpost\\\"\\n\")), mdx(\"p\", null, \"This is the basic structure of the command I want to be able to run from the terminal command line. Once this is executed, a new folder should be created and named appropriately. Inside the folder will be an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.mdx\"), \" file that has all of its basic/required frontmatter inside and all that I need to do is hop in and write the post.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-text\"\n  }, \"/content\\n  - blog\\n    -- 2019-01-30-a-handy-npm-script-for-creating-a-new-gatsby-blog-post\\n      --- index.mdx\\n\")), mdx(\"p\", null, \"This is the result for my setup after running the script.\"), mdx(\"p\", null, \"So let's take a look at how I built it by first looking at what needs to happen.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"execute a node script with arguments\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"parse the arguments\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"extract the title\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"\\\"slugify\\\" the title for use in filenames and the url\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"capture the current date\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"write the file to disk\")), mdx(\"p\", null, \"I made a sub-folder called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"scripts\"), \" and created \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"newPost.js\"), \" inside.\"), mdx(\"p\", null, \"To get the command line arguments we need to access \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"process.argv\"), \" in node. The contents of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"newPost.js\"), \" look like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"console.log(process.argv)\\n\")), mdx(\"p\", null, \"Now we can take a look at what \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"process.argv\"), \" contains by running the following command:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"node ./scripts/newPost.js \\\"this is my test post\\\"\\n\")), mdx(\"p\", null, \"Assuming there are no errors, the output is an array looks like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \";[\\n  '/Users/joel/.nodenv/versions/10.6.0/bin/node',\\n  '/Users/joel/Code/joelhooks-com/test.js',\\n  'this is my test post',\\n]\\n\")), mdx(\"p\", null, \"The contents of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"process.argv\"), \" is an array that contains the location of the node executable that is being used, the location of the script being executed, and finally the argument that we passed in.\"), mdx(\"p\", null, \"We can try again with some more arguments:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"node ./scripts/newPost.js \\\"this is my test post\\\" 1 \\\"gopher\\\"\\n\")), mdx(\"p\", null, \"And you'll see that it simply adds to the array:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \";[\\n  '/Users/joel/.nodenv/versions/10.6.0/bin/node',\\n  '/Users/joel/Code/joelhooks-com/test.js',\\n  'this is my test post',\\n  '1',\\n  'gopher',\\n]\\n\")), mdx(\"p\", null, \"We want to make sure that we actually have an name to work with, so I'm going to check and make sure with a simple if/else guard in my script.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\")), mdx(\"p\", null, \"Try and run it now with no name argument. It should throw an error and crash out.\"), mdx(\"p\", null, \"Now that we have a name, we want to create a \\\"kebab case\\\" slug. Slugs can be a little tricky to get right in all cases, so for this I'm going to use a small library. \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm i slug\"), \" will get me exactly what I need:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const slugify = require('slug')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconsole.log(slugify(title))\\n\")), mdx(\"p\", null, \"This outputs \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"This-is-the-title-of-my-blogpost\"), \" which is close, but typically a slug will be all lowercase, so it will look more like this:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const slugify = require('slug')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconst slug = slugify(title.toLowerCase())\\n\")), mdx(\"p\", null, \"For sorting purposes I also like to add the date to the post's folder name. My goto is the tiny \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"date-fns\"), \" library that provides most of the date/time utility that you'll ever need in a tiny package with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm i date-fns\"), \" and using it as so:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const slugify = require('slug')\\nconst dateFns = require('date-fns')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconst slug = slugify(title.toLowerCase())\\nconst date = dateFns.format(new Date(), 'YYYY-MM-DD')\\n\")), mdx(\"p\", null, \"You could manually extract and format a date, but... who has time for that?? This works great, and I have all the pieces I need to assemble and output my file.\"), mdx(\"p\", null, \"First I'm going to create the path:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const slugify = require('slug')\\nconst dateFns = require('date-fns')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconst slug = slugify(title.toLowerCase())\\nconst date = dateFns.format(new Date(), 'YYYY-MM-DD')\\nconst dir = `./content/blog/${date}-${slug}`\\n\")), mdx(\"p\", null, \"Now I can use the node \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs\"), \", or file system, module to create the folder:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const fs = require('fs')\\nconst slugify = require('slug')\\nconst dateFns = require('date-fns')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconst slug = slugify(title.toLowerCase())\\nconst date = dateFns.format(new Date(), 'YYYY-MM-DD')\\nconst dir = `./content/blog/${date}-${slug}`\\n\\nif (!fs.existsSync(dir)) {\\n  fs.mkdirSync(dir)\\n} else {\\n  throw 'That post already exists!'\\n}\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs\"), \" module needs to be \\\"imported\\\" using require, and then I also added an if/else around the call to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs.mkdirSync(dir)\"), \" to ensure that I wasn't going to overwrite an existing post. Better safe than sorry. Once that runs, you'll see an empty folder is created. Now we can use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs.writeFileSync\"), \" to create the actual file itself:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"const fs = require('fs')\\nconst slugify = require('slug')\\nconst dateFns = require('date-fns')\\n\\nconst title = process.argv[2]\\n\\nif (!title) {\\n  throw 'a title is required!'\\n}\\n\\nconst slug = slugify(title.toLowerCase())\\nconst date = dateFns.format(new Date(), 'YYYY-MM-DD')\\nconst dir = `./content/blog/${date}-${slug}`\\n\\nif (!fs.existsSync(dir)) {\\n  fs.mkdirSync(dir)\\n} else {\\n  throw 'That post already exists!'\\n}\\n\\nfs.writeFileSync(\\n  `${dir}/index.mdx`,\\n  `---\\nslug: ${slug}\\ndate: ${date}\\ntitle: \\\"${title}\\\"\\npublished: false\\n---`,\\n  function(err) {\\n    if (err) {\\n      return console.log(err)\\n    }\\n\\n    console.log(`${title} was created!`)\\n  },\\n)\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs.writeFileSync\"), \" takes three arguments. The first is the path or destination, and the second is the file contents. Since this is modern node, we have access to string template literals using the backticks. This is particularly nice because they allow us to create relatively clean formatted strings that respect whitespace and don't require special linebreak characters.\"), mdx(\"p\", null, \"The final argument is a callback function that is called when the operation is complete. If there is an error it is logged out, and we also get a friendly message if it was a success.\"), mdx(\"p\", null, \"To round this out and make it a bit easier to run, I added a command to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"scripts\"), \" property of my package.json.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"\\\"scripts\\\": {\\n  \\\"build\\\": \\\"gatsby build\\\",\\n  \\\"develop\\\": \\\"gatsby develop\\\",\\n  \\\"start\\\": \\\"serve public/\\\",\\n  \\\"newPost\\\": \\\"node ./scripts/newPost.js\\\"\\n}\\n\")), mdx(\"p\", null, \"And with that you can now type \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm run newPost \\\"Post title here\\\"\"), \" to create a new markdown blog posts for your Gatsby app.\"), mdx(\"p\", null, \"Since this is strictly for my personal use I didn't take any time to make the command line argument handling robust.\"), mdx(\"p\", null, \"This is a script with a very specific single task for a single user. That means it can be a little dodgy and not have any negative effects. It took about ten minutes to write, and will now save me a lot of pointless typing in the future.\"), mdx(\"p\", null, \"More importantly it removed a bit of friction/pain from my blogging experience, which means I might actually do it more \\uD83D\\uDE42\"));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"A handy npm script for creating a new Gatsby blog post","date":"January 30, 2019","banner":null,"slug":"a-handy-npm-script-for-creating-a-new-gatsby-blog-post","keywords":null}}},"pageContext":{"id":"b45e0e76-70dc-5eda-97a1-b861f1ff3427","prev":{"id":"674a3869-52d8-509f-9fbf-9111b665ca58","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"There are many ways to build a new feature for your applications. For me, when I need a new tool, component, or functionality I like to take a step back and think about it in terms of data. What data do I have, what data do I need, and how am I going…","fields":{"title":"Transforming exported CSV data for use in a React chart.","slug":"transforming-exported-csv-data-for-use-in-a-chart","date":"2019-02-08T00:00:00.000Z"}},"next":{"id":"cbaa7c1e-eda5-5b9f-9543-40ea4e726e27","parent":{"name":"index","sourceInstanceName":"blog"},"excerpt":"If you need to migrate your email list subscribers from Drip to  Convertkit , you will run into the problem of importing each of your tags as an individual list into  Convertkit . If you follow the  official recommendation , they suggest that you…","fields":{"title":"Exporting Drip Tags for Import into Convertkit","slug":"export-drip-tags-for-convertkit","date":"2019-01-15T00:00:00.000Z"}}}},
    "staticQueryHashes": ["1045846374"]}