DEV Community

Yasser Lahbibi
Yasser Lahbibi

Posted on • Edited on

How to cook Nuxt 3 modules

Ingredients

  1. 100gr of Nuxt 3
  2. 1 spoon of @nuxt/kit
  3. 1L of Nuxt Module Builder
  4. 100 tons of node_modules
  5. 1 bowl of great ideas
  6. 1 pinch of Vitest (optional)

Preparation

1) Start by pouring 1L of Nuxt Module Builder into a big bowl (this also works if you use a Terminal).

npx nuxi init -t module my-amazing-recipe
Enter fullscreen mode Exit fullscreen mode

If you succeeded this simple but very important step, your folder structure should be nicely firm and moist. It should look like the image below; if not, you failed and should start over.

Folder structure

2) It is now time to add a 100 tons of node_modules to make sure your mixture doesn't end up too light. We want something really big and thick. Run this command that should do the trick.

yarn install # or npm install
Enter fullscreen mode Exit fullscreen mode

3) The runtime folder is the place we're going to add most of our great ideas. If you bought your ingredients from Nuxt Module Builder as recommended, there should already be a plugin.ts file in it. Let's add our recipe there.

// ./src/runtime/plugin.ts

import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin(nuxtApp => {
   nuxtApp.provide('recipe', 'my-amazing-recipe')
})
Enter fullscreen mode Exit fullscreen mode

4) Alright so now that we have provided this recipe to our dish, let's add a convenient way to read it.

// ./src/runtime/composables/useRecipe/index.ts

/**
 * Use the recipe.
 */
export function useRecipe () {
  const { $recipe } = useNuxtApp()

  return $recipe
}
Enter fullscreen mode Exit fullscreen mode

But for our TypeScript lovers out there, you probably won't like that any type at all. So, let's spice things up a little bit.

// ./src/runtime/plugin.ts

import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin(() => ({
  provide: {
    recipe: 'my-amazing-recipe'
  }
}))
Enter fullscreen mode Exit fullscreen mode

Go back to your composable and enjoy that sweet sweet string type.

5) At this point - and since you're an amazing cook - you should try to make sure that your dish tastes exactly as you'd expect. Unfortunately, at the time of writing, it's not yet super straight forward. I'm sure we'll get that nice and creamy @nuxt/test-utils ready for Nuxt 3 in no time but for the sake of our high quality meal, let's just pretend we had it already.

  • Add some vitest into the mix.
yarn add --dev vitest
Enter fullscreen mode Exit fullscreen mode
  • Immediately add a pinch of testing.
// ./src/runtime/composables/useRecipe/index.test.ts

import { describe, it, expect } from 'vitest'
import { useRecipe } from '.'

describe('useRecipe', () => {
  it('should show my recipe', () => {
    expect(useRecipe()).toBe('my-amazing-recipe')
  })
})
Enter fullscreen mode Exit fullscreen mode
  • Run this command and let it fail miserably (for now).
yarn vitest
Enter fullscreen mode Exit fullscreen mode

6) Alright, we're almost done. The last step before putting our mixture into the oven is to ensure that our little useRecipe composable will evenly expand across all components. The technical cooking term is auto-importing. But some people like it better when they can choose where they want more flavour, so let's make it optional.

// ./src/module.ts
import { resolve } from 'path'
import { fileURLToPath } from 'url'
import { defineNuxtModule, addPlugin, addAutoImportDir } from '@nuxt/kit'

export interface ModuleOptions {
  addPlugin: boolean;
  autoImport: boolean; // Add some type for your option.
}

export default defineNuxtModule<ModuleOptions>({
  ...,
  defaults: {
    addPlugin: true,
    autoImport: true // Set the default option to true.
  },
  setup (options, nuxt) {
    if (options.addPlugin) {
      const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
      nuxt.options.build.transpile.push(runtimeDir)
      addPlugin(resolve(runtimeDir, 'plugin'))

      if (options.autoImport) {
        // Use that magic helper to auto-import your composables.
        addAutoImportDir(resolve(runtimeDir, 'composables'))
      }
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

7) Great, I think we're all set. Let's put it in the oven.

// ./playground/app.vue
<script setup>
const { $recipe } = useNuxtApp()
</script>

<template>
  <div>
    {{ $recipe }}
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Let's set it to 180°C by running yarn dev for about a few seconds because it's running Vite and it's just way too fast, which kind of ruins this step for me.

8) Let's head over to http://localhost:3000 and see if our recipe really is as amazing as my-amazing-recipe.

9) You're such a good cook. 🎉

Top comments (0)