vue-art-e4n 0.0.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.
@@ -0,0 +1,1185 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { dirname } from "path";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ const templatesDir = path.join(__dirname, "..", "templates");
9
+
10
+ /**
11
+ * Creates project folders and base files based on framework & architecture.
12
+ *
13
+ * @param {Object} options
14
+ * @param {string} options.projectName
15
+ * @param {"vue"|"nuxt"|"vuetify"} options.framework
16
+ * @param {string} options.architecture
17
+ * @param {string[]} options.features
18
+ *
19
+ * @returns {void}
20
+ */
21
+ function createVueVuetifyStructure(baseDir, framework, architecture, features) {
22
+ const srcDir = path.join(baseDir, "src");
23
+ fs.ensureDirSync(srcDir);
24
+
25
+ const hasVuetify = framework === "vuetify";
26
+ const useTs = features.includes("ts");
27
+
28
+ if (framework === "vue") {
29
+ // Vue projects
30
+ if (architecture === "simple") {
31
+ fs.ensureDirSync(path.join(srcDir, "components"));
32
+ fs.ensureDirSync(path.join(srcDir, "views"));
33
+ fs.ensureDirSync(path.join(srcDir, "router"));
34
+ fs.ensureDirSync(path.join(srcDir, "store"));
35
+ fs.ensureDirSync(path.join(srcDir, "assets"));
36
+ } else if (architecture === "feature") {
37
+ fs.ensureDirSync(path.join(srcDir, "features", "example"));
38
+ fs.ensureDirSync(path.join(srcDir, "components"));
39
+ fs.ensureDirSync(path.join(srcDir, "views"));
40
+ fs.ensureDirSync(path.join(srcDir, "router"));
41
+ fs.ensureDirSync(path.join(srcDir, "store"));
42
+ fs.ensureDirSync(path.join(srcDir, "assets"));
43
+ } else if (architecture === "atomic") {
44
+ fs.ensureDirSync(path.join(srcDir, "components", "atoms"));
45
+ fs.ensureDirSync(path.join(srcDir, "components", "molecules"));
46
+ fs.ensureDirSync(path.join(srcDir, "components", "organisms"));
47
+ fs.ensureDirSync(path.join(srcDir, "views"));
48
+ fs.ensureDirSync(path.join(srcDir, "assets"));
49
+ } else if (architecture === "enterprise") {
50
+ fs.ensureDirSync(path.join(srcDir, "modules"));
51
+ fs.ensureDirSync(path.join(srcDir, "features"));
52
+ fs.ensureDirSync(path.join(srcDir, "shared"));
53
+ fs.ensureDirSync(path.join(srcDir, "components", "atoms"));
54
+ fs.ensureDirSync(path.join(srcDir, "components", "molecules"));
55
+ fs.ensureDirSync(path.join(srcDir, "components", "organisms"));
56
+ fs.ensureDirSync(path.join(srcDir, "assets"));
57
+ }
58
+ } else if (framework === "vuetify") {
59
+ if (architecture === "simple") {
60
+ fs.ensureDirSync(path.join(srcDir, "components"));
61
+ fs.ensureDirSync(path.join(srcDir, "views"));
62
+ fs.ensureDirSync(path.join(srcDir, "router"));
63
+ fs.ensureDirSync(path.join(srcDir, "store"));
64
+ fs.ensureDirSync(path.join(srcDir, "plugins"));
65
+ fs.ensureDirSync(path.join(srcDir, "assets"));
66
+ } else if (architecture === "feature") {
67
+ fs.ensureDirSync(path.join(srcDir, "features", "example"));
68
+ fs.ensureDirSync(path.join(srcDir, "components"));
69
+ fs.ensureDirSync(path.join(srcDir, "views"));
70
+ fs.ensureDirSync(path.join(srcDir, "router"));
71
+ fs.ensureDirSync(path.join(srcDir, "store"));
72
+ fs.ensureDirSync(path.join(srcDir, "plugins"));
73
+ fs.ensureDirSync(path.join(srcDir, "assets"));
74
+ } else if (architecture === "enterprise") {
75
+ fs.ensureDirSync(path.join(srcDir, "modules"));
76
+ fs.ensureDirSync(path.join(srcDir, "features"));
77
+ fs.ensureDirSync(path.join(srcDir, "shared"));
78
+ fs.ensureDirSync(path.join(srcDir, "components"));
79
+ fs.ensureDirSync(path.join(srcDir, "plugins"));
80
+ fs.ensureDirSync(path.join(srcDir, "assets"));
81
+ }
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Creates directory structure for Nuxt projects based on architecture
87
+ */
88
+ function createNuxtStructure(baseDir, architecture, features) {
89
+ const useTs = features.includes("ts");
90
+
91
+ if (architecture === "default") {
92
+ fs.ensureDirSync(path.join(baseDir, "pages"));
93
+ fs.ensureDirSync(path.join(baseDir, "components"));
94
+ fs.ensureDirSync(path.join(baseDir, "composables"));
95
+ fs.ensureDirSync(path.join(baseDir, "plugins"));
96
+ fs.ensureDirSync(path.join(baseDir, "middleware"));
97
+ fs.ensureDirSync(path.join(baseDir, "server", "api"));
98
+ fs.ensureDirSync(path.join(baseDir, "assets"));
99
+ } else if (architecture === "feature") {
100
+ fs.ensureDirSync(path.join(baseDir, "pages"));
101
+ fs.ensureDirSync(path.join(baseDir, "features", "example"));
102
+ fs.ensureDirSync(path.join(baseDir, "components"));
103
+ fs.ensureDirSync(path.join(baseDir, "composables"));
104
+ fs.ensureDirSync(path.join(baseDir, "plugins"));
105
+ fs.ensureDirSync(path.join(baseDir, "middleware"));
106
+ fs.ensureDirSync(path.join(baseDir, "server", "api"));
107
+ fs.ensureDirSync(path.join(baseDir, "assets"));
108
+ } else if (architecture === "layered") {
109
+ fs.ensureDirSync(path.join(baseDir, "pages"));
110
+ fs.ensureDirSync(path.join(baseDir, "layers", "domain"));
111
+ fs.ensureDirSync(path.join(baseDir, "layers", "application"));
112
+ fs.ensureDirSync(path.join(baseDir, "layers", "presentation"));
113
+ fs.ensureDirSync(path.join(baseDir, "components"));
114
+ fs.ensureDirSync(path.join(baseDir, "composables"));
115
+ fs.ensureDirSync(path.join(baseDir, "plugins"));
116
+ fs.ensureDirSync(path.join(baseDir, "middleware"));
117
+ fs.ensureDirSync(path.join(baseDir, "server", "api"));
118
+ fs.ensureDirSync(path.join(baseDir, "assets"));
119
+ } else if (architecture === "enterprise") {
120
+ fs.ensureDirSync(path.join(baseDir, "pages"));
121
+ fs.ensureDirSync(path.join(baseDir, "features"));
122
+ fs.ensureDirSync(path.join(baseDir, "layers"));
123
+ fs.ensureDirSync(path.join(baseDir, "modules"));
124
+ fs.ensureDirSync(path.join(baseDir, "shared"));
125
+ fs.ensureDirSync(path.join(baseDir, "components"));
126
+ fs.ensureDirSync(path.join(baseDir, "composables"));
127
+ fs.ensureDirSync(path.join(baseDir, "plugins"));
128
+ fs.ensureDirSync(path.join(baseDir, "middleware"));
129
+ fs.ensureDirSync(path.join(baseDir, "server", "api"));
130
+ fs.ensureDirSync(path.join(baseDir, "assets"));
131
+ }
132
+ }
133
+ export function createProjectStructure({
134
+ projectName,
135
+ framework,
136
+ architecture,
137
+ features,
138
+ }) {
139
+ const baseDir = path.resolve(process.cwd(), projectName);
140
+ fs.ensureDirSync(baseDir);
141
+
142
+ const useTs = features.includes("ts");
143
+ const hasTailwind = features.includes("tailwind");
144
+ const hasRouter = features.includes("router");
145
+ const hasVuetify =
146
+ framework === "vuetify" || features.includes("vuetifyComponents");
147
+
148
+ fs.writeFileSync(
149
+ path.join(baseDir, ".gitignore"),
150
+ `# Logs
151
+ logs
152
+ *.log
153
+ npm-debug.log*
154
+ yarn-debug.log*
155
+ yarn-error.log*
156
+ pnpm-debug.log*
157
+ lerna-debug.log*
158
+
159
+ # Dependencies
160
+ node_modules
161
+ dist
162
+ dist-ssr
163
+ *.local
164
+
165
+ # Editor directories and files
166
+ .vscode/*
167
+ !.vscode/extensions.json
168
+ .idea
169
+ .DS_Store
170
+ *.suo
171
+ *.ntvs*
172
+ *.njsproj
173
+ *.sln
174
+ *.sw?
175
+
176
+ # Environment variables
177
+ .env
178
+ .env.local
179
+ .env.*.local
180
+
181
+ # Build outputs
182
+ ${framework === "nuxt" ? ".nuxt\n.output\n" : ""}`
183
+ );
184
+ fs.writeFileSync(
185
+ path.join(baseDir, "README.md"),
186
+ `# ${projectName}
187
+
188
+ ${
189
+ framework === "nuxt"
190
+ ? "Nuxt 3"
191
+ : framework === "vuetify"
192
+ ? "Vue 3 + Vuetify"
193
+ : "Vue 3"
194
+ } project generated by VueArt CLI.
195
+
196
+ ## Features
197
+
198
+ ${features.length > 0 ? features.map((f) => `- ${f}`).join("\n") : "- None"}
199
+
200
+ ## Getting Started
201
+
202
+ \`\`\`bash
203
+ # Install dependencies
204
+ npm install
205
+
206
+ # Start development server
207
+ npm run dev
208
+
209
+ # Build for production
210
+ npm run build
211
+ \`\`\`
212
+ `
213
+ );
214
+
215
+ if (framework !== "nuxt") {
216
+ createVueVuetifyStructure(baseDir, framework, architecture, features);
217
+ fs.ensureDirSync(path.join(baseDir, "public"));
218
+ const mainExt = useTs ? "ts" : "js";
219
+ const viteConfigExt = useTs ? "ts" : "js";
220
+ const configExt = useTs ? "ts" : "js";
221
+ fs.writeFileSync(
222
+ path.join(baseDir, "index.html"),
223
+ `<!doctype html>
224
+ <html lang="en">
225
+ <head>
226
+ <meta charset="UTF-8" />
227
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
228
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
229
+ <title>${projectName}</title>
230
+ </head>
231
+ <body>
232
+ <div id="app"></div>
233
+ <script type="module" src="/src/main.${mainExt}"></script>
234
+ </body>
235
+ </html>
236
+ `
237
+ );
238
+ fs.writeFileSync(
239
+ path.join(baseDir, "public/favicon.svg"),
240
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
241
+ <circle cx="50" cy="50" r="40" fill="#42b883"/>
242
+ <text x="50" y="60" font-size="40" text-anchor="middle" fill="white">V</text>
243
+ </svg>`
244
+ );
245
+ const assetsDir = path.join(baseDir, "src/assets");
246
+ if (fs.existsSync(assetsDir)) {
247
+ if (hasVuetify) {
248
+ const e4nTemplatePath = path.join(templatesDir, "assets", "E4N.vue");
249
+ if (fs.existsSync(e4nTemplatePath)) {
250
+ const e4nContent = fs.readFileSync(e4nTemplatePath, "utf-8");
251
+ fs.writeFileSync(path.join(assetsDir, "E4N.vue"), e4nContent);
252
+ }
253
+ fs.writeFileSync(
254
+ path.join(assetsDir, "logo.svg"),
255
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
256
+ <rect width="200" height="200" fill="#1867C0"/>
257
+ <path d="M100 50 L150 150 L50 150 Z" fill="white"/>
258
+ <circle cx="100" cy="120" r="20" fill="#1867C0"/>
259
+ </svg>`
260
+ );
261
+ } else {
262
+ const vueLogoPath = path.join(
263
+ templatesDir,
264
+ "Vue",
265
+ "assets",
266
+ "logo.svg"
267
+ );
268
+ if (fs.existsSync(vueLogoPath)) {
269
+ fs.writeFileSync(
270
+ path.join(assetsDir, "logo.svg"),
271
+ fs.readFileSync(vueLogoPath, "utf-8")
272
+ );
273
+ } else {
274
+ fs.writeFileSync(
275
+ path.join(assetsDir, "vue.svg"),
276
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
277
+ <circle cx="50" cy="50" r="40" fill="#42b883"/>
278
+ <text x="50" y="60" font-size="40" text-anchor="middle" fill="white">V</text>
279
+ </svg>`
280
+ );
281
+ }
282
+ }
283
+ }
284
+ let vitePlugins = ["vue()"];
285
+ if (hasVuetify) {
286
+ vitePlugins.push("vuetify()");
287
+ }
288
+ const viteConfigContent = `import { defineConfig } from 'vite';
289
+ import vue from '@vitejs/plugin-vue';
290
+ ${hasVuetify ? "import vuetify from 'vite-plugin-vuetify';\n" : ""}
291
+ export default defineConfig({
292
+ plugins: [${vitePlugins.join(", ")}],
293
+ });`;
294
+
295
+ fs.writeFileSync(
296
+ path.join(baseDir, `vite.config.${viteConfigExt}`),
297
+ viteConfigContent
298
+ );
299
+ if (useTs) {
300
+ fs.writeFileSync(
301
+ path.join(baseDir, "tsconfig.json"),
302
+ `{
303
+ "compilerOptions": {
304
+ "target": "ES2020",
305
+ "useDefineForClassFields": true,
306
+ "module": "ESNext",
307
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
308
+ "skipLibCheck": true,
309
+ "moduleResolution": "bundler",
310
+ "allowImportingTsExtensions": true,
311
+ "resolveJsonModule": true,
312
+ "isolatedModules": true,
313
+ "noEmit": true,
314
+ "jsx": "preserve",
315
+ "strict": true,
316
+ "noUnusedLocals": true,
317
+ "noUnusedParameters": true,
318
+ "noFallthroughCasesInSwitch": true
319
+ },
320
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
321
+ "references": [{ "path": "./tsconfig.node.json" }]
322
+ }
323
+
324
+ `
325
+ );
326
+
327
+ fs.writeFileSync(
328
+ path.join(baseDir, "tsconfig.node.json"),
329
+ `{
330
+ "compilerOptions": {
331
+ "composite": true,
332
+ "skipLibCheck": true,
333
+ "module": "ESNext",
334
+ "moduleResolution": "bundler",
335
+ "allowSyntheticDefaultImports": true
336
+ },
337
+ "include": ["vite.config.ts"]
338
+ }
339
+ `
340
+ );
341
+ } else {
342
+ fs.writeFileSync(
343
+ path.join(baseDir, "jsconfig.json"),
344
+ `{
345
+ "compilerOptions": {
346
+ "baseUrl": ".",
347
+ "paths": {
348
+ "@/*": ["./src/*"]
349
+ }
350
+ },
351
+ "include": ["src/**/*"]
352
+ }
353
+ `
354
+ );
355
+ }
356
+
357
+ // Router setup (only create router files if router directory exists)
358
+ let routerImport = "";
359
+ let routerUse = "";
360
+ const routerDir = path.join(baseDir, "src/router");
361
+ if (hasRouter && fs.existsSync(routerDir)) {
362
+ const routerExt = useTs ? "ts" : "js";
363
+
364
+ fs.writeFileSync(
365
+ path.join(routerDir, `index.${routerExt}`),
366
+ `import { createRouter, createWebHistory } from 'vue-router';
367
+ import Home from '../views/Home.vue';
368
+
369
+ const routes = [
370
+ { path: '/', name: 'home', component: Home },
371
+ ];
372
+
373
+ export const router = createRouter({
374
+ history: createWebHistory(),
375
+ routes,
376
+ });`
377
+ );
378
+
379
+ const viewsDir = path.join(baseDir, "src/views");
380
+ if (!fs.existsSync(viewsDir)) {
381
+ fs.ensureDirSync(viewsDir);
382
+ }
383
+
384
+ let homeVueContent = "";
385
+ if (hasVuetify) {
386
+ homeVueContent = `<template>
387
+ <v-container class="fill-height">
388
+ <v-row justify="center" align="center">
389
+ <v-col cols="12" class="text-center">
390
+ <h1 class="text-h3 mb-4">Welcome to Vuetify App</h1>
391
+ <p class="text-body-1">This page is generated by VueArt CLI.</p>
392
+ </v-col>
393
+ </v-row>
394
+ </v-container>
395
+ </template>
396
+
397
+ <script${useTs ? ' setup lang="ts"' : " setup"}>
398
+ </script>
399
+ `;
400
+ } else {
401
+ // Read from template file
402
+ const vueTemplatePath = path.join(
403
+ templatesDir,
404
+ "Vue",
405
+ "HelloٌWorld.vue"
406
+ );
407
+ if (fs.existsSync(vueTemplatePath)) {
408
+ homeVueContent = fs.readFileSync(vueTemplatePath, "utf-8");
409
+ // Update import paths to match project structure
410
+ homeVueContent = homeVueContent.replace(
411
+ /from "\.\/components\//g,
412
+ 'from "../components/'
413
+ );
414
+ homeVueContent = homeVueContent.replace(
415
+ /from "\.\/assets\//g,
416
+ 'from "../assets/'
417
+ );
418
+ // Update script tag if needed
419
+ if (useTs && !homeVueContent.includes('lang="ts"')) {
420
+ homeVueContent = homeVueContent.replace(
421
+ /<script setup>/g,
422
+ '<script setup lang="ts">'
423
+ );
424
+ }
425
+ } else {
426
+ // Fallback if template doesn't exist
427
+ homeVueContent = `<template>
428
+ <main class="va-container">
429
+ <h1 class="va-title">Welcome to Vue 3 App</h1>
430
+ <p class="va-subtitle">This page is generated by VueArt CLI.</p>
431
+ </main>
432
+ </template>
433
+
434
+ <script${useTs ? ' setup lang="ts"' : " setup"}>
435
+ </script>
436
+ `;
437
+ }
438
+ }
439
+
440
+ fs.writeFileSync(path.join(viewsDir, "Home.vue"), homeVueContent);
441
+
442
+ routerImport = `import { router } from './router';\n`;
443
+ routerUse = `.use(router)`;
444
+ }
445
+
446
+ // Store setup (only create store files if store directory exists)
447
+ let storeImports = "";
448
+ let storeUse = "";
449
+ const storeDir = path.join(baseDir, "src/store");
450
+ if (features.includes("pinia") && fs.existsSync(storeDir)) {
451
+ fs.writeFileSync(
452
+ path.join(storeDir, "index.ts"),
453
+ `import { createPinia } from 'pinia';
454
+
455
+ export const store = createPinia();`
456
+ );
457
+ storeImports = `import { store } from './store';\n`;
458
+ storeUse = `.use(store)`;
459
+ } else if (features.includes("vuex") && fs.existsSync(storeDir)) {
460
+ const storeExt = useTs ? "ts" : "js";
461
+ fs.writeFileSync(
462
+ path.join(storeDir, `index.${storeExt}`),
463
+ `import { createStore } from 'vuex';
464
+
465
+ export const store = createStore({
466
+ state: () => ({}),
467
+ mutations: {},
468
+ actions: {},
469
+ });`
470
+ );
471
+ storeImports = `import { store } from './store';\n`;
472
+ storeUse = `.use(store)`;
473
+ }
474
+
475
+ // Vuetify plugin setup (only if plugins directory exists)
476
+ const pluginsDir = path.join(baseDir, "src/plugins");
477
+ if (hasVuetify && fs.existsSync(pluginsDir)) {
478
+ const vuetifyExt = useTs ? "ts" : "js";
479
+ fs.writeFileSync(
480
+ path.join(pluginsDir, `vuetify.${vuetifyExt}`),
481
+ `import { createVuetify } from 'vuetify';
482
+ import * as components from 'vuetify/components';
483
+ import * as directives from 'vuetify/directives';
484
+ import { aliases, mdi } from 'vuetify/iconsets/mdi';
485
+ import '@mdi/font/css/materialdesignicons.css';
486
+
487
+ export default createVuetify({
488
+ components,
489
+ directives,
490
+ icons: {
491
+ defaultSet: 'mdi',
492
+ aliases,
493
+ sets: {
494
+ mdi,
495
+ },
496
+ },
497
+ });`
498
+ );
499
+ }
500
+
501
+ // CSS setup
502
+ let cssImports = "";
503
+ if (hasVuetify) {
504
+ // Vuetify CSS imports
505
+ cssImports = `import 'vuetify/styles';\n`;
506
+ } else if (hasTailwind) {
507
+ fs.writeFileSync(
508
+ path.join(baseDir, "tailwind.config.cjs"),
509
+ `/** @type {import('tailwindcss').Config} */
510
+ module.exports = {
511
+ content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
512
+ theme: {
513
+ extend: {},
514
+ },
515
+ plugins: [],
516
+ };`
517
+ );
518
+ fs.writeFileSync(
519
+ path.join(baseDir, "postcss.config.cjs"),
520
+ `module.exports = {
521
+ plugins: {
522
+ tailwindcss: {},
523
+ autoprefixer: {},
524
+ },
525
+ };`
526
+ );
527
+ // Assets directory should already exist from structure creation, but ensure it
528
+ if (!fs.existsSync(assetsDir)) {
529
+ fs.ensureDirSync(assetsDir);
530
+ }
531
+ fs.writeFileSync(
532
+ path.join(assetsDir, "main.css"),
533
+ `@tailwind base;
534
+ @tailwind components;
535
+ @tailwind utilities;
536
+
537
+ .va-container {
538
+ min-height: 100vh;
539
+ display: flex;
540
+ flex-direction: column;
541
+ align-items: center;
542
+ justify-content: center;
543
+ padding: 2rem;
544
+ }
545
+
546
+ .va-title {
547
+ font-size: 2rem;
548
+ font-weight: 700;
549
+ }
550
+
551
+ .va-subtitle {
552
+ margin-top: 0.5rem;
553
+ color: #64748b;
554
+ }`
555
+ );
556
+ cssImports = `import './assets/main.css';\n`;
557
+ } else {
558
+ // Copy CSS from Vue template if available, otherwise use basic styles
559
+ const vueAssetsDir = path.join(templatesDir, "Vue", "assets");
560
+ if (fs.existsSync(vueAssetsDir)) {
561
+ // Copy base.css
562
+ const baseCssPath = path.join(vueAssetsDir, "base.css");
563
+ if (fs.existsSync(baseCssPath)) {
564
+ fs.writeFileSync(
565
+ path.join(assetsDir, "base.css"),
566
+ fs.readFileSync(baseCssPath, "utf-8")
567
+ );
568
+ }
569
+
570
+ // Copy main.css
571
+ const mainCssPath = path.join(vueAssetsDir, "main.css");
572
+ if (fs.existsSync(mainCssPath)) {
573
+ fs.writeFileSync(
574
+ path.join(assetsDir, "main.css"),
575
+ fs.readFileSync(mainCssPath, "utf-8")
576
+ );
577
+ cssImports = `import './assets/main.css';\n`;
578
+ } else {
579
+ // Fallback to basic style.css
580
+ fs.writeFileSync(
581
+ path.join(baseDir, "src/style.css"),
582
+ `* {
583
+ margin: 0;
584
+ padding: 0;
585
+ box-sizing: border-box;
586
+ }
587
+
588
+ body {
589
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
590
+ }
591
+
592
+ .va-container {
593
+ min-height: 100vh;
594
+ display: flex;
595
+ flex-direction: column;
596
+ align-items: center;
597
+ justify-content: center;
598
+ padding: 2rem;
599
+ }
600
+
601
+ .va-title {
602
+ font-size: 2rem;
603
+ font-weight: 700;
604
+ }
605
+
606
+ .va-subtitle {
607
+ margin-top: 0.5rem;
608
+ color: #64748b;
609
+ }`
610
+ );
611
+ cssImports = `import './style.css';\n`;
612
+ }
613
+ } else {
614
+ // Basic style.css if no Tailwind and no template
615
+ fs.writeFileSync(
616
+ path.join(baseDir, "src/style.css"),
617
+ `* {
618
+ margin: 0;
619
+ padding: 0;
620
+ box-sizing: border-box;
621
+ }
622
+
623
+ body {
624
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
625
+ }
626
+
627
+ .va-container {
628
+ min-height: 100vh;
629
+ display: flex;
630
+ flex-direction: column;
631
+ align-items: center;
632
+ justify-content: center;
633
+ padding: 2rem;
634
+ }
635
+
636
+ .va-title {
637
+ font-size: 2rem;
638
+ font-weight: 700;
639
+ }
640
+
641
+ .va-subtitle {
642
+ margin-top: 0.5rem;
643
+ color: #64748b;
644
+ }`
645
+ );
646
+ cssImports = `import './style.css';\n`;
647
+ }
648
+ }
649
+
650
+ // main file
651
+ let vuetifyImport = "";
652
+ let vuetifyUse = "";
653
+ if (hasVuetify) {
654
+ vuetifyImport = `import vuetify from './plugins/vuetify';\n`;
655
+ vuetifyUse = `.use(vuetify)`;
656
+ }
657
+
658
+ const mainContent = `import { createApp } from 'vue';
659
+ import App from './App.vue';
660
+ ${cssImports}${routerImport}${storeImports}${vuetifyImport}const app = createApp(App)${routerUse}${storeUse}${
661
+ vuetifyUse ? ` ${vuetifyUse}` : ""
662
+ };
663
+
664
+ app.mount('#app');`;
665
+
666
+ fs.writeFileSync(path.join(baseDir, `src/main.${mainExt}`), mainContent);
667
+
668
+ // App.vue
669
+ const appScriptTag = useTs ? '<script setup lang="ts">' : "<script setup>";
670
+
671
+ let appVueContent = "";
672
+ if (hasVuetify) {
673
+ appVueContent = `<template>
674
+ <v-app>
675
+ <v-main>
676
+ <HelloWorld />
677
+ </v-main>
678
+ </v-app>
679
+ </template>
680
+
681
+ ${appScriptTag}
682
+ import HelloWorld from './components/HelloWorld.vue';
683
+ </script>
684
+ `;
685
+ } else {
686
+ // Read from template file
687
+ const vueTemplatePath = path.join(templatesDir, "Vue", "HelloٌWorld.vue");
688
+ if (fs.existsSync(vueTemplatePath)) {
689
+ appVueContent = fs.readFileSync(vueTemplatePath, "utf-8");
690
+ // Update import paths to match project structure
691
+ appVueContent = appVueContent.replace(
692
+ /from "\.\/components\//g,
693
+ 'from "./components/'
694
+ );
695
+ appVueContent = appVueContent.replace(
696
+ /from "\.\/assets\//g,
697
+ 'from "./assets/'
698
+ );
699
+ // Update script tag if needed
700
+ if (useTs && !appVueContent.includes('lang="ts"')) {
701
+ appVueContent = appVueContent.replace(
702
+ /<script setup>/g,
703
+ '<script setup lang="ts">'
704
+ );
705
+ }
706
+ } else {
707
+ // Fallback if template doesn't exist
708
+ appVueContent = `<template>
709
+ <main class="va-container">
710
+ <h1 class="va-title">Welcome to Vue 3 App</h1>
711
+ <p class="va-subtitle">This project is generated by VueArt CLI.</p>
712
+ </main>
713
+ </template>
714
+
715
+ ${appScriptTag}
716
+ </script>
717
+ `;
718
+ }
719
+ }
720
+
721
+ fs.writeFileSync(path.join(baseDir, "src/App.vue"), appVueContent);
722
+
723
+ // Sample component (only if components directory exists)
724
+ const componentsDir = path.join(baseDir, "src/components");
725
+ if (fs.existsSync(componentsDir)) {
726
+ let helloWorldContent = "";
727
+
728
+ if (hasVuetify) {
729
+ // Load Vuetify template
730
+ const vuetifyTemplatePath = path.join(
731
+ templatesDir,
732
+ "Vuetify",
733
+ "HelloWorld.vue"
734
+ );
735
+ if (fs.existsSync(vuetifyTemplatePath)) {
736
+ helloWorldContent = fs.readFileSync(vuetifyTemplatePath, "utf-8");
737
+ helloWorldContent = helloWorldContent.replace(
738
+ /logo\.png/g,
739
+ "logo.svg"
740
+ );
741
+ } else {
742
+ helloWorldContent = `<template>
743
+ <v-container>
744
+ <h1>Welcome to Vuetify</h1>
745
+ </v-container>
746
+ </template>
747
+
748
+ ${appScriptTag}
749
+ </script>
750
+ `;
751
+ }
752
+ } else {
753
+ const vueTemplatePath = path.join(
754
+ templatesDir,
755
+ "Vue",
756
+ "HelloٌWorld.vue"
757
+ );
758
+ if (fs.existsSync(vueTemplatePath)) {
759
+ // Copy E4N component
760
+ const e4nTemplatePath = path.join(
761
+ templatesDir,
762
+ "Vue",
763
+ "components",
764
+ "E4N.vue"
765
+ );
766
+ if (fs.existsSync(e4nTemplatePath)) {
767
+ const e4nContent = fs.readFileSync(e4nTemplatePath, "utf-8");
768
+ fs.writeFileSync(path.join(componentsDir, "E4N.vue"), e4nContent);
769
+ }
770
+ const theWelcomeTemplatePath = path.join(
771
+ templatesDir,
772
+ "Vue",
773
+ "components",
774
+ "TheWelcome.vue"
775
+ );
776
+ if (fs.existsSync(theWelcomeTemplatePath)) {
777
+ let theWelcomeContent = fs.readFileSync(
778
+ theWelcomeTemplatePath,
779
+ "utf-8"
780
+ );
781
+ theWelcomeContent = theWelcomeContent.replace(
782
+ /from '\.\/WelcomeItem\.vue'/g,
783
+ "from './WelcomeItem.vue'"
784
+ );
785
+ theWelcomeContent = theWelcomeContent.replace(
786
+ /from '\.\/icons\//g,
787
+ "from './icons/"
788
+ );
789
+ if (useTs && !theWelcomeContent.includes('lang="ts"')) {
790
+ theWelcomeContent = theWelcomeContent.replace(
791
+ /<script setup>/g,
792
+ '<script setup lang="ts">'
793
+ );
794
+ }
795
+ fs.writeFileSync(
796
+ path.join(componentsDir, "TheWelcome.vue"),
797
+ theWelcomeContent
798
+ );
799
+ }
800
+ const welcomeItemTemplatePath = path.join(
801
+ templatesDir,
802
+ "Vue",
803
+ "components",
804
+ "WelcomeItem.vue"
805
+ );
806
+ if (fs.existsSync(welcomeItemTemplatePath)) {
807
+ fs.writeFileSync(
808
+ path.join(componentsDir, "WelcomeItem.vue"),
809
+ fs.readFileSync(welcomeItemTemplatePath, "utf-8")
810
+ );
811
+ }
812
+ const iconsTemplateDir = path.join(
813
+ templatesDir,
814
+ "Vue",
815
+ "components",
816
+ "icons"
817
+ );
818
+ const iconsTargetDir = path.join(componentsDir, "icons");
819
+ if (fs.existsSync(iconsTemplateDir)) {
820
+ fs.ensureDirSync(iconsTargetDir);
821
+ const iconFiles = fs.readdirSync(iconsTemplateDir);
822
+ iconFiles.forEach((iconFile) => {
823
+ const iconTemplatePath = path.join(iconsTemplateDir, iconFile);
824
+ const iconTargetPath = path.join(iconsTargetDir, iconFile);
825
+ fs.writeFileSync(
826
+ iconTargetPath,
827
+ fs.readFileSync(iconTemplatePath, "utf-8")
828
+ );
829
+ });
830
+ }
831
+ helloWorldContent = `<template>
832
+ <div class="hello">
833
+ <h2>{{ msg }}</h2>
834
+ </div>
835
+ </template>
836
+
837
+ ${appScriptTag}
838
+ ${useTs ? "defineProps<{ msg: string }>();" : "defineProps({ msg: String });"}
839
+ </script>
840
+
841
+ <style scoped>
842
+ .hello {
843
+ text-align: center;
844
+ }
845
+ </style>
846
+ `;
847
+ } else {
848
+ helloWorldContent = `<template>
849
+ <div class="hello">
850
+ <h2>{{ msg }}</h2>
851
+ </div>
852
+ </template>
853
+
854
+ ${appScriptTag}
855
+ ${useTs ? "const msg: string = 'Hello World!';" : "const msg = 'Hello World!';"}
856
+ </script>
857
+
858
+ <style scoped>
859
+ .hello {
860
+ text-align: center;
861
+ }
862
+ </style>
863
+ `;
864
+ }
865
+ }
866
+
867
+ fs.writeFileSync(
868
+ path.join(componentsDir, "HelloWorld.vue"),
869
+ helloWorldContent
870
+ );
871
+ }
872
+ if (architecture === "feature") {
873
+ const exampleFeatureDir = path.join(baseDir, "src/features/example");
874
+ if (fs.existsSync(exampleFeatureDir)) {
875
+ const composableExt = useTs ? "ts" : "js";
876
+ fs.writeFileSync(
877
+ path.join(exampleFeatureDir, `useExample.${composableExt}`),
878
+ `import { ref } from 'vue';
879
+
880
+ export function useExample() {
881
+ const count = ref(0);
882
+ const increment = () => count.value++;
883
+ return { count, increment };
884
+ }`
885
+ );
886
+
887
+ fs.writeFileSync(
888
+ path.join(exampleFeatureDir, "ExampleView.vue"),
889
+ `<template>
890
+ <section class="va-container">
891
+ <h2 class="va-title">Feature: Example</h2>
892
+ <button @click="increment">Count: {{ count }}</button>
893
+ </section>
894
+ </template>
895
+
896
+ ${appScriptTag}
897
+ import { useExample } from './useExample${useTs ? "" : ".js"}';
898
+
899
+ const { count, increment } = useExample();
900
+ </script>
901
+ `
902
+ );
903
+ }
904
+ }
905
+ const atomsDir = path.join(baseDir, "src/components/atoms");
906
+ const moleculesDir = path.join(baseDir, "src/components/molecules");
907
+ const organismsDir = path.join(baseDir, "src/components/organisms");
908
+ if (
909
+ fs.existsSync(atomsDir) &&
910
+ fs.existsSync(moleculesDir) &&
911
+ fs.existsSync(organismsDir)
912
+ ) {
913
+ fs.writeFileSync(
914
+ path.join(atomsDir, "VaButton.vue"),
915
+ `<template>
916
+ <button class="va-button">
917
+ <slot />
918
+ </button>
919
+ </template>
920
+
921
+ ${appScriptTag}
922
+ </script>
923
+ `
924
+ );
925
+
926
+ fs.writeFileSync(
927
+ path.join(moleculesDir, "VaCard.vue"),
928
+ `<template>
929
+ <section class="va-card">
930
+ <header class="va-card__header">
931
+ <slot name="title" />
932
+ </header>
933
+ <div class="va-card__body">
934
+ <slot />
935
+ </div>
936
+ </section>
937
+ </template>
938
+
939
+ ${appScriptTag}
940
+ </script>
941
+ `
942
+ );
943
+
944
+ fs.writeFileSync(
945
+ path.join(organismsDir, "VaLayout.vue"),
946
+ `<template>
947
+ <div class="va-layout">
948
+ <header class="va-layout__header">
949
+ <slot name="header" />
950
+ </header>
951
+ <main class="va-layout__content">
952
+ <slot />
953
+ </main>
954
+ </div>
955
+ </template>
956
+
957
+ ${appScriptTag}
958
+ </script>
959
+ `
960
+ );
961
+ }
962
+
963
+ // Enterprise structure example files
964
+ if (architecture === "enterprise") {
965
+ const sharedDir = path.join(baseDir, "src/shared");
966
+ if (fs.existsSync(sharedDir)) {
967
+ if (!fs.existsSync(path.join(sharedDir, "components"))) {
968
+ fs.ensureDirSync(path.join(sharedDir, "components"));
969
+ }
970
+ if (!fs.existsSync(path.join(sharedDir, "utils"))) {
971
+ fs.ensureDirSync(path.join(sharedDir, "utils"));
972
+ }
973
+ }
974
+ }
975
+ }
976
+ if (framework === "nuxt") {
977
+ createNuxtStructure(baseDir, architecture, features);
978
+ fs.ensureDirSync(path.join(baseDir, "layouts"));
979
+ fs.ensureDirSync(path.join(baseDir, "public"));
980
+ if (architecture === "feature") {
981
+ const exampleFeatureDir = path.join(baseDir, "features/example");
982
+ if (fs.existsSync(exampleFeatureDir)) {
983
+ const composableExt = useTs ? "ts" : "js";
984
+ fs.writeFileSync(
985
+ path.join(exampleFeatureDir, `useExample.${composableExt}`),
986
+ `export const useExample = () => {
987
+ const count = useState('example', () => 0);
988
+ const increment = () => count.value++;
989
+ return { count, increment };
990
+ };`
991
+ );
992
+
993
+ fs.writeFileSync(
994
+ path.join(exampleFeatureDir, "ExamplePage.vue"),
995
+ `<template>
996
+ <main class="va-container">
997
+ <h2 class="va-title">Feature: Example</h2>
998
+ <button @click="increment">Count: {{ count }}</button>
999
+ </main>
1000
+ </template>
1001
+
1002
+ <script${useTs ? ' setup lang="ts"' : " setup"}>
1003
+ import { useExample } from './useExample${useTs ? "" : ".js"}';
1004
+
1005
+ const { count, increment } = useExample();
1006
+ </script>
1007
+ `
1008
+ );
1009
+ }
1010
+ }
1011
+
1012
+ if (architecture === "layered" || architecture === "enterprise") {
1013
+ const layersDir = path.join(baseDir, "layers");
1014
+ if (fs.existsSync(layersDir)) {
1015
+ const domainDir = path.join(layersDir, "domain");
1016
+ const applicationDir = path.join(layersDir, "application");
1017
+ const presentationDir = path.join(layersDir, "presentation");
1018
+
1019
+ if (!fs.existsSync(path.join(domainDir, "entities"))) {
1020
+ fs.ensureDirSync(path.join(domainDir, "entities"));
1021
+ }
1022
+ if (!fs.existsSync(path.join(domainDir, "repositories"))) {
1023
+ fs.ensureDirSync(path.join(domainDir, "repositories"));
1024
+ }
1025
+ if (!fs.existsSync(path.join(applicationDir, "services"))) {
1026
+ fs.ensureDirSync(path.join(applicationDir, "services"));
1027
+ }
1028
+ if (!fs.existsSync(path.join(applicationDir, "use-cases"))) {
1029
+ fs.ensureDirSync(path.join(applicationDir, "use-cases"));
1030
+ }
1031
+ if (!fs.existsSync(path.join(presentationDir, "components"))) {
1032
+ fs.ensureDirSync(path.join(presentationDir, "components"));
1033
+ }
1034
+ if (!fs.existsSync(path.join(presentationDir, "composables"))) {
1035
+ fs.ensureDirSync(path.join(presentationDir, "composables"));
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ if (architecture === "enterprise") {
1041
+ const sharedDir = path.join(baseDir, "shared");
1042
+ if (fs.existsSync(sharedDir)) {
1043
+ if (!fs.existsSync(path.join(sharedDir, "components"))) {
1044
+ fs.ensureDirSync(path.join(sharedDir, "components"));
1045
+ }
1046
+ if (!fs.existsSync(path.join(sharedDir, "composables"))) {
1047
+ fs.ensureDirSync(path.join(sharedDir, "composables"));
1048
+ }
1049
+ if (!fs.existsSync(path.join(sharedDir, "utils"))) {
1050
+ fs.ensureDirSync(path.join(sharedDir, "utils"));
1051
+ }
1052
+
1053
+ if (features.includes("pinia")) {
1054
+ const storeDir = path.join(sharedDir, "store");
1055
+ if (!fs.existsSync(storeDir)) {
1056
+ fs.ensureDirSync(storeDir);
1057
+ }
1058
+ const storeExt = useTs ? "ts" : "js";
1059
+ fs.writeFileSync(
1060
+ path.join(storeDir, `index.${storeExt}`),
1061
+ `import { defineStore } from 'pinia';
1062
+
1063
+ export const useAppStore = defineStore('app', {
1064
+ state: () => ({
1065
+ // Add your state here
1066
+ }),
1067
+ getters: {
1068
+ // Add your getters here
1069
+ },
1070
+ actions: {
1071
+ // Add your actions here
1072
+ },
1073
+ });`
1074
+ );
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ const nuxtModules = [];
1080
+ if (hasTailwind) nuxtModules.push("'@nuxt/tailwindcss'");
1081
+ if (features.includes("pwa")) nuxtModules.push("'@nuxt/pwa'");
1082
+ if (features.includes("axios")) nuxtModules.push("'@nuxt/axios'");
1083
+ if (features.includes("i18n")) nuxtModules.push("'@nuxt/i18n'");
1084
+ if (features.includes("auth")) nuxtModules.push("'@nuxt/auth-next'");
1085
+ if (features.includes("pinia")) nuxtModules.push("'@pinia/nuxt'");
1086
+
1087
+ fs.writeFileSync(
1088
+ path.join(baseDir, "nuxt.config.ts"),
1089
+ `// https://nuxt.com/docs/api/configuration/nuxt-config
1090
+ export default defineNuxtConfig({
1091
+ modules: [${nuxtModules.length > 0 ? nuxtModules.join(", ") : ""}],
1092
+ })
1093
+ `
1094
+ );
1095
+
1096
+ fs.writeFileSync(
1097
+ path.join(baseDir, "app.vue"),
1098
+ `<template>
1099
+ <NuxtLayout>
1100
+ <NuxtPage />
1101
+ </NuxtLayout>
1102
+ </template>
1103
+ `
1104
+ );
1105
+
1106
+ fs.writeFileSync(
1107
+ path.join(baseDir, "layouts/default.vue"),
1108
+ `<template>
1109
+ <div>
1110
+ <header>
1111
+ <nav>
1112
+ <NuxtLink to="/">Home</NuxtLink>
1113
+ </nav>
1114
+ </header>
1115
+ <main>
1116
+ <slot />
1117
+ </main>
1118
+ </div>
1119
+ </template>
1120
+
1121
+ <script${useTs ? ' setup lang="ts"' : " setup"}>
1122
+ </script>
1123
+ `
1124
+ );
1125
+
1126
+ fs.writeFileSync(
1127
+ path.join(baseDir, "pages/index.vue"),
1128
+ `<template>
1129
+ <main class="va-container">
1130
+ <h1 class="va-title">Welcome to Nuxt App</h1>
1131
+ <p class="va-subtitle">This page is generated by VueArt CLI.</p>
1132
+ </main>
1133
+ </template>
1134
+
1135
+ <script${useTs ? ' setup lang="ts"' : " setup"}>
1136
+ </script>
1137
+ `
1138
+ );
1139
+
1140
+ fs.writeFileSync(path.join(baseDir, "public/favicon.ico"), "");
1141
+ if (useTs) {
1142
+ fs.writeFileSync(
1143
+ path.join(baseDir, "tsconfig.json"),
1144
+ `{
1145
+ "extends": "./.nuxt/tsconfig.json"
1146
+ }
1147
+ `
1148
+ );
1149
+ }
1150
+ if (hasTailwind) {
1151
+ const nuxtAssetsDir = path.join(baseDir, "assets");
1152
+ if (fs.existsSync(nuxtAssetsDir)) {
1153
+ const cssDir = path.join(nuxtAssetsDir, "css");
1154
+ if (!fs.existsSync(cssDir)) {
1155
+ fs.ensureDirSync(cssDir);
1156
+ }
1157
+ fs.writeFileSync(
1158
+ path.join(cssDir, "main.css"),
1159
+ `@tailwind base;
1160
+ @tailwind components;
1161
+ @tailwind utilities;
1162
+
1163
+ .va-container {
1164
+ min-height: 100vh;
1165
+ display: flex;
1166
+ flex-direction: column;
1167
+ align-items: center;
1168
+ justify-content: center;
1169
+ padding: 2rem;
1170
+ }
1171
+
1172
+ .va-title {
1173
+ font-size: 2rem;
1174
+ font-weight: 700;
1175
+ }
1176
+
1177
+ .va-subtitle {
1178
+ margin-top: 0.5rem;
1179
+ color: #64748b;
1180
+ }`
1181
+ );
1182
+ }
1183
+ }
1184
+ }
1185
+ }