- |
+ |
diff --git a/client/components/DrawerRight.vue b/client/components/DrawerRight.vue
new file mode 100644
index 0000000..e5d1231
--- /dev/null
+++ b/client/components/DrawerRight.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
diff --git a/client/pages/index.vue b/client/pages/index.vue
index df4ff79..94aa2bf 100644
--- a/client/pages/index.vue
+++ b/client/pages/index.vue
@@ -1,5 +1,6 @@
-
-
+
+
@@ -40,14 +54,27 @@ onMounted(() => {
{{ table.name }}
-
-
+
+
+
+
+
+
+
+
+ Select a collection to start
+
+
+
+
+
+
diff --git a/module.cjs b/module.cjs
new file mode 100644
index 0000000..e3ecaed
--- /dev/null
+++ b/module.cjs
@@ -0,0 +1,15 @@
+/* eslint-disable eslint-comments/no-unlimited-disable */
+/* eslint-disable */
+module.exports = function (...args) {
+ const nuxt = this.nuxt || args[1]
+ let _a
+ let version = (nuxt == null ? void 0 : nuxt._version) || (nuxt == null ? void 0 : nuxt.version) || ((_a = nuxt == null ? void 0 : nuxt.constructor) == null ? void 0 : _a.version) || ''
+ version = version.replace(/^v/g, '')
+ // Nuxt DevTools is not compatible with Nuxt 2, disabled
+ if (version.startsWith('2.')) {
+ return
+ }
+ return import('./dist/module.mjs').then(m => m.default.call(this, ...args))
+}
+const _meta = module.exports.meta = require('./dist/module.json')
+module.exports.getMeta = () => Promise.resolve(_meta)
diff --git a/package.json b/package.json
index 2dca8e5..875542d 100644
--- a/package.json
+++ b/package.json
@@ -11,24 +11,28 @@
"exports": {
".": {
"types": "./dist/types.d.ts",
- "require": "./dist/module.cjs",
+ "require": "./module.cjs",
"import": "./dist/module.mjs"
- }
+ },
+ "./types": {
+ "types": "./dist/types.d.ts",
+ "import": "./dist/types.mjs"
+ },
+ "./*": "./*"
},
- "main": "./dist/module.cjs",
+ "main": "./module.cjs",
"types": "./dist/types.d.ts",
"files": [
"dist"
],
"scripts": {
- "prepack": "nuxt-module-build && npm run build:client",
+ "build": "pnpm dev:prepare && pnpm build:module && pnpm 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",
+ "build:module": "nuxt-build-module",
+ "dev": "nuxi dev playground",
+ "dev:prepare": "nuxi prepare client",
+ "dev:prod": "npm run build && pnpm dev",
+ "release": "npm run lint && npm run test && npm run build && changelogen --release && npm publish && git push --follow-tags",
"lint": "eslint .",
"test": "vitest run",
"test:watch": "vitest watch"
diff --git a/playground/server/api/users.get.ts b/playground/server/api/users.get.ts
deleted file mode 100644
index a3fc245..0000000
--- a/playground/server/api/users.get.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { User } from '~/server/models/user.schema'
-
-export default defineEventHandler(() => {
- return User.find()
-})
diff --git a/playground/server/models/user.schema.ts b/playground/server/models/user.schema.ts
deleted file mode 100644
index 83449d3..0000000
--- a/playground/server/models/user.schema.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { defineMongooseModel } from '#nuxt/mongoose'
-
-export const User = defineMongooseModel({
- name: 'User',
- schema: {
- name: {
- type: String,
- required: true,
- },
- },
-})
-
-// export const User = defineMongooseModel('User', {
-// name: {
-// type: String,
-// required: true,
-// },
-// })
diff --git a/src/module.ts b/src/module.ts
index 5530b12..2127b53 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -1,4 +1,4 @@
-import { addServerPlugin, addTemplate, createResolver, defineNuxtModule, logger } from '@nuxt/kit'
+import { addImportsDir, addServerPlugin, addTemplate, createResolver, defineNuxtModule, logger } from '@nuxt/kit'
import { pathExists } from 'fs-extra'
import { tinyws } from 'tinyws'
import { defu } from 'defu'
@@ -22,6 +22,8 @@ export default defineNuxtModule({
setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url)
+ addImportsDir(resolve('./runtime/composables'))
+
if (!options.uri)
console.warn('Missing `MONGODB_URI` in `.env`')
diff --git a/src/runtime/composables/useMongoose.ts b/src/runtime/composables/useMongoose.ts
new file mode 100644
index 0000000..7878dcd
--- /dev/null
+++ b/src/runtime/composables/useMongoose.ts
@@ -0,0 +1,8 @@
+import type { mongo } from 'mongoose'
+import { connection } from 'mongoose'
+
+export function useMongoose(): { db: mongo.Db } {
+ return {
+ db: connection?.db,
+ }
+}
diff --git a/src/server-rpc/database.ts b/src/server-rpc/database.ts
index 121569a..3958b49 100644
--- a/src/server-rpc/database.ts
+++ b/src/server-rpc/database.ts
@@ -1,39 +1,38 @@
import { logger } from '@nuxt/kit'
-import { ObjectId } from 'mongodb'
-import { connection as MongooseConnection, connect } from 'mongoose'
+import mongoose from 'mongoose'
import type { NuxtDevtoolsServerContext, ServerFunctions } from '../types'
export function setupDatabaseRPC({ nuxt }: NuxtDevtoolsServerContext): any {
// TODO:
- connect('mongodb://127.0.0.1:27017/arcane')
+ mongoose.connect('mongodb://127.0.0.1:27017/arcane')
return {
async createCollection(name: string) {
- return await MongooseConnection.db.createCollection(name)
+ return await mongoose.connection.db.createCollection(name)
},
async listCollections() {
- return await MongooseConnection.db.listCollections().toArray()
+ return await mongoose.connection.db.listCollections().toArray()
},
async getCollection(name: string) {
- return MongooseConnection.db.collection(name)
+ return mongoose.connection.db.collection(name)
},
async dropCollection(name: string) {
- return await MongooseConnection.db.collection(name).drop()
+ return await mongoose.connection.db.collection(name).drop()
},
async createDocument(collection: string, data: any) {
- return await MongooseConnection.db.collection(collection).insertOne(data)
+ return await mongoose.connection.db.collection(collection).insertOne(data)
},
async listDocuments(collection: string) {
- return await MongooseConnection.db.collection(collection).find().toArray()
+ return await mongoose.connection.db.collection(collection).find().toArray()
},
async getDocument(collection: string, document: {}) {
- return await MongooseConnection.db.collection(collection).findOne({ document })
+ return await mongoose.connection.db.collection(collection).findOne({ document })
},
async updateDocument(collection: string, data: any) {
const { _id, ...rest } = data
try {
- return await MongooseConnection.db.collection(collection).findOneAndUpdate({ _id: new ObjectId(_id) }, { $set: rest })
+ return await mongoose.connection.db.collection(collection).findOneAndUpdate({ _id: new mongoose.Types.ObjectId(_id) }, { $set: rest })
}
catch (error) {
logger.log(error)
@@ -41,7 +40,7 @@ export function setupDatabaseRPC({ nuxt }: NuxtDevtoolsServerContext): any {
}
},
async deleteDocument(collection: string, id: string) {
- return await MongooseConnection.db.collection(collection).deleteOne({ _id: new ObjectId(id) })
+ return await mongoose.connection.db.collection(collection).deleteOne({ _id: new mongoose.Types.ObjectId(id) })
},
} satisfies Partial
}
diff --git a/src/server-rpc/index.ts b/src/server-rpc/index.ts
index 651b4bf..8fa84b1 100644
--- a/src/server-rpc/index.ts
+++ b/src/server-rpc/index.ts
@@ -8,6 +8,7 @@ import { parse, stringify } from 'flatted'
import type { Nuxt } from 'nuxt/schema'
import type { ClientFunctions, ModuleOptions, NuxtDevtoolsServerContext, ServerFunctions } from '../types'
import { setupDatabaseRPC } from './database'
+import { setupResourceRPC } from './resource'
export function setupRPC(nuxt: Nuxt, options: ModuleOptions): any {
const serverFunctions = {} as ServerFunctions
@@ -63,6 +64,7 @@ export function setupRPC(nuxt: Nuxt, options: ModuleOptions): any {
Object.assign(serverFunctions, {
...setupDatabaseRPC(ctx),
+ ...setupResourceRPC(ctx),
} satisfies Partial)
const wsClients = new Set()
diff --git a/src/server-rpc/resource.ts b/src/server-rpc/resource.ts
new file mode 100644
index 0000000..defdf8c
--- /dev/null
+++ b/src/server-rpc/resource.ts
@@ -0,0 +1,77 @@
+import fs from 'fs-extra'
+import { resolve } from 'pathe'
+import mongoose from 'mongoose'
+import type { Collection, NuxtDevtoolsServerContext, Resource, ServerFunctions } from '../types'
+import { generateApiRoute, generateSchemaFile } from '../utils/schematics'
+import { capitalize, pluralize, singularize } from '../utils/formatting'
+
+export function setupResourceRPC({ nuxt }: NuxtDevtoolsServerContext): any {
+ return {
+ async generateResource(collection: Collection, resources: Resource[]) {
+ const singular = singularize(collection.name).toLowerCase()
+ const plural = pluralize(collection.name).toLowerCase()
+ const dbName = capitalize(singular)
+
+ if (collection.fields) {
+ if (!fs.existsSync(resolve(nuxt.options.serverDir, 'models', `${singular}.schema.ts`))) {
+ fs.ensureDirSync(resolve(nuxt.options.serverDir, 'models'))
+ fs.writeFileSync(
+ resolve(nuxt.options.serverDir, 'models', `${singular}.schema.ts`),
+ generateSchemaFile(dbName, collection.fields),
+ )
+ }
+
+ const model = { name: dbName, path: `${singular}.schema` }
+ fs.ensureDirSync(resolve(nuxt.options.serverDir, `api/${plural}`))
+
+ // create resources
+ // TODO: fix this
+ resources.forEach((route: any) => {
+ let fileName = ''
+ if (route.type === 'index')
+ fileName = 'index.get.ts'
+
+ if (route.type === 'create')
+ fileName = 'create.post.ts'
+
+ if (route.type === 'show')
+ fileName = `[_${route.by}].get.ts`.replace('_', '')
+
+ if (route.type === 'put')
+ fileName = `[_${route.by}].put.ts`.replace('_', '')
+
+ if (route.type === 'delete')
+ fileName = `[_${route.by}].delete.ts`.replace('_', '')
+
+ if (!fs.existsSync(resolve(nuxt.options.serverDir, `api/${plural}`, fileName))) {
+ const content = generateApiRoute(route.type, { model, by: route.by })
+ fs.writeFileSync(resolve(nuxt.options.serverDir, 'api', plural, fileName), content)
+ }
+ })
+ }
+
+ // create collection if not exists
+ if (!mongoose.connection.modelNames().includes(dbName))
+ await mongoose.connection.db.createCollection(plural)
+
+ // create rows and columns
+ },
+ async resourceSchema(collection: string) {
+ // get schema file if exists
+ const singular = singularize(collection).toLowerCase()
+
+ if (fs.existsSync(resolve(nuxt.options.serverDir, 'models', `${singular}.schema.ts`))) {
+ const schemaPath = resolve(nuxt.options.serverDir, 'models', `${singular}.schema.ts`)
+
+ const content = fs.readFileSync(schemaPath, 'utf-8').match(/schema: \{(.|\n)*\}/g)
+
+ if (content) {
+ const schemaString = content[0].replace('schema: ', '').slice(0, -3)
+ // eslint-disable-next-line no-eval
+ const schema = eval(`(${schemaString})`)
+ return schema
+ }
+ }
+ },
+ } satisfies Partial
+}
diff --git a/src/types/rpc.ts b/src/types/rpc.ts
index efb9286..a90c32f 100644
--- a/src/types/rpc.ts
+++ b/src/types/rpc.ts
@@ -1,18 +1,32 @@
export interface ServerFunctions {
- // collections
+ // Database - collections
createCollection(name: string): Promise
listCollections(): Promise
getCollection(name: string): Promise
dropCollection(name: string): Promise
- // documents
+ // Database - documents
createDocument(collection: string, data: any): Promise
listDocuments(collection: string): Promise
getDocument(collection: string, id: string): Promise
updateDocument(collection: string, data: any): Promise
deleteDocument(collection: string, id: string): Promise
+
+ // Resource - api-routes & models
+ generateResource(collection: Collection, resources: Resource[]): Promise
+ resourceSchema(collection: string): Promise
}
export interface ClientFunctions {
refresh(type: string): void
}
+
+export interface Collection {
+ name: string
+ fields?: {}[]
+}
+
+export interface Resource {
+ type: string
+ by?: string
+}
diff --git a/src/utils/formatting.ts b/src/utils/formatting.ts
new file mode 100644
index 0000000..4ee9177
--- /dev/null
+++ b/src/utils/formatting.ts
@@ -0,0 +1,22 @@
+import plrz from 'pluralize'
+
+export function normalizeToKebabOrSnakeCase(str: string) {
+ const STRING_DASHERIZE_REGEXP = /\s/g
+ const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g
+ return str
+ .replace(STRING_DECAMELIZE_REGEXP, '$1-$2')
+ .toLowerCase()
+ .replace(STRING_DASHERIZE_REGEXP, '-')
+}
+
+export function pluralize(str: string) {
+ return plrz.plural(str)
+}
+
+export function singularize(str: string) {
+ return plrz.singular(str)
+}
+
+export function capitalize(str: string) {
+ return str.charAt(0).toUpperCase() + str.slice(1)
+}
diff --git a/src/utils/schematics.ts b/src/utils/schematics.ts
new file mode 100644
index 0000000..b70e25b
--- /dev/null
+++ b/src/utils/schematics.ts
@@ -0,0 +1,49 @@
+import { capitalize } from './formatting'
+
+export function generateSchemaFile(name: string, fields: any) {
+ name = capitalize(name)
+ // TODO: fix spacing
+ const outputObject = JSON.stringify(
+ fields.reduce((acc: any, curr: any) => {
+ const { name, ...rest } = curr
+ acc[name] = rest
+ return acc
+ }, {}),
+ null, 2)
+ .replace(/"([^"]+)":/g, '$1:')
+ .replace(/"(\w+)":/g, '$1:')
+ .replace(/\s*"\w+":/g, match => match.trim())
+ .replace(/"string"/g, '\'string\'')
+
+ return `import { defineMongooseModel } from '#nuxt/mongoose'
+
+export const ${name}Schema = defineMongooseModel({
+ name: '${name}',
+ schema: ${outputObject},
+})
+`
+}
+
+export function generateApiRoute(action: string, { model, by }: { model: { name: string; path: string }; by?: string }) {
+ const modelName = capitalize(model.name)
+ const schemaImport = `import { ${modelName}Schema } from '../../models/${model.path}'\n\n`
+ const operation = {
+ index: `return await ${modelName}Schema.find({})`,
+ create: `return await new ${modelName}Schema(body).save()`,
+ show: `return await ${modelName}Schema.findOne({ ${by}: event.context.params?.${by} })`,
+ put: `return await ${modelName}Schema.findOneAndUpdate({ ${by}: event.context.params?.${by} }, body, { new: true })`,
+ delete: `return await ${modelName}Schema.findOneAndDelete({ ${by}: event.context.params?.${by} })`,
+ }[action]
+
+ const main = `try {
+ ${operation}
+ }
+ catch (error) {
+ return error
+ }`
+
+ return `${schemaImport}export default defineEventHandler(async (event) => {
+ ${(action === 'create' || action === 'put') ? `const body = await readBody(event)\n ${main}` : main}
+})
+`
+}
|