feat: initial setup for nuxt devtools
This commit is contained in:
1
client/.nuxtrc
Normal file
1
client/.nuxtrc
Normal file
@ -0,0 +1 @@
|
||||
imports.autoImport=true
|
||||
15
client/app.vue
Normal file
15
client/app.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<Html>
|
||||
<Body h-screen>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</Body>
|
||||
</Html>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#__nuxt {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
17
client/nuxt.config.ts
Normal file
17
client/nuxt.config.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { PATH_CLIENT } from '../src/constants'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
ssr: false,
|
||||
modules: [
|
||||
'@nuxt/devtools-ui-kit',
|
||||
],
|
||||
nitro: {
|
||||
output: {
|
||||
publicDir: resolve(__dirname, '../dist/client'),
|
||||
},
|
||||
},
|
||||
app: {
|
||||
baseURL: PATH_CLIENT,
|
||||
},
|
||||
})
|
||||
4
client/package.json
Normal file
4
client/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "nuxt-mongoose-client",
|
||||
"private": true
|
||||
}
|
||||
30
package.json
30
package.json
@ -21,11 +21,14 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"prepack": "nuxt-module-build",
|
||||
"dev": "nuxi dev playground",
|
||||
"dev:build": "nuxi build playground",
|
||||
"dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
|
||||
"release": "npm run lint && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
||||
"prepack": "nuxt-module-build && npm run build:client",
|
||||
"build:client": "nuxi generate client",
|
||||
"dev:client": "nuxi dev client --port 3300",
|
||||
"dev": "npm run play:dev",
|
||||
"dev:prepare": "nuxt-module-build --stub && nuxi prepare client",
|
||||
"play:dev": "nuxi dev playground",
|
||||
"play:prod": "npm run prepack && nuxi dev playground",
|
||||
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest watch"
|
||||
@ -35,18 +38,31 @@
|
||||
"nuxt": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/devtools-kit": "^0.4.1",
|
||||
"@nuxt/kit": "^3.4.1",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"birpc": "^0.2.11",
|
||||
"defu": "^6.1.2",
|
||||
"mongoose": "^7.0.3"
|
||||
"flatted": "^3.2.7",
|
||||
"fs-extra": "^11.1.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"sirv": "^2.0.2",
|
||||
"tinyws": "^0.1.0",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.38.4",
|
||||
"@nuxt/devtools": "^0.4.1",
|
||||
"@nuxt/devtools-ui-kit": "^0.4.1",
|
||||
"@nuxt/module-builder": "^0.3.0",
|
||||
"@nuxt/schema": "^3.4.1",
|
||||
"@nuxt/test-utils": "^3.4.1",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@types/ws": "^8.5.4",
|
||||
"changelogen": "^0.5.3",
|
||||
"eslint": "^8.38.0",
|
||||
"mongoose": "^7.0.4",
|
||||
"nuxt": "^3.4.1",
|
||||
"vitest": "^0.30.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,28 @@
|
||||
export default defineNuxtConfig({
|
||||
modules: ['../src/module'],
|
||||
import { resolve } from 'node:path'
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
import { startSubprocess } from '@nuxt/devtools-kit'
|
||||
|
||||
mongoose: {
|
||||
uri: 'mongodb://127.0.0.1/nuxt-mongoose',
|
||||
},
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/devtools',
|
||||
'../src/module',
|
||||
defineNuxtModule({
|
||||
setup(_, nuxt) {
|
||||
if (!nuxt.options.dev)
|
||||
return
|
||||
|
||||
const process = startSubprocess(
|
||||
{
|
||||
command: 'npx',
|
||||
args: ['nuxi', 'dev', '--port', '3300'],
|
||||
cwd: resolve(__dirname, '../client'),
|
||||
},
|
||||
{
|
||||
id: 'nuxt-mongoose:client',
|
||||
name: 'Nuxt Mongoose Client Dev',
|
||||
},
|
||||
)
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
2182
pnpm-lock.yaml
generated
2182
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
3
src/constants/index.ts
Normal file
3
src/constants/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const PATH = '/__nuxt_mongoose__'
|
||||
export const PATH_ENTRY = `${PATH}/entry`
|
||||
export const PATH_CLIENT = `${PATH}/client`
|
||||
@ -1,11 +1,13 @@
|
||||
import { addServerPlugin, addTemplate, createResolver, defineNuxtModule } from '@nuxt/kit'
|
||||
import { addServerPlugin, addTemplate, createResolver, defineNuxtModule, logger } from '@nuxt/kit'
|
||||
import { pathExists } from 'fs-extra'
|
||||
import { tinyws } from 'tinyws'
|
||||
import { defu } from 'defu'
|
||||
import type { ConnectOptions } from 'mongoose'
|
||||
import sirv from 'sirv'
|
||||
|
||||
export interface ModuleOptions {
|
||||
uri?: string
|
||||
options?: ConnectOptions
|
||||
}
|
||||
import { PATH_CLIENT, PATH_ENTRY } from './constants'
|
||||
import type { ModuleOptions } from './types'
|
||||
|
||||
import { setupRPC } from './server-rpc'
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
meta: {
|
||||
@ -14,6 +16,7 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
},
|
||||
defaults: {
|
||||
uri: process.env.MONGODB_URI as string,
|
||||
devtools: true,
|
||||
options: {},
|
||||
},
|
||||
setup(options, nuxt) {
|
||||
@ -26,6 +29,39 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
nuxt.options.runtimeConfig.public.mongoose = defu(nuxt.options.runtimeConfig.public.mongoose || {}, {
|
||||
uri: options.uri,
|
||||
options: options.options,
|
||||
devtools: options.devtools,
|
||||
})
|
||||
|
||||
// Setup devtools UI
|
||||
const distResolve = (p: string) => {
|
||||
const cwd = resolve('.')
|
||||
if (cwd.endsWith('/dist'))
|
||||
return resolve(p)
|
||||
return resolve(`../dist/${p}`)
|
||||
}
|
||||
|
||||
const clientPath = distResolve('./client')
|
||||
const { middleware: rpcMiddleware } = setupRPC(nuxt, options)
|
||||
|
||||
nuxt.hook('vite:serverCreated', async (server) => {
|
||||
server.middlewares.use(PATH_ENTRY, tinyws() as any)
|
||||
server.middlewares.use(PATH_ENTRY, rpcMiddleware as any)
|
||||
if (await pathExists(clientPath))
|
||||
server.middlewares.use(PATH_CLIENT, sirv(clientPath, { dev: true, single: true }))
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
||||
// @ts-ignore runtime type
|
||||
nuxt.hook('devtools:customTabs', (iframeTabs) => {
|
||||
iframeTabs.push({
|
||||
name: 'mongoose',
|
||||
title: 'Mongoose',
|
||||
icon: 'skill-icons:mongodb',
|
||||
view: {
|
||||
type: 'iframe',
|
||||
src: PATH_CLIENT,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// virtual imports
|
||||
@ -55,5 +91,7 @@ export default defineNuxtModule<ModuleOptions>({
|
||||
|
||||
// Add server-plugin for database connection
|
||||
addServerPlugin(resolve('./runtime/server/plugins/mongoose.db'))
|
||||
|
||||
logger.success('`nuxt-mongoose` is ready!')
|
||||
},
|
||||
})
|
||||
|
||||
100
src/server-rpc/index.ts
Normal file
100
src/server-rpc/index.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import type { TinyWSRequest } from 'tinyws'
|
||||
import type { NodeIncomingMessage, NodeServerResponse } from 'h3'
|
||||
import type { WebSocket } from 'ws'
|
||||
import { createBirpcGroup } from 'birpc'
|
||||
import type { ChannelOptions } from 'birpc'
|
||||
|
||||
import { parse, stringify } from 'flatted'
|
||||
import type { Nuxt } from 'nuxt/schema'
|
||||
import type { ClientFunctions, ModuleOptions, NuxtDevtoolsServerContext, ServerFunctions } from '../types'
|
||||
|
||||
export function setupRPC(nuxt: Nuxt, options: ModuleOptions): any {
|
||||
const serverFunctions = {} as ServerFunctions
|
||||
const extendedRpcMap = new Map<string, any>()
|
||||
const rpc = createBirpcGroup<ClientFunctions, ServerFunctions>(
|
||||
serverFunctions,
|
||||
[],
|
||||
{
|
||||
resolver: (name, fn) => {
|
||||
if (fn)
|
||||
return fn
|
||||
|
||||
if (!name.includes(':'))
|
||||
return
|
||||
|
||||
const [namespace, fnName] = name.split(':')
|
||||
return extendedRpcMap.get(namespace)?.[fnName]
|
||||
},
|
||||
onError(error, name) {
|
||||
console.error(`[nuxt-devtools] RPC error on executing "${name}":`, error)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
function refresh(event: keyof ServerFunctions) {
|
||||
rpc.broadcast.refresh.asEvent(event)
|
||||
}
|
||||
|
||||
function extendServerRpc(namespace: string, functions: any): any {
|
||||
extendedRpcMap.set(namespace, functions)
|
||||
|
||||
return {
|
||||
broadcast: new Proxy({}, {
|
||||
get: (_, key) => {
|
||||
if (typeof key !== 'string')
|
||||
return
|
||||
return (rpc.broadcast as any)[`${namespace}:${key}`]
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
const ctx: NuxtDevtoolsServerContext = {
|
||||
nuxt,
|
||||
options,
|
||||
rpc,
|
||||
refresh,
|
||||
extendServerRpc,
|
||||
}
|
||||
|
||||
// @ts-expect-error untyped
|
||||
nuxt.devtools = ctx
|
||||
|
||||
Object.assign(serverFunctions, {
|
||||
// TODO: add rpc
|
||||
} satisfies Partial<ServerFunctions>)
|
||||
|
||||
const wsClients = new Set<WebSocket>()
|
||||
const middleware = async (req: NodeIncomingMessage & TinyWSRequest, _res: NodeServerResponse, next: Function) => {
|
||||
// Handle WebSocket
|
||||
if (req.ws) {
|
||||
const ws = await req.ws()
|
||||
wsClients.add(ws)
|
||||
const channel: ChannelOptions = {
|
||||
post: d => ws.send(d),
|
||||
on: fn => ws.on('message', fn),
|
||||
serialize: stringify,
|
||||
deserialize: parse,
|
||||
}
|
||||
rpc.updateChannels((c) => {
|
||||
c.push(channel)
|
||||
})
|
||||
ws.on('close', () => {
|
||||
wsClients.delete(ws)
|
||||
rpc.updateChannels((c) => {
|
||||
const index = c.indexOf(channel)
|
||||
if (index >= 0)
|
||||
c.splice(index, 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
middleware,
|
||||
...ctx,
|
||||
}
|
||||
}
|
||||
3
src/types/index.ts
Normal file
3
src/types/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './rpc'
|
||||
export * from './server-ctx'
|
||||
export * from './module-options'
|
||||
7
src/types/module-options.ts
Normal file
7
src/types/module-options.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { ConnectOptions } from 'mongoose'
|
||||
|
||||
export interface ModuleOptions {
|
||||
uri: string
|
||||
devtools: boolean
|
||||
options?: ConnectOptions
|
||||
}
|
||||
7
src/types/rpc.ts
Normal file
7
src/types/rpc.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface ServerFunctions {
|
||||
listCollections(): Promise<any>
|
||||
}
|
||||
|
||||
export interface ClientFunctions {
|
||||
refresh(type: string): void
|
||||
}
|
||||
15
src/types/server-ctx.ts
Normal file
15
src/types/server-ctx.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { BirpcGroup } from 'birpc'
|
||||
import type { Nuxt } from 'nuxt/schema'
|
||||
import type { ClientFunctions, ServerFunctions } from './rpc'
|
||||
import type { ModuleOptions } from './module-options'
|
||||
|
||||
export interface NuxtDevtoolsServerContext {
|
||||
nuxt: Nuxt
|
||||
options: ModuleOptions
|
||||
|
||||
rpc: BirpcGroup<ClientFunctions, ServerFunctions>
|
||||
|
||||
refresh: (event: keyof ServerFunctions) => void
|
||||
|
||||
extendServerRpc: <ClientFunctions = {}, ServerFunctions = {}>(name: string, functions: ServerFunctions) => BirpcGroup<ClientFunctions, ServerFunctions>
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"extends": "./playground/.nuxt/tsconfig.json"
|
||||
"extends": "./client/.nuxt/tsconfig.json"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user