7 mins read
|
4 Aug 2020

Nuxt Blog: Build Hooks

Previously I showed you how to add new content to your NuxtJS blog. To recap, we add the markdown file to the content folder. Then we need to update our postIds with the latest Id in our AllPosts end point. This is all we need to do to access our post. However being a lazy programmer I don’t want to update the list everytime! Plus it would be nice to have some of the markdown attributes automated. Also if you’re using Nuxt < 2.xx you will need to generate your own paths

- Add A Configuration Hook

So what is a hook? Nuxt defines them as:

Hooks are listeners to Nuxt events that are typically used in Nuxt modules, but are also available in nuxt.config.js

We will be using the build hook which can be exposed in the nuxt.config.js file. The build hook gives us access to several events such as:

  • build:before Before Nuxt build started
  • build:compiled Webpack build finished
  • build:done Nuxt Build finished

In our case we want the build:before event so we can update our attributes and Id’s list before the build starts.

Add the following to your nuxt.config.js file:

hooks: { build: { async before(builder) { console.log('build:before event hit') } } }

Now run the dev build command:

npm run dev

Watch the console as your project is being built, you should see the following:
build hook hit

Great! We can now instruct nuxt to do something before it starts building. Add a new folder called config and a new file within it called update-content.js. This file will hold all the logic we want to implement. Add the following to it:

module.exports = { async checkContentFolder() { return new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 2000) }) } }

This code exports the function checkContentFolder so it can be accessed by nuxt.config.js. To access it from nuxt.config.js add the following in the build hook:

const config = require('./config/update-content') console.log('initiate checkContentFolder') await config.checkContentFolder().then(() => { console.log('checkContentFolder resolved') })

now run the dev build command again:

npm run dev

You will see that the checkContentFolder function is called in an asynchronous (async) way so that we can pause (await) the build until we have finished executing our logic. I won’t go into any depth on async, await and promises here but the topics are very much worth some in depth research.

- Update Post Ids List

Time to actually update the Id list! First add an index.js file to your content folder. This is where we will move the Id list to as it makes it accessible. The goal is for the content to read something like this:

export const postIds = ['my-first-post', 'my-second-post'] export const postRoutes = postIds.map((postId) => `/posts/${postId}`)

Note that the second line is only required if you are generating your own routes. In Nuxt > 2.xx this is done for you by default.

How to update your markdown files and how to manage your Id list will vary based on your project. This is an overview of how i’ve done it:

In your update-content.js file replace the content with the following:

import fs from 'fs' import path from 'path' module.exports = { async checkContentFolder() { const folder = './content/' const allFiles = fs.readdirSync(folder) const Ids = [] // Filter the files to get only markdown files const files = allFiles.filter( (file) => path.extname(file).toLowerCase() === '.md' ) for (const file of files) { const fExt = path.extname(file) const fName = path.basename(file, fExt).trim() Ids.push({ id: fName }) } await this.updateContentIndex(folder, Ids) }, updateContentIndex(folder, ids) { console.log('Update Content Index File...') return new Promise((resolve, reject) => { const output = [] const postIds = ids.map((post) => `"${post.id}"`) output.push(`export const postIds = [${postIds}]`) output.push( 'export const postRoutes = postIds.map((postId) => `/posts/${postId}`)' ) fs.writeFile(`${folder}/index.js`, output.join('\n'), 'utf-8', function( err ) { if (err) reject(err) console.log('Content Index File Updated') resolve() }) }) } }

Here we import the required library’s. Then in our entry function, checkContentFolder, we extract all the files in the content folder and filter them to so we only get markdown files in out file array. Then we loop through the files extracting the file name and adding this to our Ids array.

I add the Id’s as an object to allow for future expansion of the function. For example on my site I separate the Id’s by a type: projects or tutorial.

Then we update the content folders index.js file. Two lines are added one lists the post id’s and the other lists the post routes (required in Nuxt < 2.xx). Now when you run the build command you should see that the index.js file updates!

Now lets replace the static Id const in the file pages/posts/index.vue:

<script> import { postIds } from '~/content' export default { async asyncData() { // const postIds = ['my-first-post', 'my-second-post'] const posts = await Promise.all( postIds.map(async (id) => { const post = await import(`~/content/${id}.md`) return post.attributes }) ) return { posts } } } </script>

From now on any markdown file that gets added to the content folder will be available!

- Update Content Flag

Currently everytime you build you project, either for development or production, the update content logic gets executed. This might not be a problem if you keep everything simple but I’ve added some more time consuming logic and as the the number of post you create expands it might not become very scalable. So I like to only execute the update content logic when required. To do this I check for a the argument content in my build hook. If it appears I execute the logic.

Add the following to your nuxt.config.js file in the before function:

async before(builder) { const updateContent = process.argv.includes('--content') if (updateContent) { console.log('Updating Content folder') const config = require('./config/update-content') await config.checkContentFolder().then(() => { console.log('Content folder updated') }) } else { console.log('Content folder will NOT be updated') } }

Now the logic will only be executed when the content argument is found. A good way in integrate this into you workflow is to add it as one of your scripts. Open the package.json file found in your root directory and add the following line to your scripts object:

"scripts": { "dev": "nuxt", "dev-content": "nuxt --content", ... }

To build and execute the update content folder logic:

npm run dev-content

To build without updating the content folder:

npm run dev

This same logic can also be applied to production build and deploy scripts.

- Generate Routes

For those of you who are using nuxt < 2.xx or who want to generate their own routes you will need to add one more thing to the nuxt.config.js file to let nuxt know about your routes. For users of Nuxt > 2.xx who select the static option a Nuxt integrated crawler will find all your routes for you.

In the nuxt.config.js file import the postRoutes just like we did for the postIds. Then add the array to the generate/routes object:

import { postRoutes } from './content' export default { ... generate: { routes: postRoutes } ... }

- Future Work

Now that you have the basic logic up and running I’m sure you can see many ways to improve this process/logic to improve your particular workflow. For example in my update-content.js logic I also read each markdown file. I then check that it has an id, date and readTime attributes. If any of these are missing or incorrect I update them. I also have a type attribute which allows me to split the ids and routes based on whether the post is of tutorial type or of project type. This can ofcourse easily be done at runtime but I had the option to do it here so I took it.

- Summary

In this post you learnt how to automate the postId and postRoute array using a Nuxt build hook. You also learnt how to toggle this automation on and off.

I hope this post was helpful. Please get in touch with any feedback you have:

Loading...

- Resources