pni 1.0.0 → 1.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.
- package/dist/app.js +1 -1
- package/dist/utils/add-three.js +18 -3
- package/dist/utils/config-generator.js +89 -266
- package/dist/utils/css-variables.d.ts +1 -1
- package/dist/utils/css-variables.js +6 -2
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -82,7 +82,7 @@ export default function App({ nuxt = false, vue = false, threejs = false, cssVar
|
|
|
82
82
|
await generateCSSVariables(finalProjectType, workingPath, false);
|
|
83
83
|
// Create typography page after CSS variables are set up
|
|
84
84
|
if (selectedFeatures.cssVars) {
|
|
85
|
-
await createTypographyPage(workingPath);
|
|
85
|
+
await createTypographyPage(workingPath, finalProjectType);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
else {
|
package/dist/utils/add-three.js
CHANGED
|
@@ -98,9 +98,24 @@ export async function addThree(currentDir) {
|
|
|
98
98
|
mkdirSync(composablesTargetDir, { recursive: true });
|
|
99
99
|
}
|
|
100
100
|
// Calculate import path from composables/{directoryName} to three folder
|
|
101
|
-
//
|
|
102
|
-
const
|
|
103
|
-
|
|
101
|
+
// The import path should be @/pages/{directoryName}/three/World.js when run in a pages directory
|
|
102
|
+
const currentPathRelative = relative(projectRoot, currentPath).replace(/\\/g, '/');
|
|
103
|
+
// Check if the current path is in a pages directory
|
|
104
|
+
let importPath;
|
|
105
|
+
if (currentPathRelative.startsWith('pages/')) {
|
|
106
|
+
// If we're in a pages directory (Vue), use @/pages/{directoryName}/three/World.js
|
|
107
|
+
importPath = `@/pages/${directoryName}/three/World.js`;
|
|
108
|
+
}
|
|
109
|
+
else if (currentPathRelative.startsWith('app/pages/')) {
|
|
110
|
+
// If we're in app/pages directory (Nuxt), use @/pages/{directoryName}/three/World.js
|
|
111
|
+
// Note: @/ in Nuxt resolves to project root, so app/pages becomes pages
|
|
112
|
+
importPath = `@/pages/${directoryName}/three/World.js`;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// Otherwise, use the relative path from project root
|
|
116
|
+
const threePathRelative = relative(projectRoot, targetDir).replace(/\\/g, '/');
|
|
117
|
+
importPath = `@/${threePathRelative}/World.js`;
|
|
118
|
+
}
|
|
104
119
|
// Determine file extension based on project type
|
|
105
120
|
const fileExtension = projectType === 'vue' ? 'js' : 'ts';
|
|
106
121
|
// Create usethree.{js|ts}
|
|
@@ -4,24 +4,20 @@ export async function generateNuxtConfig(projectPath,
|
|
|
4
4
|
//@ts-ignore
|
|
5
5
|
threejs, cssVars) {
|
|
6
6
|
const configPath = join(projectPath, 'nuxt.config.ts');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const tailwindImport = cssVars
|
|
22
|
-
? "import tailwindcss from '@tailwindcss/vite'\n\n"
|
|
23
|
-
: '';
|
|
24
|
-
configContent = `${tailwindImport}// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
7
|
+
// Always replace the config with the full template, regardless of whether it exists
|
|
8
|
+
// Generate full config template
|
|
9
|
+
const modules = [
|
|
10
|
+
'shadcn-nuxt',
|
|
11
|
+
'@nuxtjs/seo',
|
|
12
|
+
'@nuxt/image',
|
|
13
|
+
'@nuxtjs/device',
|
|
14
|
+
];
|
|
15
|
+
// Use assets/css/tailwind.css for Nuxt projects
|
|
16
|
+
const cssImport = '~/assets/css/tailwind.css';
|
|
17
|
+
const tailwindImport = cssVars
|
|
18
|
+
? "import tailwindcss from '@tailwindcss/vite'\n\n"
|
|
19
|
+
: '';
|
|
20
|
+
const configContent = `${tailwindImport}// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
25
21
|
export default defineNuxtConfig({
|
|
26
22
|
// 1. Updated to a realistic 2024/2025 date for Nuxt 4 features
|
|
27
23
|
compatibilityDate: '2024-11-01',
|
|
@@ -59,7 +55,7 @@ ${cssVars ? ` css: ['${cssImport}'],\n\n` : ''} ssr: true,
|
|
|
59
55
|
},
|
|
60
56
|
|
|
61
57
|
${cssVars
|
|
62
|
-
|
|
58
|
+
? ` vite: {
|
|
63
59
|
plugins: [tailwindcss()],
|
|
64
60
|
esbuild: {
|
|
65
61
|
drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [],
|
|
@@ -71,7 +67,7 @@ ${cssVars
|
|
|
71
67
|
},
|
|
72
68
|
|
|
73
69
|
`
|
|
74
|
-
|
|
70
|
+
: ''} nitro: {
|
|
75
71
|
compressPublicAssets: {
|
|
76
72
|
brotli: true,
|
|
77
73
|
gzip: true,
|
|
@@ -104,14 +100,14 @@ ${cssVars
|
|
|
104
100
|
|
|
105
101
|
// UI Framework: Shadcn
|
|
106
102
|
${cssVars
|
|
107
|
-
|
|
103
|
+
? ` shadcn: {
|
|
108
104
|
prefix: '',
|
|
109
105
|
componentDir: '@/components/ui',
|
|
110
106
|
},
|
|
111
107
|
|
|
112
108
|
|
|
113
109
|
`
|
|
114
|
-
|
|
110
|
+
: ''} ogImage: {
|
|
115
111
|
defaults: {
|
|
116
112
|
component: 'OgImageTemplate',
|
|
117
113
|
props: {
|
|
@@ -132,180 +128,7 @@ ${cssVars
|
|
|
132
128
|
},
|
|
133
129
|
})
|
|
134
130
|
`;
|
|
135
|
-
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
// If config exists, merge new modules and settings
|
|
139
|
-
const modules = [];
|
|
140
|
-
if (cssVars && !configContent.includes('shadcn-nuxt')) {
|
|
141
|
-
modules.push('shadcn-nuxt');
|
|
142
|
-
}
|
|
143
|
-
if (!configContent.includes('@nuxtjs/seo')) {
|
|
144
|
-
modules.push('@nuxtjs/seo');
|
|
145
|
-
}
|
|
146
|
-
if (!configContent.includes('@nuxt/image')) {
|
|
147
|
-
modules.push('@nuxt/image');
|
|
148
|
-
}
|
|
149
|
-
if (!configContent.includes('@nuxtjs/device')) {
|
|
150
|
-
modules.push('@nuxtjs/device');
|
|
151
|
-
}
|
|
152
|
-
// Add compatibilityDate if not present
|
|
153
|
-
if (!configContent.includes('compatibilityDate')) {
|
|
154
|
-
configContent = configContent.replace(/export default defineNuxtConfig\(\{/, `export default defineNuxtConfig({
|
|
155
|
-
// 1. Updated to a realistic 2024/2025 date for Nuxt 4 features
|
|
156
|
-
compatibilityDate: '2024-11-01',`);
|
|
157
|
-
}
|
|
158
|
-
// Add ssr if not present
|
|
159
|
-
if (!configContent.includes('ssr:')) {
|
|
160
|
-
configContent = configContent.replace(/compatibilityDate:\s*'[^']*',/, `$&\n ssr: true,`);
|
|
161
|
-
}
|
|
162
|
-
// Add CSS import if cssVars is true
|
|
163
|
-
if (cssVars) {
|
|
164
|
-
// Use assets/css/tailwind.css for Nuxt projects
|
|
165
|
-
const cssImport = '~/assets/css/tailwind.css';
|
|
166
|
-
if (!configContent.includes('import tailwindcss')) {
|
|
167
|
-
configContent = `import tailwindcss from '@tailwindcss/vite'\n\n${configContent}`;
|
|
168
|
-
}
|
|
169
|
-
if (!configContent.includes('css:')) {
|
|
170
|
-
configContent = configContent.replace(/devtools:\s*\{[^}]*\},/, `$&\n\n css: ['${cssImport}'],`);
|
|
171
|
-
}
|
|
172
|
-
else if (!configContent.includes(cssImport)) {
|
|
173
|
-
configContent = configContent.replace(/css:\s*\[/, `css: ['${cssImport}',`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
// Add vite plugins for Tailwind if cssVars is true
|
|
177
|
-
if (cssVars && !configContent.includes('@tailwindcss/vite')) {
|
|
178
|
-
if (!configContent.includes('vite:')) {
|
|
179
|
-
configContent = configContent.replace(/ssr:\s*true,/, `$&\n\n vite: {
|
|
180
|
-
plugins: [tailwindcss()],
|
|
181
|
-
esbuild: {
|
|
182
|
-
drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [],
|
|
183
|
-
},
|
|
184
|
-
build: {
|
|
185
|
-
// Ensures CSS is also minified correctly by lightningcss (default in Vite 6)
|
|
186
|
-
cssMinify: 'lightningcss'
|
|
187
|
-
}
|
|
188
|
-
},`);
|
|
189
|
-
}
|
|
190
|
-
else if (!configContent.includes('tailwindcss()')) {
|
|
191
|
-
configContent = configContent.replace(/vite:\s*\{/, `vite: {
|
|
192
|
-
plugins: [tailwindcss()],`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
// Add modules
|
|
196
|
-
if (modules.length > 0) {
|
|
197
|
-
if (configContent.includes('modules:')) {
|
|
198
|
-
// Add to existing modules array
|
|
199
|
-
const existingModules = configContent.match(/modules:\s*\[([^\]]*)\]/);
|
|
200
|
-
if (existingModules && existingModules[1]) {
|
|
201
|
-
const existingModuleList = existingModules[1].trim();
|
|
202
|
-
const allModules = [...modules];
|
|
203
|
-
if (existingModuleList) {
|
|
204
|
-
const existing = existingModuleList
|
|
205
|
-
.split(',')
|
|
206
|
-
.map(m => m.trim().replace(/['"]/g, ''));
|
|
207
|
-
allModules.push(...existing);
|
|
208
|
-
}
|
|
209
|
-
const uniqueModules = [...new Set(allModules)];
|
|
210
|
-
configContent = configContent.replace(/modules:\s*\[[^\]]*\]/, `modules: [${uniqueModules.map(m => `'${m}'`).join(', ')}]`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
// Add new modules array
|
|
215
|
-
configContent = configContent.replace(/ssr:\s*true,/, `$&\n\n modules: [${modules.map(m => `'${m}'`).join(', ')}],`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Add app.head config if not present
|
|
219
|
-
if (!configContent.includes('app:')) {
|
|
220
|
-
configContent = configContent.replace(/site:\s*\{[^}]*\},/, `$&\n\n // App Config for UI-wide settings
|
|
221
|
-
app: {
|
|
222
|
-
head: {
|
|
223
|
-
charset: 'utf-8',
|
|
224
|
-
viewport: 'width=device-width, initial-scale=1',
|
|
225
|
-
meta: [
|
|
226
|
-
{ name: 'description', content: 'A new setup for your project' },
|
|
227
|
-
{ name: 'author', content: 'New Setup' },
|
|
228
|
-
{ property: 'og:type', content: 'website' },
|
|
229
|
-
{ name: 'msapplication-TileColor', content: '#000000' },
|
|
230
|
-
{ name: 'theme-color', content: '#000000' },
|
|
231
|
-
],
|
|
232
|
-
link: [
|
|
233
|
-
// { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
|
234
|
-
// { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon-180x180.png' },
|
|
235
|
-
// { rel: 'manifest', href: '/manifest.webmanifest' },
|
|
236
|
-
],
|
|
237
|
-
},
|
|
238
|
-
},`);
|
|
239
|
-
}
|
|
240
|
-
// Add nitro config if not present
|
|
241
|
-
if (!configContent.includes('nitro:')) {
|
|
242
|
-
configContent = configContent.replace(/vite:\s*\{[^}]*\},/, `$&\n\n nitro: {
|
|
243
|
-
compressPublicAssets: {
|
|
244
|
-
brotli: true,
|
|
245
|
-
gzip: true,
|
|
246
|
-
},
|
|
247
|
-
// Enable crawling for SEO module compatibility
|
|
248
|
-
prerender: {
|
|
249
|
-
// set to true for production
|
|
250
|
-
crawlLinks: false,
|
|
251
|
-
routes: ['/']
|
|
252
|
-
}
|
|
253
|
-
},`);
|
|
254
|
-
}
|
|
255
|
-
// Add image config if not present
|
|
256
|
-
if (!configContent.includes('image:')) {
|
|
257
|
-
configContent = configContent.replace(/modules:\s*\[[^\]]*\],/, `$&\n\n // Image Optimization
|
|
258
|
-
image: {
|
|
259
|
-
quality: 95,
|
|
260
|
-
format: ['webp'],
|
|
261
|
-
screens: {
|
|
262
|
-
xs: 320,
|
|
263
|
-
sm: 640,
|
|
264
|
-
md: 768,
|
|
265
|
-
lg: 1200,
|
|
266
|
-
xl: 1400,
|
|
267
|
-
xxl: 1800,
|
|
268
|
-
'2xl': 2000,
|
|
269
|
-
},
|
|
270
|
-
},`);
|
|
271
|
-
}
|
|
272
|
-
// Add shadcn config if cssVars is true
|
|
273
|
-
if (cssVars && !configContent.includes('shadcn:')) {
|
|
274
|
-
configContent = configContent.replace(/image:\s*\{[^}]*\},/, `$&\n\n // UI Framework: Shadcn
|
|
275
|
-
shadcn: {
|
|
276
|
-
prefix: '',
|
|
277
|
-
componentDir: '@/components/ui',
|
|
278
|
-
},`);
|
|
279
|
-
}
|
|
280
|
-
// Update site config if not present
|
|
281
|
-
if (!configContent.includes('site:')) {
|
|
282
|
-
configContent = configContent.replace(/compatibilityDate:\s*'[^']*',/, `$&\n\n // SEO: Centralizing data
|
|
283
|
-
site: {
|
|
284
|
-
name: 'New Setup',
|
|
285
|
-
url: 'https://newsetup.com',
|
|
286
|
-
description: 'A new setup for your project',
|
|
287
|
-
defaultLocale: 'en',
|
|
288
|
-
},`);
|
|
289
|
-
}
|
|
290
|
-
// Update ogImage config if not present
|
|
291
|
-
if (!configContent.includes('ogImage:')) {
|
|
292
|
-
configContent = configContent.replace(/sitemap:\s*\{[^}]*\},/, `$&\n ogImage: {
|
|
293
|
-
defaults: {
|
|
294
|
-
component: 'OgImageTemplate',
|
|
295
|
-
props: {
|
|
296
|
-
title: 'New Setup',
|
|
297
|
-
description: 'A new setup for your project',
|
|
298
|
-
image: 'https://newsetup.com/og-image.png',
|
|
299
|
-
},
|
|
300
|
-
}
|
|
301
|
-
},`);
|
|
302
|
-
}
|
|
303
|
-
// Update robots config if not present
|
|
304
|
-
if (!configContent.includes('robots:')) {
|
|
305
|
-
configContent = configContent.replace(/ogImage:\s*\{[^}]*\},/, `$&\n robots: {
|
|
306
|
-
disallow: ['/api',],
|
|
307
|
-
},`);
|
|
308
|
-
}
|
|
131
|
+
// Always write the full config, replacing any existing one
|
|
309
132
|
writeFileSync(configPath, configContent, 'utf-8');
|
|
310
133
|
}
|
|
311
134
|
export async function generateViteConfig(projectPath,
|
|
@@ -337,86 +160,86 @@ threejs, cssVars = false) {
|
|
|
337
160
|
: '';
|
|
338
161
|
const tailwindPlugin = cssVars ? ' tailwindcss(),\n' : '';
|
|
339
162
|
configContent = `import { fileURLToPath, URL } from 'node:url'
|
|
340
|
-
${tailwindImport}import { defineConfig } from 'vite'
|
|
341
|
-
import vue from '@vitejs/plugin-vue'
|
|
342
|
-
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
343
|
-
import viteCompression from 'vite-plugin-compression'
|
|
344
|
-
import Sitemap from 'vite-plugin-sitemap'
|
|
345
|
-
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
|
163
|
+
${tailwindImport}import { defineConfig } from 'vite'
|
|
164
|
+
import vue from '@vitejs/plugin-vue'
|
|
165
|
+
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
166
|
+
import viteCompression from 'vite-plugin-compression'
|
|
167
|
+
import Sitemap from 'vite-plugin-sitemap'
|
|
168
|
+
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'
|
|
346
169
|
|
|
347
|
-
export default defineConfig(({ mode }) => {
|
|
348
|
-
|
|
170
|
+
export default defineConfig(({ mode }) => {
|
|
171
|
+
const isProd = mode === 'production'
|
|
349
172
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
${tailwindPlugin} // 1. Only load DevTools in development
|
|
354
|
-
|
|
173
|
+
return {
|
|
174
|
+
plugins: [
|
|
175
|
+
vue(),
|
|
176
|
+
${tailwindPlugin} // 1. Only load DevTools in development
|
|
177
|
+
!isProd && vueDevTools(),
|
|
355
178
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
179
|
+
ViteImageOptimizer({
|
|
180
|
+
test: /\\.(jpe?g|png|gif|tiff|webp|svg|avif)$/i,
|
|
181
|
+
includePublic: true,
|
|
182
|
+
logStats: true,
|
|
183
|
+
png: { quality: 75 },
|
|
184
|
+
jpeg: { quality: 75 },
|
|
185
|
+
jpg: { quality: 75 },
|
|
186
|
+
webp: { lossless: false, quality: 75 },
|
|
187
|
+
avif: { quality: 70 },
|
|
188
|
+
}),
|
|
366
189
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
190
|
+
Sitemap({
|
|
191
|
+
hostname: 'https://newsetup.com', // Don't forget to update this!
|
|
192
|
+
dynamicRoutes: [],
|
|
193
|
+
}),
|
|
371
194
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
195
|
+
// 2. Gzip Compression (Universal Fallback)
|
|
196
|
+
viteCompression({
|
|
197
|
+
algorithm: 'gzip',
|
|
198
|
+
ext: '.gz',
|
|
199
|
+
threshold: 10240,
|
|
200
|
+
deleteOriginFile: false,
|
|
201
|
+
}),
|
|
379
202
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
203
|
+
// 3. Brotli Compression (Modern Performance)
|
|
204
|
+
viteCompression({
|
|
205
|
+
algorithm: 'brotliCompress',
|
|
206
|
+
ext: '.br',
|
|
207
|
+
threshold: 10240,
|
|
208
|
+
deleteOriginFile: false,
|
|
209
|
+
}),
|
|
210
|
+
],
|
|
388
211
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
212
|
+
resolve: {
|
|
213
|
+
alias: {
|
|
214
|
+
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
215
|
+
},
|
|
216
|
+
},
|
|
394
217
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
${threejsChunk}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
218
|
+
build: {
|
|
219
|
+
cssMinify: 'lightningcss', // Ensure 'lightningcss' is in package.json
|
|
220
|
+
// 4. Split Chunks for better Browser Caching
|
|
221
|
+
rollupOptions: {
|
|
222
|
+
output: {
|
|
223
|
+
manualChunks(id) {
|
|
224
|
+
if (id.includes('node_modules')) {
|
|
225
|
+
// Split standard Vue dependencies into their own chunk
|
|
226
|
+
if (id.includes('vue') || id.includes('pinia') || id.includes('vue-router')) {
|
|
227
|
+
return 'vue-vendor';
|
|
228
|
+
}
|
|
229
|
+
${threejsChunk}
|
|
230
|
+
|
|
231
|
+
return 'vendor';
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
410
236
|
},
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
237
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
})
|
|
238
|
+
esbuild: {
|
|
239
|
+
drop: isProd ? ['console', 'debugger'] : [],
|
|
240
|
+
},
|
|
241
|
+
}
|
|
242
|
+
})
|
|
420
243
|
`;
|
|
421
244
|
}
|
|
422
245
|
writeFileSync(configPath, configContent, 'utf-8');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ProjectType } from './project-detection.js';
|
|
2
2
|
export declare function generateCSSVariables(projectType: ProjectType, projectPath: string, initialSetup?: boolean): Promise<void>;
|
|
3
3
|
export declare function updateIndexHtml(projectPath: string): Promise<void>;
|
|
4
|
-
export declare function createTypographyPage(projectPath: string): Promise<void>;
|
|
4
|
+
export declare function createTypographyPage(projectPath: string, projectType?: ProjectType): Promise<void>;
|
|
@@ -262,8 +262,12 @@ export async function updateIndexHtml(projectPath) {
|
|
|
262
262
|
}
|
|
263
263
|
writeFileSync(indexPath, htmlContent, 'utf-8');
|
|
264
264
|
}
|
|
265
|
-
export async function createTypographyPage(projectPath) {
|
|
266
|
-
|
|
265
|
+
export async function createTypographyPage(projectPath, projectType) {
|
|
266
|
+
// For Nuxt, create in app/pages, for Vue use pages
|
|
267
|
+
const pagesDir = projectType === 'nuxt'
|
|
268
|
+
? join(projectPath, 'app', 'pages')
|
|
269
|
+
: join(projectPath, 'pages');
|
|
270
|
+
const typographyPagePath = join(pagesDir, 'typography', 'index.vue');
|
|
267
271
|
// Ensure directory exists
|
|
268
272
|
mkdirSync(dirname(typographyPagePath), { recursive: true });
|
|
269
273
|
const typographyPageContent = `<script setup lang="ts">
|