@plutocms/supabase 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/nuxt/GENERATION.md +5 -0
- package/.agents/skills/nuxt/SKILL.md +55 -0
- package/.agents/skills/nuxt/references/advanced-hooks.md +289 -0
- package/.agents/skills/nuxt/references/advanced-layers.md +299 -0
- package/.agents/skills/nuxt/references/advanced-module-authoring.md +554 -0
- package/.agents/skills/nuxt/references/best-practices-data-fetching.md +357 -0
- package/.agents/skills/nuxt/references/best-practices-ssr.md +355 -0
- package/.agents/skills/nuxt/references/core-cli.md +263 -0
- package/.agents/skills/nuxt/references/core-config.md +162 -0
- package/.agents/skills/nuxt/references/core-data-fetching.md +236 -0
- package/.agents/skills/nuxt/references/core-deployment.md +224 -0
- package/.agents/skills/nuxt/references/core-directory-structure.md +269 -0
- package/.agents/skills/nuxt/references/core-modules.md +292 -0
- package/.agents/skills/nuxt/references/core-routing.md +226 -0
- package/.agents/skills/nuxt/references/features-components-autoimport.md +328 -0
- package/.agents/skills/nuxt/references/features-components.md +264 -0
- package/.agents/skills/nuxt/references/features-composables.md +276 -0
- package/.agents/skills/nuxt/references/features-server.md +265 -0
- package/.agents/skills/nuxt/references/features-state.md +194 -0
- package/.agents/skills/nuxt/references/rendering-modes.md +237 -0
- package/.agents/skills/nuxt-ui/SKILL.md +334 -0
- package/.agents/skills/nuxt-ui/references/components.md +377 -0
- package/.agents/skills/nuxt-ui/references/composables.md +127 -0
- package/.agents/skills/nuxt-ui/references/layouts/chat.md +195 -0
- package/.agents/skills/nuxt-ui/references/layouts/dashboard.md +220 -0
- package/.agents/skills/nuxt-ui/references/layouts/docs.md +141 -0
- package/.agents/skills/nuxt-ui/references/layouts/editor.md +167 -0
- package/.agents/skills/nuxt-ui/references/layouts/page.md +260 -0
- package/.agents/skills/nuxt-ui/references/theming.md +427 -0
- package/.agents/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/CLAUDE.md +1 -0
- package/.agents/skills/supabase-postgres-best-practices/README.md +116 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/.editorconfig +12 -0
- package/.env.example +2 -0
- package/.eslintignore +1 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +61 -0
- package/README.md +62 -0
- package/app/components/navbar/NavbarAdmin.vue +169 -0
- package/app/composables/auth.ts +81 -0
- package/app/layouts/setup.vue +13 -0
- package/app/middleware/auth.ts +27 -0
- package/app/middleware/setup-check.ts +25 -0
- package/app/pages/admin/(auth)/login.vue +84 -0
- package/app/pages/admin/(auth)/signup.vue +128 -0
- package/app/pages/admin/settings.vue +116 -0
- package/app/pages/admin/setup.vue +300 -0
- package/app/pages/admin.vue +38 -0
- package/bun.lock +2685 -0
- package/eslint.config.mjs +88 -0
- package/nuxt.config.ts +55 -0
- package/package.json +44 -0
- package/public/schema.sql +103 -0
- package/scripts/supabase-typegen.ts +36 -0
- package/server/api/category/list.get.ts +9 -0
- package/server/api/category/new.post.ts +31 -0
- package/server/api/settings/first_setup.get.ts +39 -0
- package/server/api/settings/index.get.ts +27 -0
- package/server/api/settings/update.post.ts +36 -0
- package/server/api/setup/create.post.ts +58 -0
- package/server/api/signup.post.ts +28 -0
- package/server/api/users/[id].get.ts +21 -0
- package/server/api/users/index.get.ts +16 -0
- package/shared/types/supabase.ts +406 -0
- package/skills-lock.json +20 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: module-authoring
|
|
3
|
+
description: Complete guide to creating publishable Nuxt modules with best practices
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Module Authoring
|
|
7
|
+
|
|
8
|
+
This guide covers creating publishable Nuxt modules with proper structure, type safety, and best practices.
|
|
9
|
+
|
|
10
|
+
## Module Structure
|
|
11
|
+
|
|
12
|
+
Recommended structure for a publishable module:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
my-nuxt-module/
|
|
16
|
+
├── src/
|
|
17
|
+
│ ├── module.ts # Module entry
|
|
18
|
+
│ └── runtime/
|
|
19
|
+
│ ├── components/ # Vue components
|
|
20
|
+
│ ├── composables/ # Composables
|
|
21
|
+
│ ├── plugins/ # Nuxt plugins
|
|
22
|
+
│ └── server/ # Server handlers
|
|
23
|
+
├── playground/ # Development app
|
|
24
|
+
├── package.json
|
|
25
|
+
└── tsconfig.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Module Definition
|
|
29
|
+
|
|
30
|
+
### Basic Module with Type-safe Options
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// src/module.ts
|
|
34
|
+
import { defineNuxtModule, createResolver, addPlugin, addComponent, addImports } from '@nuxt/kit'
|
|
35
|
+
|
|
36
|
+
export interface ModuleOptions {
|
|
37
|
+
prefix?: string
|
|
38
|
+
apiKey: string
|
|
39
|
+
enabled?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default defineNuxtModule<ModuleOptions>({
|
|
43
|
+
meta: {
|
|
44
|
+
name: 'my-module',
|
|
45
|
+
configKey: 'myModule',
|
|
46
|
+
compatibility: {
|
|
47
|
+
nuxt: '>=3.0.0',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
defaults: {
|
|
51
|
+
prefix: 'My',
|
|
52
|
+
enabled: true,
|
|
53
|
+
},
|
|
54
|
+
setup(options, nuxt) {
|
|
55
|
+
if (!options.enabled) return
|
|
56
|
+
|
|
57
|
+
const { resolve } = createResolver(import.meta.url)
|
|
58
|
+
|
|
59
|
+
// Module setup logic here
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Using `.with()` for Strict Type Inference
|
|
65
|
+
|
|
66
|
+
When you need TypeScript to infer that default values are always present:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { defineNuxtModule } from '@nuxt/kit'
|
|
70
|
+
|
|
71
|
+
interface ModuleOptions {
|
|
72
|
+
apiKey: string
|
|
73
|
+
baseURL: string
|
|
74
|
+
timeout?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default defineNuxtModule<ModuleOptions>().with({
|
|
78
|
+
meta: {
|
|
79
|
+
name: '@nuxtjs/my-api',
|
|
80
|
+
configKey: 'myApi',
|
|
81
|
+
},
|
|
82
|
+
defaults: {
|
|
83
|
+
baseURL: 'https://api.example.com',
|
|
84
|
+
timeout: 5000,
|
|
85
|
+
},
|
|
86
|
+
setup(resolvedOptions, nuxt) {
|
|
87
|
+
// resolvedOptions.baseURL is guaranteed to be string (not undefined)
|
|
88
|
+
// resolvedOptions.timeout is guaranteed to be number (not undefined)
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Adding Runtime Assets
|
|
94
|
+
|
|
95
|
+
### Components
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { addComponent, addComponentsDir, createResolver } from '@nuxt/kit'
|
|
99
|
+
|
|
100
|
+
export default defineNuxtModule({
|
|
101
|
+
setup() {
|
|
102
|
+
const { resolve } = createResolver(import.meta.url)
|
|
103
|
+
|
|
104
|
+
// Single component
|
|
105
|
+
addComponent({
|
|
106
|
+
name: 'MyButton',
|
|
107
|
+
filePath: resolve('./runtime/components/MyButton.vue'),
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Component directory with prefix
|
|
111
|
+
addComponentsDir({
|
|
112
|
+
path: resolve('./runtime/components'),
|
|
113
|
+
prefix: 'My',
|
|
114
|
+
pathPrefix: false,
|
|
115
|
+
})
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Composables and Auto-imports
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { addImports, addImportsDir, createResolver } from '@nuxt/kit'
|
|
124
|
+
|
|
125
|
+
export default defineNuxtModule({
|
|
126
|
+
setup() {
|
|
127
|
+
const { resolve } = createResolver(import.meta.url)
|
|
128
|
+
|
|
129
|
+
// Single import
|
|
130
|
+
addImports({
|
|
131
|
+
name: 'useMyUtil',
|
|
132
|
+
from: resolve('./runtime/composables/useMyUtil'),
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Directory of composables
|
|
136
|
+
addImportsDir(resolve('./runtime/composables'))
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Plugins
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { addPlugin, addPluginTemplate, createResolver } from '@nuxt/kit'
|
|
145
|
+
|
|
146
|
+
export default defineNuxtModule({
|
|
147
|
+
setup(options) {
|
|
148
|
+
const { resolve } = createResolver(import.meta.url)
|
|
149
|
+
|
|
150
|
+
// Static plugin file
|
|
151
|
+
addPlugin({
|
|
152
|
+
src: resolve('./runtime/plugins/myPlugin'),
|
|
153
|
+
mode: 'client', // 'client', 'server', or 'all'
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Dynamic plugin with generated code
|
|
157
|
+
addPluginTemplate({
|
|
158
|
+
filename: 'my-module-plugin.mjs',
|
|
159
|
+
getContents: () => `
|
|
160
|
+
import { defineNuxtPlugin } from '#app/nuxt'
|
|
161
|
+
|
|
162
|
+
export default defineNuxtPlugin({
|
|
163
|
+
name: 'my-module',
|
|
164
|
+
setup() {
|
|
165
|
+
const config = ${JSON.stringify(options)}
|
|
166
|
+
// Plugin logic
|
|
167
|
+
}
|
|
168
|
+
})`,
|
|
169
|
+
})
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Server Extensions
|
|
175
|
+
|
|
176
|
+
### Server Handlers
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { addServerHandler, addServerScanDir, createResolver } from '@nuxt/kit'
|
|
180
|
+
|
|
181
|
+
export default defineNuxtModule({
|
|
182
|
+
setup() {
|
|
183
|
+
const { resolve } = createResolver(import.meta.url)
|
|
184
|
+
|
|
185
|
+
// Single handler
|
|
186
|
+
addServerHandler({
|
|
187
|
+
route: '/api/my-endpoint',
|
|
188
|
+
handler: resolve('./runtime/server/api/my-endpoint'),
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// Scan entire server directory (api/, routes/, middleware/, utils/)
|
|
192
|
+
addServerScanDir(resolve('./runtime/server'))
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Server Composables
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { addServerImports, addServerImportsDir, createResolver } from '@nuxt/kit'
|
|
201
|
+
|
|
202
|
+
export default defineNuxtModule({
|
|
203
|
+
setup() {
|
|
204
|
+
const { resolve } = createResolver(import.meta.url)
|
|
205
|
+
|
|
206
|
+
// Single server import
|
|
207
|
+
addServerImports({
|
|
208
|
+
name: 'useServerUtil',
|
|
209
|
+
from: resolve('./runtime/server/utils/useServerUtil'),
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// Server composables directory
|
|
213
|
+
addServerImportsDir(resolve('./runtime/server/composables'))
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Nitro Plugin
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import { addServerPlugin, createResolver } from '@nuxt/kit'
|
|
222
|
+
|
|
223
|
+
export default defineNuxtModule({
|
|
224
|
+
setup() {
|
|
225
|
+
const { resolve } = createResolver(import.meta.url)
|
|
226
|
+
addServerPlugin(resolve('./runtime/server/plugin'))
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
// runtime/server/plugin.ts
|
|
233
|
+
import { defineNitroPlugin } from 'nitropack/runtime'
|
|
234
|
+
|
|
235
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
236
|
+
nitroApp.hooks.hook('request', (event) => {
|
|
237
|
+
console.log('Request:', event.path)
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Templates and Virtual Files
|
|
243
|
+
|
|
244
|
+
### Generate Virtual Files
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { addTemplate, addTypeTemplate, addServerTemplate, createResolver } from '@nuxt/kit'
|
|
248
|
+
|
|
249
|
+
export default defineNuxtModule({
|
|
250
|
+
setup(options, nuxt) {
|
|
251
|
+
const { resolve } = createResolver(import.meta.url)
|
|
252
|
+
|
|
253
|
+
// Client/build virtual file (accessible via #build/my-config.mjs)
|
|
254
|
+
addTemplate({
|
|
255
|
+
filename: 'my-config.mjs',
|
|
256
|
+
getContents: () => `export default ${JSON.stringify(options)}`,
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// Type declarations
|
|
260
|
+
addTypeTemplate({
|
|
261
|
+
filename: 'types/my-module.d.ts',
|
|
262
|
+
getContents: () => `
|
|
263
|
+
declare module '#my-module' {
|
|
264
|
+
export interface Config {
|
|
265
|
+
apiKey: string
|
|
266
|
+
}
|
|
267
|
+
}`,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
// Nitro virtual file (accessible in server routes)
|
|
271
|
+
addServerTemplate({
|
|
272
|
+
filename: '#my-module/config.mjs',
|
|
273
|
+
getContents: () => `export const config = ${JSON.stringify(options)}`,
|
|
274
|
+
})
|
|
275
|
+
},
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Access Virtual Files
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
// In runtime plugin
|
|
283
|
+
// @ts-expect-error - virtual file
|
|
284
|
+
import config from '#build/my-config.mjs'
|
|
285
|
+
|
|
286
|
+
// In server routes
|
|
287
|
+
import { config } from '#my-module/config.js'
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Extending Pages and Routes
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { extendPages, extendRouteRules, addRouteMiddleware, createResolver } from '@nuxt/kit'
|
|
294
|
+
|
|
295
|
+
export default defineNuxtModule({
|
|
296
|
+
setup() {
|
|
297
|
+
const { resolve } = createResolver(import.meta.url)
|
|
298
|
+
|
|
299
|
+
// Add pages
|
|
300
|
+
extendPages((pages) => {
|
|
301
|
+
pages.push({
|
|
302
|
+
name: 'my-page',
|
|
303
|
+
path: '/my-route',
|
|
304
|
+
file: resolve('./runtime/pages/MyPage.vue'),
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Add route rules (caching, redirects, etc.)
|
|
309
|
+
extendRouteRules('/api/**', {
|
|
310
|
+
cache: { maxAge: 60 },
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
// Add middleware
|
|
314
|
+
addRouteMiddleware({
|
|
315
|
+
name: 'my-middleware',
|
|
316
|
+
path: resolve('./runtime/middleware/myMiddleware'),
|
|
317
|
+
global: true,
|
|
318
|
+
})
|
|
319
|
+
},
|
|
320
|
+
})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Module Dependencies
|
|
324
|
+
|
|
325
|
+
Declare dependencies on other modules with version constraints:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
export default defineNuxtModule({
|
|
329
|
+
meta: {
|
|
330
|
+
name: 'my-module',
|
|
331
|
+
},
|
|
332
|
+
moduleDependencies: {
|
|
333
|
+
'@nuxtjs/tailwindcss': {
|
|
334
|
+
version: '>=6.0.0',
|
|
335
|
+
// Set defaults (user can override)
|
|
336
|
+
defaults: {
|
|
337
|
+
exposeConfig: true,
|
|
338
|
+
},
|
|
339
|
+
// Force specific options
|
|
340
|
+
overrides: {
|
|
341
|
+
viewer: false,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
'@nuxtjs/i18n': {
|
|
345
|
+
optional: true, // Won't fail if not installed
|
|
346
|
+
defaults: {
|
|
347
|
+
defaultLocale: 'en',
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
setup() {
|
|
352
|
+
// Dependencies are guaranteed to be set up before this runs
|
|
353
|
+
},
|
|
354
|
+
})
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Dynamic Dependencies
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
moduleDependencies(nuxt) {
|
|
361
|
+
const deps: Record<string, any> = {
|
|
362
|
+
'@nuxtjs/tailwindcss': { version: '>=6.0.0' },
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (nuxt.options.ssr) {
|
|
366
|
+
deps['@nuxtjs/html-validator'] = { optional: true }
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return deps
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Lifecycle Hooks
|
|
374
|
+
|
|
375
|
+
Requires `meta.name` and `meta.version`:
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
export default defineNuxtModule({
|
|
379
|
+
meta: {
|
|
380
|
+
name: 'my-module',
|
|
381
|
+
version: '1.2.0',
|
|
382
|
+
},
|
|
383
|
+
onInstall(nuxt) {
|
|
384
|
+
// First-time setup
|
|
385
|
+
console.log('Module installed for the first time')
|
|
386
|
+
},
|
|
387
|
+
onUpgrade(nuxt, options, previousVersion) {
|
|
388
|
+
// Version upgrade migrations
|
|
389
|
+
console.log(`Upgrading from ${previousVersion}`)
|
|
390
|
+
},
|
|
391
|
+
setup(options, nuxt) {
|
|
392
|
+
// Regular setup runs every build
|
|
393
|
+
},
|
|
394
|
+
})
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Extending Configuration
|
|
398
|
+
|
|
399
|
+
```ts
|
|
400
|
+
export default defineNuxtModule({
|
|
401
|
+
setup(options, nuxt) {
|
|
402
|
+
// Add CSS
|
|
403
|
+
nuxt.options.css.push('my-module/styles.css')
|
|
404
|
+
|
|
405
|
+
// Add runtime config
|
|
406
|
+
nuxt.options.runtimeConfig.public.myModule = {
|
|
407
|
+
apiUrl: options.apiUrl,
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Extend Vite config
|
|
411
|
+
nuxt.options.vite.optimizeDeps ||= {}
|
|
412
|
+
nuxt.options.vite.optimizeDeps.include ||= []
|
|
413
|
+
nuxt.options.vite.optimizeDeps.include.push('some-package')
|
|
414
|
+
|
|
415
|
+
// Add build transpile
|
|
416
|
+
nuxt.options.build.transpile.push('my-package')
|
|
417
|
+
},
|
|
418
|
+
})
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Using Hooks
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
export default defineNuxtModule({
|
|
425
|
+
// Declarative hooks
|
|
426
|
+
hooks: {
|
|
427
|
+
'components:dirs': (dirs) => {
|
|
428
|
+
dirs.push({ path: '~/extra' })
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
setup(options, nuxt) {
|
|
433
|
+
// Programmatic hooks
|
|
434
|
+
nuxt.hook('pages:extend', (pages) => {
|
|
435
|
+
// Modify pages
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
nuxt.hook('imports:extend', (imports) => {
|
|
439
|
+
imports.push({ name: 'myHelper', from: 'my-package' })
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
nuxt.hook('nitro:config', (config) => {
|
|
443
|
+
// Modify Nitro config
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
nuxt.hook('vite:extendConfig', (config) => {
|
|
447
|
+
// Modify Vite config
|
|
448
|
+
})
|
|
449
|
+
},
|
|
450
|
+
})
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Path Resolution
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
import { createResolver, resolvePath, findPath } from '@nuxt/kit'
|
|
457
|
+
|
|
458
|
+
export default defineNuxtModule({
|
|
459
|
+
async setup(options, nuxt) {
|
|
460
|
+
// Resolver relative to module
|
|
461
|
+
const { resolve } = createResolver(import.meta.url)
|
|
462
|
+
|
|
463
|
+
const pluginPath = resolve('./runtime/plugin')
|
|
464
|
+
|
|
465
|
+
// Resolve with extensions and aliases
|
|
466
|
+
const entrypoint = await resolvePath('@some/package')
|
|
467
|
+
|
|
468
|
+
// Find first existing file
|
|
469
|
+
const configPath = await findPath([
|
|
470
|
+
resolve('./config.ts'),
|
|
471
|
+
resolve('./config.js'),
|
|
472
|
+
])
|
|
473
|
+
},
|
|
474
|
+
})
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Module Package.json
|
|
478
|
+
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"name": "my-nuxt-module",
|
|
482
|
+
"version": "1.0.0",
|
|
483
|
+
"type": "module",
|
|
484
|
+
"exports": {
|
|
485
|
+
".": {
|
|
486
|
+
"import": "./dist/module.mjs",
|
|
487
|
+
"require": "./dist/module.cjs"
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
"main": "./dist/module.cjs",
|
|
491
|
+
"module": "./dist/module.mjs",
|
|
492
|
+
"types": "./dist/types.d.ts",
|
|
493
|
+
"files": ["dist"],
|
|
494
|
+
"scripts": {
|
|
495
|
+
"dev": "nuxi dev playground",
|
|
496
|
+
"build": "nuxt-module-build build",
|
|
497
|
+
"prepare": "nuxt-module-build build --stub"
|
|
498
|
+
},
|
|
499
|
+
"dependencies": {
|
|
500
|
+
"@nuxt/kit": "^3.0.0"
|
|
501
|
+
},
|
|
502
|
+
"devDependencies": {
|
|
503
|
+
"@nuxt/module-builder": "latest",
|
|
504
|
+
"nuxt": "^3.0.0"
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Disabling Modules
|
|
510
|
+
|
|
511
|
+
Users can disable a module via config key:
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
// nuxt.config.ts
|
|
515
|
+
export default defineNuxtConfig({
|
|
516
|
+
// Disable entirely
|
|
517
|
+
myModule: false,
|
|
518
|
+
|
|
519
|
+
// Or with options
|
|
520
|
+
myModule: {
|
|
521
|
+
enabled: false,
|
|
522
|
+
},
|
|
523
|
+
})
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
## Development Workflow
|
|
527
|
+
|
|
528
|
+
1. **Create module**: `npx nuxi init -t module my-module`
|
|
529
|
+
2. **Develop**: `npm run dev` (runs playground)
|
|
530
|
+
3. **Build**: `npm run build`
|
|
531
|
+
4. **Test**: `npm run test`
|
|
532
|
+
|
|
533
|
+
## Best Practices
|
|
534
|
+
|
|
535
|
+
- Use `createResolver(import.meta.url)` for all path resolution
|
|
536
|
+
- Prefix components to avoid naming conflicts
|
|
537
|
+
- Make options type-safe with `ModuleOptions` interface
|
|
538
|
+
- Use `moduleDependencies` instead of `installModule`
|
|
539
|
+
- Provide sensible defaults for all options
|
|
540
|
+
- Add compatibility requirements in `meta.compatibility`
|
|
541
|
+
- Use virtual files for dynamic configuration
|
|
542
|
+
- Separate client/server plugins appropriately
|
|
543
|
+
|
|
544
|
+
<!--
|
|
545
|
+
Source references:
|
|
546
|
+
- https://nuxt.com/docs/api/kit/modules
|
|
547
|
+
- https://nuxt.com/docs/api/kit/components
|
|
548
|
+
- https://nuxt.com/docs/api/kit/autoimports
|
|
549
|
+
- https://nuxt.com/docs/api/kit/plugins
|
|
550
|
+
- https://nuxt.com/docs/api/kit/templates
|
|
551
|
+
- https://nuxt.com/docs/api/kit/nitro
|
|
552
|
+
- https://nuxt.com/docs/api/kit/pages
|
|
553
|
+
- https://nuxt.com/docs/api/kit/resolving
|
|
554
|
+
-->
|