vue-ready-modular 1.0.0

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/README.md ADDED
File without changes
package/bin/cli.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createModule } from "../lib/generator.js";
4
+
5
+ const args = process.argv.slice(2);
6
+
7
+ if (args[0] !== "make" || !args[1]) {
8
+ console.log("Usage: vue-modular make <module-name>");
9
+ process.exit(1);
10
+ }
11
+
12
+ createModule(args[1]);
@@ -0,0 +1,6 @@
1
+ # Vue Modular CLI
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ npm install -g vue-ready-modular
@@ -0,0 +1,76 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+
4
+ import { createDir, createFile } from "./utils/file.js";
5
+ import { capitalize } from "./utils/string.js";
6
+
7
+ // Templates
8
+ import indexTemplate from "./templates/index.template.js";
9
+ import routesTemplate from "./templates/routes.template.js";
10
+ import storeTemplate from "./templates/store.template.js";
11
+ import dataTemplate from "./templates/data.template.js";
12
+ import serviceTemplate from "./templates/service.template.js";
13
+ import queryTemplate from "./templates/query.template.js";
14
+ import mutationTemplate from "./templates/mutation.template.js";
15
+ import pageTemplate from "./templates/page.template.js";
16
+ import addModalTemplate from "./templates/modal-add.template.js";
17
+ import editModalTemplate from "./templates/modal-edit.template.js";
18
+ import viewModalTemplate from "./templates/modal-view.template.js";
19
+ import deleteModalTemplate from "./templates/modal-delete.template.js";
20
+
21
+ /**
22
+ * Generates a modular Vue.js module with a full structure.
23
+ * @param {string} name - Module name (e.g., "country")
24
+ */
25
+ export function createModule(name) {
26
+ const module = name.toLowerCase();
27
+ const Module = capitalize(module);
28
+
29
+ const basePath = path.join(process.cwd(), "src/modules", module);
30
+
31
+ // Prevent overwriting existing module
32
+ if (fs.existsSync(basePath)) {
33
+ console.error(`❌ Module "${module}" already exists`);
34
+ process.exit(1);
35
+ }
36
+
37
+ // -----------------------------
38
+ // 1. Create folder structure
39
+ // -----------------------------
40
+ const folders = [
41
+ `${basePath}/stores`,
42
+ `${basePath}/pages/components`,
43
+ `${basePath}/data`,
44
+ `${basePath}/services`,
45
+ `${basePath}/queries`,
46
+ ];
47
+
48
+ folders.forEach(createDir);
49
+
50
+ // -----------------------------
51
+ // 2. Prepare template context
52
+ // -----------------------------
53
+ const ctx = { name: module, Name: Module };
54
+
55
+ // -----------------------------
56
+ // 3. Create core module files
57
+ // -----------------------------
58
+ createFile(`${basePath}/index.js`, indexTemplate(ctx));
59
+ createFile(`${basePath}/routes.js`, routesTemplate(ctx));
60
+ createFile(`${basePath}/stores/${module}Store.js`, storeTemplate(ctx));
61
+ createFile(`${basePath}/data/${module}Data.js`, dataTemplate(ctx));
62
+ createFile(`${basePath}/services/${module}Service.js`, serviceTemplate(ctx));
63
+ createFile(`${basePath}/queries/use${Module}sQuery.js`, queryTemplate(ctx));
64
+ createFile(`${basePath}/queries/use${Module}Mutations.js`, mutationTemplate(ctx));
65
+
66
+ // -----------------------------
67
+ // 4. Create page & modal components
68
+ // -----------------------------
69
+ createFile(`${basePath}/pages/${Module}Page.vue`, pageTemplate(ctx));
70
+ createFile(`${basePath}/pages/components/AddModal.vue`, addModalTemplate(ctx));
71
+ createFile(`${basePath}/pages/components/EditModal.vue`, editModalTemplate(ctx));
72
+ createFile(`${basePath}/pages/components/ViewModal.vue`, viewModalTemplate(ctx));
73
+ createFile(`${basePath}/pages/components/DeleteModal.vue`, deleteModalTemplate(ctx));
74
+
75
+ console.log(`✅ Module "${module}" created successfully`);
76
+ }
@@ -0,0 +1,10 @@
1
+ export default ({ name }) => `const ${name}Data = [
2
+ { id: 1, name: 'Test Name' },
3
+ { id: 2, name: 'Test Name 2' },
4
+ { id: 3, name: 'Test Name 3' },
5
+ { id: 4, name: 'Test Name 4' },
6
+ { id: 5, name: 'Test Name 5' }
7
+ ]
8
+
9
+ export default ${name}Data
10
+ `;
@@ -0,0 +1,6 @@
1
+ export default () => `import routes from './routes';
2
+
3
+ export default {
4
+ routes
5
+ };
6
+ `;
@@ -0,0 +1,59 @@
1
+ export default ({ name, Name }) => `<template>
2
+ <BaseModal
3
+ :isVisible="store.isModal"
4
+ :title="\`Add \${store.moduleName}\`"
5
+ :maxWidth="\`\${store.modalWidth}\`"
6
+ @close="store.handleToggleModal"
7
+ >
8
+ <form @submit.prevent="handleSubmit" class="space-y-4">
9
+ <!-- ${Name} Name -->
10
+ <div>
11
+ <BaseLabel for="name">Name</BaseLabel>
12
+ <BaseInput
13
+ id="name"
14
+ v-model="formData.name"
15
+ :required="true"
16
+ placeholder="Eg: Example Name"
17
+ />
18
+ </div>
19
+
20
+ <!-- Actions -->
21
+ <div class="flex justify-end gap-2 pt-4">
22
+ <BaseButton
23
+ class="bg-yellow-600 hover:bg-yellow-700"
24
+ type="button"
25
+ @click="store.handleToggleModal"
26
+ >Cancel</BaseButton>
27
+ <BaseButton type="submit">Save</BaseButton>
28
+ </div>
29
+ </form>
30
+ </BaseModal>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { ref } from 'vue'
35
+ import { use${Name}Mutations } from '@/modules/${name}/queries/use${Name}Mutations'
36
+ import app from '@/shared/config/appConfig'
37
+ import { use${Name}Store } from '@/modules/${name}/stores/${name}Store'
38
+
39
+ const store = use${Name}Store()
40
+
41
+ const formData = ref({
42
+ name: app.moduleLocal ? 'Test Name' : '',
43
+ })
44
+
45
+ const { submit } = use${Name}Mutations(store.moduleName, {
46
+ onSuccess() {
47
+ store.handleToggleModal() // ✅ close modal
48
+ store.handleReset(formData.value) // ✅ reset form
49
+ },
50
+ onError: (error) => {
51
+ console.log('Custom error handling', error)
52
+ },
53
+ })
54
+
55
+ const handleSubmit = async () => {
56
+ await submit.mutateAsync(formData.value)
57
+ }
58
+ </script>
59
+ `;
@@ -0,0 +1,52 @@
1
+ export default ({ name, Name }) => `<template>
2
+ <BaseModal
3
+ :isVisible="store.isDeleteModal"
4
+ :title="\`Are you sure you want to delete this \${store.moduleName}?\`"
5
+ :maxWidth="\`\${store.modalWidth}\`"
6
+ @close="store.handleToggleModal"
7
+ >
8
+ <form @submit.prevent="handleSubmit" class="space-y-4">
9
+ <div class="space-y-2">
10
+ <p class="text-lg">
11
+ <strong>ID:</strong>
12
+ {{ store.item.id }}
13
+ </p>
14
+ <p class="text-lg">
15
+ <strong>Name:</strong>
16
+ {{ store.item.name }}
17
+ </p>
18
+ </div>
19
+
20
+ <!-- Actions -->
21
+ <div class="flex justify-end gap-2 pt-4">
22
+ <BaseButton
23
+ class="bg-yellow-600 hover:bg-yellow-700"
24
+ type="button"
25
+ @click="store.handleToggleModal"
26
+ >Cancel</BaseButton>
27
+ <BaseButton type="submit" class="bg-red-600 hover:bg-red-700">Delete</BaseButton>
28
+ </div>
29
+ </form>
30
+ </BaseModal>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { use${Name}Mutations } from '@/modules/${name}/queries/use${Name}Mutations'
35
+ import { use${Name}Store } from '@/modules/${name}/stores/${name}Store'
36
+
37
+ const store = use${Name}Store()
38
+
39
+ const { remove } = use${Name}Mutations(store.moduleName, {
40
+ onSuccess() {
41
+ store.handleToggleModal() // ✅ close modal
42
+ },
43
+ onError: (error) => {
44
+ console.log('Custom error handling', error)
45
+ },
46
+ })
47
+
48
+ const handleSubmit = async () => {
49
+ await remove.mutateAsync(store.item.id)
50
+ }
51
+ </script>
52
+ `;
@@ -0,0 +1,69 @@
1
+ export default ({ name, Name }) => `<template>
2
+ <BaseModal
3
+ :isVisible="store.isEditModal"
4
+ :title="\`Edit \${store.moduleName}\`"
5
+ :maxWidth="\`\${store.modalWidth}\`"
6
+ @close="store.handleToggleModal"
7
+ >
8
+ <form @submit.prevent="handleSubmit" class="space-y-4">
9
+ <!-- ${Name} Name -->
10
+ <div>
11
+ <BaseLabel for="name">Name</BaseLabel>
12
+ <BaseInput
13
+ id="name"
14
+ v-model="formData.name"
15
+ placeholder="Eg: Example Name"
16
+ />
17
+ </div>
18
+
19
+ <!-- Actions -->
20
+ <div class="flex justify-end gap-2 pt-4">
21
+ <BaseButton
22
+ class="bg-yellow-600 hover:bg-yellow-700"
23
+ type="button"
24
+ @click="store.handleToggleModal"
25
+ >Cancel</BaseButton>
26
+ <BaseButton type="submit">Save</BaseButton>
27
+ </div>
28
+ </form>
29
+ </BaseModal>
30
+ </template>
31
+
32
+ <script setup>
33
+ import { ref, watch } from 'vue'
34
+ import { use${Name}Mutations } from '@/modules/${name}/queries/use${Name}Mutations'
35
+ import { use${Name}Store } from '@/modules/${name}/stores/${name}Store'
36
+
37
+ const store = use${Name}Store()
38
+
39
+ const formData = ref({
40
+ name: '',
41
+ id: '',
42
+ })
43
+
44
+ // Prefill form when selected item changes
45
+ watch(
46
+ () => store.item,
47
+ (newItem) => {
48
+ if (newItem) {
49
+ formData.value.name = newItem.name || ''
50
+ formData.value.id = newItem.id || ''
51
+ }
52
+ },
53
+ { immediate: true }
54
+ )
55
+
56
+ const { update } = use${Name}Mutations(store.moduleName, {
57
+ onSuccess() {
58
+ store.handleToggleModal() // ✅ close modal
59
+ },
60
+ onError: (error) => {
61
+ console.log('Custom error handling', error)
62
+ },
63
+ })
64
+
65
+ const handleSubmit = async () => {
66
+ await update.mutateAsync(formData.value)
67
+ }
68
+ </script>
69
+ `;
@@ -0,0 +1,33 @@
1
+ export default ({ name, Name }) => `<template>
2
+ <BaseModal
3
+ :isVisible="store.isViewModal"
4
+ :title="\`View \${store.moduleName} Details\`"
5
+ :maxWidth="\`\${store.modalWidth}\`"
6
+ @close="store.handleToggleModal"
7
+ >
8
+ <div class="space-y-2">
9
+ <p class="text-lg">
10
+ <strong>ID:</strong>
11
+ {{ store.item.id }}
12
+ </p>
13
+ <p class="text-lg">
14
+ <strong>Name:</strong>
15
+ {{ store.item.name }}
16
+ </p>
17
+ <p class="text-lg">
18
+ <strong>Created At:</strong>
19
+ {{ store.item.created_at }}
20
+ </p>
21
+ <p class="text-lg">
22
+ <strong>Updated At:</strong>
23
+ {{ store.item.updated_at }}
24
+ </p>
25
+ </div>
26
+ </BaseModal>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { use${Name}Store } from '@/modules/${name}/stores/${name}Store'
31
+ const store = use${Name}Store()
32
+ </script>
33
+ `;
@@ -0,0 +1,29 @@
1
+ export default ({
2
+ name,
3
+ Name,
4
+ }) => `import { useMutation, useQueryClient } from '@tanstack/vue-query'
5
+ import { submitData, updateData, deleteItem } from '../services/${name}Service'
6
+ import { toast } from '@/shared/config/toastConfig'
7
+
8
+ export function use${Name}Mutations(moduleName, options = {}) {
9
+ const queryClient = useQueryClient()
10
+
11
+ const handleSuccess = (data, variables) => {
12
+ toast.success(\`\${moduleName} operation successful\`)
13
+ queryClient.invalidateQueries(['${name}s'])
14
+ options.onSuccess?.(data, variables) // ✅ generic callback
15
+ }
16
+
17
+ const handleError = (error) => {
18
+ console.error(error)
19
+ toast.error(\`Request Failed: \${error?.message || 'Unknown error'}\`)
20
+ options.onError?.(error)
21
+ }
22
+
23
+ const submit = useMutation({ mutationFn: submitData, onSuccess: handleSuccess, onError: handleError })
24
+ const update = useMutation({ mutationFn: updateData, onSuccess: handleSuccess, onError: handleError })
25
+ const remove = useMutation({ mutationFn: deleteItem, onSuccess: handleSuccess, onError: handleError })
26
+
27
+ return { submit, update, remove }
28
+ }
29
+ `;
@@ -0,0 +1,61 @@
1
+ export default ({ name, Name }) => `<template>
2
+ <div class="min-h-screen bg-gray-50">
3
+ <div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
4
+ <PageTitle>{{ store.moduleName }} List</PageTitle>
5
+ <BaseButton @click="store.handleToggleModal('add')">
6
+ Add {{ store.moduleName }}
7
+ </BaseButton>
8
+ </div>
9
+
10
+ <div class="rounded-lg bg-white shadow-sm">
11
+ <BaseTable
12
+ v-if="!isLoading"
13
+ :columns="columns"
14
+ :rows="rows"
15
+ show-actions
16
+ @view="onView"
17
+ @edit="onEdit"
18
+ @delete="onDelete"
19
+ />
20
+
21
+ <AddModal />
22
+ <EditModal />
23
+ <ViewModal />
24
+ <DeleteModal />
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup>
30
+ import { computed, defineAsyncComponent } from 'vue'
31
+ import { use${Name}sQuery } from '../queries/use${Name}sQuery'
32
+ import { use${Name}Store } from '../stores/${name}Store'
33
+
34
+ const ViewModal = defineAsyncComponent(() => import('./components/ViewModal.vue'))
35
+ const AddModal = defineAsyncComponent(() => import('./components/AddModal.vue'))
36
+ const EditModal = defineAsyncComponent(() => import('./components/EditModal.vue'))
37
+ const DeleteModal = defineAsyncComponent(() => import('./components/DeleteModal.vue'))
38
+
39
+ const store = use${Name}Store()
40
+ const { data, isLoading } = use${Name}sQuery()
41
+
42
+ const columns = [
43
+ { key: 'sl', label: 'SL' },
44
+ { key: 'name', label: '${Name}' },
45
+ { key: 'created_at', label: 'Created At' },
46
+ { key: 'updated_at', label: 'Updated At' }
47
+ ]
48
+
49
+ const rows = computed(() => data.value?.data?.data ?? [])
50
+
51
+ function onView(row) {
52
+ store.handleToggleModal('view', row)
53
+ }
54
+ function onEdit(row) {
55
+ store.handleToggleModal('edit', row)
56
+ }
57
+ function onDelete(row) {
58
+ store.handleToggleModal('delete', row)
59
+ }
60
+ </script>
61
+ `;
@@ -0,0 +1,19 @@
1
+ export default ({
2
+ name,
3
+ Name,
4
+ }) => `import { useQuery } from '@tanstack/vue-query'
5
+ import { fetchAll } from '../services/${name}Service'
6
+
7
+ export function use${Name}sQuery(params = {}) {
8
+ return useQuery({
9
+ queryKey: ['${name}s', params],
10
+ queryFn: () => fetchAll(params),
11
+ staleTime: 5 * 60 * 1000, // 5 minutes
12
+ cacheTime: 1000 * 60 * 60, // 1 hour
13
+ keepPreviousData: true,
14
+ meta: {
15
+ persist: true, // ✅ persisted
16
+ },
17
+ })
18
+ }
19
+ `;
@@ -0,0 +1,21 @@
1
+ export default ({
2
+ name,
3
+ Name,
4
+ }) => `import ${Name}Page from './pages/${Name}Page.vue'
5
+ import DashboardLayout from '@/shared/layouts/DashboardLayout.vue'
6
+
7
+ export default [
8
+ {
9
+ path: '/${name}s',
10
+ component: DashboardLayout,
11
+ meta: { requiresAuth: true },
12
+ children: [
13
+ {
14
+ path: '',
15
+ name: '${Name}List',
16
+ component: ${Name}Page,
17
+ },
18
+ ],
19
+ },
20
+ ]
21
+ `;
@@ -0,0 +1,43 @@
1
+ export default ({
2
+ name,
3
+ Name,
4
+ }) => `import { useApi } from '@/shared/composables/useApi'
5
+
6
+ const BASE_URL = '/${name}s'
7
+
8
+ const response = (api) => ({
9
+ data: api.data.value,
10
+ error: api.error.value,
11
+ })
12
+
13
+ export async function fetchAll() {
14
+ const api = useApi()
15
+ await api.sendRequest(BASE_URL)
16
+ return response(api)
17
+ }
18
+
19
+ export async function submitData(payload) {
20
+ const api = useApi()
21
+ await api.sendRequest(BASE_URL, 'POST', payload)
22
+ if (api.error.value) {
23
+ throw api.error.value // 🚨 so useMutation.onError triggers
24
+ }
25
+ return api.data.value
26
+ }
27
+
28
+ export async function updateData(payload) {
29
+ const api = useApi()
30
+ await api.sendRequest(\`\${BASE_URL}/\${payload.id}\`, 'POST', payload, {
31
+ headers: { 'X-HTTP-Method-Override': 'PUT' }
32
+ })
33
+ if (api.error.value) throw api.error.value
34
+ return api.data.value
35
+ }
36
+
37
+ export async function deleteItem(id) {
38
+ const api = useApi()
39
+ await api.sendRequest(\`\${BASE_URL}/\${id}\`, 'DELETE')
40
+ if (api.error.value) throw api.error.value
41
+ return api.data.value
42
+ }
43
+ `;
@@ -0,0 +1,31 @@
1
+ export default ({ name, Name }) => `import { defineStore } from 'pinia'
2
+ import { useModalHelpers } from '@/shared/composables/useModalHelpers'
3
+
4
+ export const use${Name}Store = defineStore('${name}', () => {
5
+
6
+ const {
7
+ item,
8
+ isModal,
9
+ isViewModal,
10
+ isEditModal,
11
+ isDeleteModal,
12
+ handleToggleModal,
13
+ handleReset,
14
+ } = useModalHelpers()
15
+
16
+ const moduleName = '${Name}'
17
+ const modalWidth = '30vw'
18
+
19
+ return {
20
+ item,
21
+ isModal,
22
+ isViewModal,
23
+ isEditModal,
24
+ isDeleteModal,
25
+ moduleName,
26
+ modalWidth,
27
+ handleToggleModal,
28
+ handleReset,
29
+ }
30
+ })
31
+ `;
@@ -0,0 +1,9 @@
1
+ import fs from 'fs';
2
+
3
+ export function createDir(dir) {
4
+ fs.mkdirSync(dir, { recursive: true });
5
+ }
6
+
7
+ export function createFile(filePath, content) {
8
+ fs.writeFileSync(filePath, content);
9
+ }
@@ -0,0 +1,3 @@
1
+ export function capitalize(str) {
2
+ return str.charAt(0).toUpperCase() + str.slice(1);
3
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "vue-ready-modular",
3
+ "version": "1.0.0",
4
+ "description": "Vue.js module generator",
5
+ "main": "lib/generator.js",
6
+ "bin": {
7
+ "vue-modular": "./bin/cli.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "test": "echo \"No tests yet\""
12
+ },
13
+ "keywords": ["vue", "generator", "module", "cli"],
14
+ "author": "Your Name",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "path": "^0.12.7"
18
+ }
19
+ }