Initialize or sync Nuxt module architecture (catalogs, playground, fixtures, GH actions)
Initializes or syncs Nuxt modules to standardized architecture with runtime directories, config files, and test fixtures.
/plugin marketplace add harlan-zw/harlan-claude-code/plugin install harlan-claude-code@harlan-claude-codeThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Initialize a new Nuxt module or sync an existing one to standardized architecture.
Extends /pkg-init with Nuxt-specific patterns.
/nuxt-module-init # sync existing module
/nuxt-module-init my-module # init new module
Check for @nuxt/module-builder in devDependencies.
Add these to pnpm-workspace.yaml alongside base catalogs from /pkg-init:
catalogs:
# ... base catalogs from /pkg-init ...
# Nuxt testing
dev-test:
vitest: ^4.0.16
happy-dom: ^20.1.0
'@vue/test-utils': ^2.4.6
'@nuxt/test-utils': ^3.23.0
playwright: ^1.57.0
# Vue build
dev-build:
typescript: ^5.9.3
unbuild: ^3.6.1
vue-tsc: ^3.2.2
bumpp: ^10.3.2
'@arethetypeswrong/cli': ^0.18.2
# Nuxt ecosystem
nuxt:
'@nuxt/kit': ^4.2.2
'@nuxt/module-builder': ^1.0.2
'@nuxt/schema': ^4.2.2
nuxt: ^4.2.2
nitropack: ^2.12.9
src/
├── module.ts # Main module entry (build-time)
├── runtime/
│ ├── app/ # Client/SSR code (Vue context)
│ │ ├── composables/ # Auto-imported composables
│ │ │ └── useX.ts
│ │ ├── components/ # Auto-imported components
│ │ ├── plugins/ # Nuxt plugins
│ │ │ ├── init.ts
│ │ │ └── X.server.ts # SSR-only plugin
│ │ └── utils/ # Client utilities
│ ├── server/ # Nitro server code
│ │ ├── composables/ # Server composables (auto-imported)
│ │ │ └── useX.ts
│ │ ├── plugins/ # Nitro plugins (startup hooks)
│ │ │ └── init.ts
│ │ ├── routes/ # API endpoints
│ │ │ ├── api.ts
│ │ │ └── __my-module__/ # Debug routes (prefixed)
│ │ │ └── debug.ts
│ │ ├── middleware/ # H3 middleware
│ │ └── utils/ # Server utilities
│ ├── shared/ # Isomorphic code (client + server)
│ │ ├── utils.ts
│ │ └── constants.ts
│ └── types.ts # Runtime types
├── content.ts # Content module integration (optional)
└── types.ts # Module options types
playground/
├── nuxt.config.ts
├── app.vue
└── pages/
└── index.vue
test/
├── unit/
│ └── *.test.ts
├── e2e/
│ └── *.test.ts
└── fixtures/
├── basic/
│ ├── nuxt.config.ts
│ └── app.vue
└── i18n/ # Additional fixture variants
runtime/app/ - Client & SSR code (Vue context)
useNuxtApp(), useRuntimeConfig(), Vue reactivityaddImports()addPlugin().server.ts suffix for SSR-only files.client.ts suffix for client-only filesruntime/server/ - Nitro server code
useRuntimeConfig()addServerImportsDir()addServerPlugin() (run on Nitro startup)addServerHandler()__module-name__/runtime/shared/ - Isomorphic code
import { addImports, addPlugin, addServerHandler, addServerImportsDir, addServerPlugin, createResolver, defineNuxtModule, hasNuxtModule } from '@nuxt/kit'
import type { ModuleOptions } from './types'
export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'my-module',
configKey: 'myModule',
},
defaults: {
enabled: true,
},
async setup(config, nuxt) {
const { resolve } = createResolver(import.meta.url)
// Skip if disabled
if (!config.enabled)
return
// Aliases for clean imports
nuxt.options.alias['#my-module'] = resolve('./runtime')
nuxt.options.nitro.alias ||= {}
nuxt.options.nitro.alias['#my-module'] = resolve('./runtime')
nuxt.options.nitro.alias['#my-module/server'] = resolve('./runtime/server')
// App plugins
addPlugin({
src: resolve('./runtime/app/plugins/init'),
mode: 'all', // or 'server' | 'client'
})
// App composables (auto-imported)
addImports({
name: 'useMyModule',
from: resolve('./runtime/app/composables/useMyModule'),
})
// Server composables (auto-imported in Nitro)
addServerImportsDir(resolve('./runtime/server/composables'))
// Nitro plugin (runs on server startup)
addServerPlugin(resolve('./runtime/server/plugins/init'))
// API routes
addServerHandler({
route: '/api/my-module',
handler: resolve('./runtime/server/routes/api'),
})
// Debug routes (dev only)
if (nuxt.options.dev) {
addServerHandler({
route: '/__my-module__/debug',
handler: resolve('./runtime/server/routes/__my-module__/debug'),
})
}
// H3 middleware
addServerHandler({
middleware: true,
handler: resolve('./runtime/server/middleware/context'),
})
// Conditional features based on other modules
if (hasNuxtModule('@nuxt/content')) {
addServerPlugin(resolve('./runtime/server/plugins/content'))
}
// Virtual modules (dynamic content at build time)
nuxt.hooks.hook('nitro:config', (nitroConfig) => {
nitroConfig.virtual ||= {}
nitroConfig.virtual['#my-module/virtual'] = `export default ${JSON.stringify(config)}`
})
// Runtime config
nuxt.options.runtimeConfig.public.myModule = defu(
nuxt.options.runtimeConfig.public.myModule,
{ enabled: config.enabled }
)
},
})
| What | Function | Mode/Location |
|---|---|---|
| App composable | addImports() | auto-imported in Vue |
| App plugin | addPlugin() | mode: 'all'/'server'/'client' |
| App component | addComponent() | auto-imported in Vue |
| Server composable | addServerImportsDir() | auto-imported in Nitro |
| Server plugin | addServerPlugin() | runs on Nitro startup |
| API route | addServerHandler({ route }) | GET/POST endpoint |
| Middleware | addServerHandler({ middleware: true }) | H3 middleware |
// In module setup when disabled:
if (!config.enabled) {
// Provide mock composables so imports don't break
addImports({
name: 'useMyModule',
from: resolve('./runtime/app/composables/mock'),
})
// Mock server composables
nuxt.hooks.hook('nitro:config', (nitroConfig) => {
nitroConfig.imports ||= {}
nitroConfig.imports.presets ||= []
nitroConfig.imports.presets.push({
from: resolve('./runtime/server/composables/mock'),
imports: ['useMyServerComposable'],
})
})
return
}
Nuxt modules extend generated config:
{
"extends": "./.nuxt/tsconfig.json"
}
Important: Requires nuxi prepare before typecheck works.
Use @nuxt/test-utils/config for e2e tests:
import { defineConfig, defineProject } from 'vitest/config'
import { defineVitestProject } from '@nuxt/test-utils/config'
export default defineConfig({
test: {
globals: true,
reporters: 'dot',
projects: [
defineProject({
test: {
name: 'unit',
include: ['test/unit/**/*.test.ts'],
},
}),
defineVitestProject({
test: {
name: 'e2e',
include: ['test/e2e/**/*.test.ts'],
environment: 'nuxt',
environmentOptions: {
nuxt: {
rootDir: './test/fixtures/basic',
},
},
},
}),
],
},
})
import antfu from '@antfu/eslint-config'
export default antfu({
type: 'lib',
ignores: [
'CLAUDE.md',
'test/fixtures/**',
'playground/**',
],
})
Nuxt modules need externals for runtime dependencies:
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
declaration: true,
entries: [
'src/module',
// Additional entries for separate bundles
{ input: 'src/content', name: 'content' },
],
externals: [
// Nuxt core
'nuxt',
'nuxt/schema',
'@nuxt/kit',
'@nuxt/schema',
'nitropack',
'nitropack/types',
'h3',
// Vue
'vue',
'vue-router',
'@vue/runtime-core',
// Common deps
'#imports',
],
})
# Nuxt
.nuxt
.output
playground/.nuxt
playground/.output
test/fixtures/**/.nuxt
test/fixtures/**/.output
{
"name": "@nuxtjs/my-module",
"type": "module",
"exports": {
".": {
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
}
},
"main": "./dist/module.cjs",
"types": "./dist/types.d.ts",
"files": ["dist"],
"peerDependencies": {
"nuxt": "^3.0.0 || ^4.0.0"
}
}
{
"scripts": {
"build": "nuxt-module-build build",
"dev": "nuxi dev playground",
"dev:prepare": "nuxt-module-build prepare && nuxi prepare playground",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "nuxt typecheck",
"prepare:fixtures": "nuxi prepare test/fixtures/basic",
"test": "pnpm prepare:fixtures && vitest",
"test:run": "pnpm prepare:fixtures && vitest run",
"release": "bumpp && pnpm publish"
}
}
export default defineNuxtConfig({
modules: ['../src/module'],
myModule: {
// module options
},
devtools: { enabled: true },
compatibilityDate: '2025-01-01',
})
<template>
<div>
<NuxtPage />
</div>
</template>
<template>
<div>
<h1>My Module Playground</h1>
</div>
</template>
export default defineNuxtConfig({
modules: ['../../../src/module'],
myModule: {
// test options
},
})
<template>
<div>
<NuxtPage />
</div>
</template>
name: Test
on:
push:
paths-ignore:
- '**/README.md'
- 'docs/**'
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: lts/*
cache: pnpm
- run: pnpm i
- name: Lint
run: pnpm lint
- name: Prepare
run: pnpm dev:prepare
- name: Typecheck
run: pnpm typecheck
- name: Build
run: pnpm build
- name: Test
run: pnpm test:run
import { describe, expect, it } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils/e2e'
describe('my-module', async () => {
await setup({
rootDir: './test/fixtures/basic',
})
it('renders page', async () => {
const html = await $fetch('/')
expect(html).toContain('My Module')
})
})
import { describe, expect, it } from 'vitest'
import { setup, useTestContext } from '@nuxt/test-utils/e2e'
describe('composables', async () => {
await setup({
rootDir: './test/fixtures/basic',
})
it('useMyComposable works', async () => {
const ctx = useTestContext()
// test composable behavior
})
})
In addition to /pkg-init checklist:
Structure:
src/module.ts - main module entry existssrc/runtime/app/ - client/SSR code directorysrc/runtime/server/ - Nitro server code directorysrc/runtime/shared/ - isomorphic utilities (if needed)src/types.ts - module options typesplayground/ - exists with nuxt.config.ts, app.vue, pages/test/fixtures/basic/ - exists with nuxt.config.tsConfig:
8. [ ] pnpm-workspace.yaml - add nuxt: catalog
9. [ ] package.json - nuxt module exports, peerDependencies
10. [ ] tsconfig.json - extends .nuxt/tsconfig.json
11. [ ] vitest.config.ts - use defineVitestProject for e2e
12. [ ] build.config.ts - nuxt externals including #imports
13. [ ] eslint.config.mjs - ignore fixtures/playground
14. [ ] .gitignore - nuxt build dirs (.nuxt, .output)
Scripts:
15. [ ] typecheck - uses nuxt typecheck (not tsc --noEmit)
16. [ ] dev:prepare - prepares module + playground
17. [ ] prepare:fixtures - prepares test fixtures
18. [ ] .github/workflows/test.yml - includes prepare step before typecheck
| Project Type | Command | Why |
|---|---|---|
| Nuxt module | nuxt typecheck | Uses generated .nuxt/tsconfig.json with auto-imports |
| Non-Nuxt lib | tsc --noEmit | Standard TypeScript check |
pnpm dev:prepare first (generates .nuxt/)../../../src/module)addServerImportsDir() not addImports()mode is set correctly ('server', 'client', or 'all')Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.