pni 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/dist/add-three-app.d.ts +6 -0
- package/dist/add-three-app.js +111 -0
- package/dist/app.d.ts +11 -0
- package/dist/app.js +143 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +71 -0
- package/dist/components/FeatureSelector.d.ts +21 -0
- package/dist/components/FeatureSelector.js +175 -0
- package/dist/components/ProgressIndicator.d.ts +7 -0
- package/dist/components/ProgressIndicator.js +46 -0
- package/dist/components/Summary.d.ts +8 -0
- package/dist/components/Summary.js +51 -0
- package/dist/components/WelcomeHeader.d.ts +2 -0
- package/dist/components/WelcomeHeader.js +8 -0
- package/dist/template_code/three/README.md +146 -0
- package/dist/template_code/three/World.js +133 -0
- package/dist/template_code/three/camera.js +30 -0
- package/dist/template_code/three/components/GlobeSphere.js +608 -0
- package/dist/template_code/three/components/cube.js +27 -0
- package/dist/template_code/three/components/lights.js +16 -0
- package/dist/template_code/three/components/sphere.js +26 -0
- package/dist/template_code/three/components/torus.js +25 -0
- package/dist/template_code/three/scene.js +28 -0
- package/dist/template_code/three/systems/Loop.js +43 -0
- package/dist/template_code/three/systems/Resizer.js +26 -0
- package/dist/template_code/three/systems/controls.js +19 -0
- package/dist/template_code/three/systems/post-processing.js +50 -0
- package/dist/template_code/three/systems/renderer.js +17 -0
- package/dist/template_code/three/utils/deviceDetector.js +141 -0
- package/dist/template_code/three/utils/gltfLoader.js +14 -0
- package/dist/template_code/three/utils/loadKTX2Texture.js +42 -0
- package/dist/template_code/three/utils/textureLoader.js +21 -0
- package/dist/utils/add-three.d.ts +7 -0
- package/dist/utils/add-three.js +288 -0
- package/dist/utils/app-creation.d.ts +4 -0
- package/dist/utils/app-creation.js +35 -0
- package/dist/utils/config-generator.d.ts +6 -0
- package/dist/utils/config-generator.js +508 -0
- package/dist/utils/css-variables.d.ts +4 -0
- package/dist/utils/css-variables.js +316 -0
- package/dist/utils/dependencies.d.ts +11 -0
- package/dist/utils/dependencies.js +68 -0
- package/dist/utils/package-manager.d.ts +4 -0
- package/dist/utils/package-manager.js +56 -0
- package/dist/utils/project-detection.d.ts +2 -0
- package/dist/utils/project-detection.js +60 -0
- package/dist/utils/shadcn-setup.d.ts +2 -0
- package/dist/utils/shadcn-setup.js +46 -0
- package/package.json +81 -0
- package/readme.md +119 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
const CSS_VARIABLES_CONTENT = `
|
|
4
|
+
@import "tailwindcss";
|
|
5
|
+
@import "tw-animate-css";
|
|
6
|
+
|
|
7
|
+
@font-face {
|
|
8
|
+
font-family: 'Helvetica';
|
|
9
|
+
src: url('~/assets/fonts/Helvetica.ttf') format('truetype');
|
|
10
|
+
font-weight: normal;
|
|
11
|
+
font-style: normal;
|
|
12
|
+
font-display: swap;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@custom-variant dark (&:is(.dark *));
|
|
16
|
+
|
|
17
|
+
@theme inline {
|
|
18
|
+
--spacing: 4rem;
|
|
19
|
+
--font-helvetica: 'Helvetica', sans-serif;
|
|
20
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
21
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
22
|
+
--radius-lg: var(--radius);
|
|
23
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
24
|
+
--color-background: var(--background);
|
|
25
|
+
--color-foreground: var(--foreground);
|
|
26
|
+
--color-card: var(--card);
|
|
27
|
+
--color-card-foreground: var(--card-foreground);
|
|
28
|
+
--color-popover: var(--popover);
|
|
29
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
30
|
+
--color-primary: var(--primary);
|
|
31
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
32
|
+
--color-secondary: var(--secondary);
|
|
33
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
34
|
+
--color-muted: var(--muted);
|
|
35
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
36
|
+
--color-accent: var(--accent);
|
|
37
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
38
|
+
--color-destructive: var(--destructive);
|
|
39
|
+
--color-border: var(--border);
|
|
40
|
+
--color-input: var(--input);
|
|
41
|
+
--color-ring: var(--ring);
|
|
42
|
+
--color-chart-1: var(--chart-1);
|
|
43
|
+
--color-chart-2: var(--chart-2);
|
|
44
|
+
--color-chart-3: var(--chart-3);
|
|
45
|
+
--color-chart-4: var(--chart-4);
|
|
46
|
+
--color-chart-5: var(--chart-5);
|
|
47
|
+
--color-sidebar: var(--sidebar);
|
|
48
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
49
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
50
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
51
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
52
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
53
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
54
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:root {
|
|
58
|
+
/* osmo scaling system starts here */
|
|
59
|
+
--size-unit: 16; /* body font-size in design - no px */
|
|
60
|
+
--size-container-ideal: 1440; /* screen-size in design - no px */
|
|
61
|
+
--size-container-min: 992px;
|
|
62
|
+
--size-container-max: 1920px;
|
|
63
|
+
--size-container: clamp(var(--size-container-min), 100vw, var(--size-container-max));
|
|
64
|
+
--size-font: calc(var(--size-container) / (var(--size-container-ideal) / var(--size-unit)));
|
|
65
|
+
|
|
66
|
+
/* 1 fluid pixel unit */
|
|
67
|
+
--fluid-px: calc(var(--size-font) / 16);
|
|
68
|
+
/* osmo scaling system ends here */
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
--radius: 0.625rem;
|
|
73
|
+
--background: oklch(1 0 0);
|
|
74
|
+
--foreground: oklch(0.145 0 0);
|
|
75
|
+
--card: oklch(1 0 0);
|
|
76
|
+
--card-foreground: oklch(0.145 0 0);
|
|
77
|
+
--popover: oklch(1 0 0);
|
|
78
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
79
|
+
--primary: oklch(0.205 0 0);
|
|
80
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
81
|
+
--secondary: oklch(0.97 0 0);
|
|
82
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
83
|
+
--muted: oklch(0.97 0 0);
|
|
84
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
85
|
+
--accent: oklch(0.97 0 0);
|
|
86
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
87
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
88
|
+
--border: oklch(0.922 0 0);
|
|
89
|
+
--input: oklch(0.922 0 0);
|
|
90
|
+
--ring: oklch(0.708 0 0);
|
|
91
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
92
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
93
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
94
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
95
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
96
|
+
--sidebar: oklch(0.985 0 0);
|
|
97
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
98
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
99
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
100
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
101
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
102
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
103
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.dark {
|
|
107
|
+
--background: oklch(0.145 0 0);
|
|
108
|
+
--foreground: oklch(0.985 0 0);
|
|
109
|
+
--card: oklch(0.205 0 0);
|
|
110
|
+
--card-foreground: oklch(0.985 0 0);
|
|
111
|
+
--popover: oklch(0.205 0 0);
|
|
112
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
113
|
+
--primary: oklch(0.922 0 0);
|
|
114
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
115
|
+
--secondary: oklch(0.269 0 0);
|
|
116
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
117
|
+
--muted: oklch(0.269 0 0);
|
|
118
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
119
|
+
--accent: oklch(0.269 0 0);
|
|
120
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
121
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
122
|
+
--border: oklch(1 0 0 / 10%);
|
|
123
|
+
--input: oklch(1 0 0 / 15%);
|
|
124
|
+
--ring: oklch(0.556 0 0);
|
|
125
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
126
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
127
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
128
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
129
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
130
|
+
--sidebar: oklch(0.205 0 0);
|
|
131
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
132
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
133
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
134
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
135
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
136
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
137
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@layer base {
|
|
141
|
+
* {
|
|
142
|
+
@apply border-border outline-ring/50;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
body {
|
|
146
|
+
@apply bg-background text-foreground;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
html {
|
|
151
|
+
font-size: var(--fluid-px);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
body {
|
|
155
|
+
/* figma design 16px fontsize = 16rem fluid pixel unit */
|
|
156
|
+
font-size: 16rem;
|
|
157
|
+
font-family: var(--font-helvetica);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
/* osmo scaling system starts here */
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
.container {
|
|
165
|
+
max-width: var(--size-container);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.container.medium {
|
|
169
|
+
max-width: calc(var(--size-container) * 0.85);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.container.small {
|
|
173
|
+
max-width: calc(var(--size-container) * 0.7);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Tablet */
|
|
177
|
+
@media screen and (max-width: 991px) {
|
|
178
|
+
:root {
|
|
179
|
+
--size-container-ideal: 834;
|
|
180
|
+
/* screen-size in design - no px */
|
|
181
|
+
--size-container-min: 768px;
|
|
182
|
+
--size-container-max: 991px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
body {
|
|
186
|
+
background-color: red;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Mobile Landscape */
|
|
191
|
+
@media screen and (max-width: 767px) {
|
|
192
|
+
:root {
|
|
193
|
+
--size-container-ideal: 550;
|
|
194
|
+
/* screen-size in design - no px */
|
|
195
|
+
--size-container-min: 480px;
|
|
196
|
+
--size-container-max: 767px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
body {
|
|
200
|
+
background-color: blue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Mobile Portrait */
|
|
205
|
+
@media screen and (max-width: 479px) {
|
|
206
|
+
:root {
|
|
207
|
+
--size-container-ideal: 375;
|
|
208
|
+
/* screen-size in design - no px */
|
|
209
|
+
--size-container-min: 320px;
|
|
210
|
+
--size-container-max: 479px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
body {
|
|
214
|
+
background-color: green;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
`;
|
|
219
|
+
export async function generateCSSVariables(projectType, projectPath, initialSetup = false) {
|
|
220
|
+
let cssPath;
|
|
221
|
+
if (projectType === 'nuxt') {
|
|
222
|
+
// Nuxt: always use app/assets/css/tailwind.css
|
|
223
|
+
cssPath = join(projectPath, 'app', 'assets', 'css', 'tailwind.css');
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// Vue: src/assets/style.css
|
|
227
|
+
cssPath = join(projectPath, 'src', 'assets', 'style.css');
|
|
228
|
+
}
|
|
229
|
+
// Ensure directory exists
|
|
230
|
+
mkdirSync(dirname(cssPath), { recursive: true });
|
|
231
|
+
if (initialSetup && projectType === 'nuxt') {
|
|
232
|
+
// For initial Nuxt setup, just write the basic import
|
|
233
|
+
writeFileSync(cssPath, '@import "tailwindcss";\n', 'utf-8');
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Overwrite the CSS file with full content
|
|
237
|
+
writeFileSync(cssPath, CSS_VARIABLES_CONTENT.trim() + '\n', 'utf-8');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export async function updateIndexHtml(projectPath) {
|
|
241
|
+
const indexPath = join(projectPath, 'index.html');
|
|
242
|
+
if (!existsSync(indexPath)) {
|
|
243
|
+
return; // Skip if index.html doesn't exist
|
|
244
|
+
}
|
|
245
|
+
let htmlContent = readFileSync(indexPath, 'utf-8');
|
|
246
|
+
// Check if stylesheet link already exists
|
|
247
|
+
if (htmlContent.includes('/src/assets/style.css')) {
|
|
248
|
+
return; // Already added
|
|
249
|
+
}
|
|
250
|
+
// Add stylesheet link in the head section
|
|
251
|
+
// Look for <head> tag and add the link before closing </head>
|
|
252
|
+
if (htmlContent.includes('</head>')) {
|
|
253
|
+
htmlContent = htmlContent.replace('</head>', ' <link href="/src/assets/style.css" rel="stylesheet">\n</head>');
|
|
254
|
+
}
|
|
255
|
+
else if (htmlContent.includes('<head>')) {
|
|
256
|
+
// If no closing head tag, add after opening head tag
|
|
257
|
+
htmlContent = htmlContent.replace('<head>', '<head>\n <link href="/src/assets/style.css" rel="stylesheet">');
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// If no head tag at all, add it after <html>
|
|
261
|
+
htmlContent = htmlContent.replace('<html>', '<html>\n<head>\n <link href="/src/assets/style.css" rel="stylesheet">\n</head>');
|
|
262
|
+
}
|
|
263
|
+
writeFileSync(indexPath, htmlContent, 'utf-8');
|
|
264
|
+
}
|
|
265
|
+
export async function createTypographyPage(projectPath) {
|
|
266
|
+
const typographyPagePath = join(projectPath, 'pages', 'typography', 'index.vue');
|
|
267
|
+
// Ensure directory exists
|
|
268
|
+
mkdirSync(dirname(typographyPagePath), { recursive: true });
|
|
269
|
+
const typographyPageContent = `<script setup lang="ts">
|
|
270
|
+
</script>
|
|
271
|
+
|
|
272
|
+
<template>
|
|
273
|
+
<section class="w-full py-20 px-4 min-h-screen flex items-center justify-center bg-[#DADADA] flex-col gap-16 ">
|
|
274
|
+
<div class="hero-container flex flex-col gap-16 leading-[1.2]">
|
|
275
|
+
<!-- figma design 120px fontsize -->
|
|
276
|
+
<h1 class="md:text-[120rem] text-[72rem]">Heading H1</h1>
|
|
277
|
+
<div class="divider"></div>
|
|
278
|
+
<!-- figma design 80px fontsize -->
|
|
279
|
+
<h2 class="md:text-[80rem] text-[64rem]">Heading H2</h2>
|
|
280
|
+
<div class="divider"></div>
|
|
281
|
+
<h3 class="md:text-[40rem] text-[32rem]">Heading H3</h3>
|
|
282
|
+
<!-- figma design 40px fontsize -->
|
|
283
|
+
<div class="divider"></div>
|
|
284
|
+
<h4 class="md:text-[28rem] text-[24rem]">Heading H4</h4>
|
|
285
|
+
<!-- figma design 28px fontsize -->
|
|
286
|
+
<div class="divider"></div>
|
|
287
|
+
<h5 class="md:text-[20rem] text-[18rem]">Heading H5</h5>
|
|
288
|
+
<!-- figma design 20px fontsize -->
|
|
289
|
+
<div class="divider"></div>
|
|
290
|
+
<!-- figma design 16px fontsize -->
|
|
291
|
+
<p class="text-[16rem] leading-[1.4] max-md:max-w-85.75 w-full font-helvetica">
|
|
292
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum
|
|
293
|
+
tristique. Duis cursus, all links in the website, eros dolor interdum nulla, ut commodo diam libero
|
|
294
|
+
vitae erat. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
|
|
295
|
+
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae
|
|
296
|
+
vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
|
|
297
|
+
sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
|
|
298
|
+
</p>
|
|
299
|
+
</div>
|
|
300
|
+
</section>
|
|
301
|
+
</template>
|
|
302
|
+
|
|
303
|
+
<style scoped>
|
|
304
|
+
.hero-container {
|
|
305
|
+
max-width: calc(1184em / 16);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.divider {
|
|
309
|
+
background-color: #969696;
|
|
310
|
+
height: 1px;
|
|
311
|
+
width: 100%;
|
|
312
|
+
}
|
|
313
|
+
</style>
|
|
314
|
+
`;
|
|
315
|
+
writeFileSync(typographyPagePath, typographyPageContent, 'utf-8');
|
|
316
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface DependencySet {
|
|
2
|
+
production: string[];
|
|
3
|
+
dev: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare const BASE_NUXT_DEPS: DependencySet;
|
|
6
|
+
export declare const BASE_VUE_DEPS: DependencySet;
|
|
7
|
+
export declare const THREEJS_NUXT_DEPS: DependencySet;
|
|
8
|
+
export declare const THREEJS_VUE_DEPS: DependencySet;
|
|
9
|
+
export declare const CSS_VARS_DEPS: DependencySet;
|
|
10
|
+
export declare const CSS_VARS_NUXT_DEPS: DependencySet;
|
|
11
|
+
export declare function getDependencies(projectType: 'nuxt' | 'vue', threejs: boolean, cssVars: boolean): DependencySet;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export const BASE_NUXT_DEPS = {
|
|
2
|
+
production: [
|
|
3
|
+
'@vueuse/core',
|
|
4
|
+
'@nuxtjs/seo',
|
|
5
|
+
'@nuxt/image',
|
|
6
|
+
'@nuxtjs/device',
|
|
7
|
+
'shadcn-nuxt',
|
|
8
|
+
],
|
|
9
|
+
dev: [],
|
|
10
|
+
};
|
|
11
|
+
export const BASE_VUE_DEPS = {
|
|
12
|
+
production: [],
|
|
13
|
+
dev: [],
|
|
14
|
+
};
|
|
15
|
+
export const THREEJS_NUXT_DEPS = {
|
|
16
|
+
production: ['three', '@vueuse/core', 'postprocessing'],
|
|
17
|
+
dev: ['@types/three'],
|
|
18
|
+
};
|
|
19
|
+
export const THREEJS_VUE_DEPS = {
|
|
20
|
+
production: ['three', '@vueuse/core'],
|
|
21
|
+
dev: [],
|
|
22
|
+
};
|
|
23
|
+
export const CSS_VARS_DEPS = {
|
|
24
|
+
production: [],
|
|
25
|
+
dev: ['tailwindcss', '@tailwindcss/vite'],
|
|
26
|
+
};
|
|
27
|
+
export const CSS_VARS_NUXT_DEPS = {
|
|
28
|
+
production: [],
|
|
29
|
+
dev: ['typescript', 'tailwindcss', '@tailwindcss/vite'],
|
|
30
|
+
};
|
|
31
|
+
export function getDependencies(projectType, threejs, cssVars) {
|
|
32
|
+
const deps = {
|
|
33
|
+
production: ['gsap', 'lenis'],
|
|
34
|
+
dev: [],
|
|
35
|
+
};
|
|
36
|
+
if (projectType === 'nuxt') {
|
|
37
|
+
deps.production.push(...BASE_NUXT_DEPS.production);
|
|
38
|
+
deps.dev.push(...BASE_NUXT_DEPS.dev);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
deps.production.push(...BASE_VUE_DEPS.production);
|
|
42
|
+
deps.dev.push(...BASE_VUE_DEPS.dev);
|
|
43
|
+
}
|
|
44
|
+
if (threejs) {
|
|
45
|
+
if (projectType === 'nuxt') {
|
|
46
|
+
deps.production.push(...THREEJS_NUXT_DEPS.production);
|
|
47
|
+
deps.dev.push(...THREEJS_NUXT_DEPS.dev);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
deps.production.push(...THREEJS_VUE_DEPS.production);
|
|
51
|
+
deps.dev.push(...THREEJS_VUE_DEPS.dev);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (cssVars) {
|
|
55
|
+
if (projectType === 'nuxt') {
|
|
56
|
+
deps.production.push(...CSS_VARS_NUXT_DEPS.production);
|
|
57
|
+
deps.dev.push(...CSS_VARS_NUXT_DEPS.dev);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
deps.production.push(...CSS_VARS_DEPS.production);
|
|
61
|
+
deps.dev.push(...CSS_VARS_DEPS.dev);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Remove duplicates
|
|
65
|
+
deps.production = [...new Set(deps.production)];
|
|
66
|
+
deps.dev = [...new Set(deps.dev)];
|
|
67
|
+
return deps;
|
|
68
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
2
|
+
export declare function detectPackageManager(cwd?: string): Promise<PackageManager>;
|
|
3
|
+
export declare function getInstallCommand(pm: PackageManager, packages: string[]): string;
|
|
4
|
+
export declare function getDevInstallCommand(pm: PackageManager, packages: string[]): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
export async function detectPackageManager(cwd = process.cwd()) {
|
|
5
|
+
// Check for lock files
|
|
6
|
+
if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
|
|
7
|
+
return 'pnpm';
|
|
8
|
+
}
|
|
9
|
+
if (existsSync(join(cwd, 'yarn.lock'))) {
|
|
10
|
+
return 'yarn';
|
|
11
|
+
}
|
|
12
|
+
if (existsSync(join(cwd, 'package-lock.json'))) {
|
|
13
|
+
return 'npm';
|
|
14
|
+
}
|
|
15
|
+
// Check if package managers are available
|
|
16
|
+
try {
|
|
17
|
+
execSync('pnpm --version', { stdio: 'ignore' });
|
|
18
|
+
return 'pnpm';
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// pnpm not available
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
execSync('yarn --version', { stdio: 'ignore' });
|
|
25
|
+
return 'yarn';
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// yarn not available
|
|
29
|
+
}
|
|
30
|
+
// Default to npm
|
|
31
|
+
return 'npm';
|
|
32
|
+
}
|
|
33
|
+
export function getInstallCommand(pm, packages) {
|
|
34
|
+
const packageList = packages.join(' ');
|
|
35
|
+
switch (pm) {
|
|
36
|
+
case 'pnpm':
|
|
37
|
+
return `pnpm add ${packageList}`;
|
|
38
|
+
case 'yarn':
|
|
39
|
+
return `yarn add ${packageList}`;
|
|
40
|
+
case 'npm':
|
|
41
|
+
default:
|
|
42
|
+
return `npm install ${packageList}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function getDevInstallCommand(pm, packages) {
|
|
46
|
+
const packageList = packages.join(' ');
|
|
47
|
+
switch (pm) {
|
|
48
|
+
case 'pnpm':
|
|
49
|
+
return `pnpm add -D ${packageList}`;
|
|
50
|
+
case 'yarn':
|
|
51
|
+
return `yarn add -D ${packageList}`;
|
|
52
|
+
case 'npm':
|
|
53
|
+
default:
|
|
54
|
+
return `npm install --save-dev ${packageList}`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
export async function detectProjectType(cwd = process.cwd()) {
|
|
4
|
+
// Check for Nuxt project
|
|
5
|
+
const nuxtConfigFiles = [
|
|
6
|
+
'nuxt.config.ts',
|
|
7
|
+
'nuxt.config.js',
|
|
8
|
+
'nuxt.config.mjs',
|
|
9
|
+
];
|
|
10
|
+
const hasNuxtConfig = nuxtConfigFiles.some(file => existsSync(join(cwd, file)));
|
|
11
|
+
if (hasNuxtConfig) {
|
|
12
|
+
return 'nuxt';
|
|
13
|
+
}
|
|
14
|
+
// Check package.json for Nuxt
|
|
15
|
+
const packageJsonPath = join(cwd, 'package.json');
|
|
16
|
+
if (existsSync(packageJsonPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
19
|
+
const deps = {
|
|
20
|
+
...packageJson.dependencies,
|
|
21
|
+
...packageJson.devDependencies,
|
|
22
|
+
};
|
|
23
|
+
if (deps.nuxt || deps['@nuxt/kit']) {
|
|
24
|
+
return 'nuxt';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Ignore parse errors
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Check for Vue project
|
|
32
|
+
const viteConfigFiles = [
|
|
33
|
+
'vite.config.ts',
|
|
34
|
+
'vite.config.js',
|
|
35
|
+
'vite.config.mjs',
|
|
36
|
+
];
|
|
37
|
+
const vueConfigFiles = ['vue.config.js', 'vue.config.ts'];
|
|
38
|
+
const hasViteConfig = viteConfigFiles.some(file => existsSync(join(cwd, file)));
|
|
39
|
+
const hasVueConfig = vueConfigFiles.some(file => existsSync(join(cwd, file)));
|
|
40
|
+
if (hasViteConfig || hasVueConfig) {
|
|
41
|
+
return 'vue';
|
|
42
|
+
}
|
|
43
|
+
// Check package.json for Vue
|
|
44
|
+
if (existsSync(packageJsonPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
47
|
+
const deps = {
|
|
48
|
+
...packageJson.dependencies,
|
|
49
|
+
...packageJson.devDependencies,
|
|
50
|
+
};
|
|
51
|
+
if (deps.vue && !deps.nuxt) {
|
|
52
|
+
return 'vue';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Ignore parse errors
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return 'none';
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
export async function setupShadcnNuxt(projectPath, packageManager) {
|
|
5
|
+
const isPnpm = packageManager === 'pnpm';
|
|
6
|
+
const execCommand = isPnpm ? 'pnpm dlx' : 'npx';
|
|
7
|
+
// Step 1: Create initial tailwind.css with basic import
|
|
8
|
+
// Always use app/assets/css/tailwind.css for Nuxt projects
|
|
9
|
+
const tailwindCssPath = join(projectPath, 'app', 'assets', 'css', 'tailwind.css');
|
|
10
|
+
mkdirSync(dirname(tailwindCssPath), { recursive: true });
|
|
11
|
+
writeFileSync(tailwindCssPath, '@import "tailwindcss";\n', 'utf-8');
|
|
12
|
+
// Step 2: Run nuxi module add shadcn-nuxt
|
|
13
|
+
execSync(`${execCommand} nuxi@latest module add shadcn-nuxt`, {
|
|
14
|
+
cwd: projectPath,
|
|
15
|
+
stdio: 'inherit',
|
|
16
|
+
});
|
|
17
|
+
// Step 3: Create SSR width plugin
|
|
18
|
+
const pluginPath1 = join(projectPath, 'app', 'plugins', 'ssr-width.ts');
|
|
19
|
+
const pluginPath2 = join(projectPath, 'plugins', 'ssr-width.ts');
|
|
20
|
+
const pluginPath = existsSync(join(projectPath, 'app'))
|
|
21
|
+
? pluginPath1
|
|
22
|
+
: pluginPath2;
|
|
23
|
+
mkdirSync(dirname(pluginPath), { recursive: true });
|
|
24
|
+
const pluginContent = `import { provideSSRWidth } from '@vueuse/core'
|
|
25
|
+
|
|
26
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
27
|
+
provideSSRWidth(1024, nuxtApp.vueApp)
|
|
28
|
+
})
|
|
29
|
+
`;
|
|
30
|
+
writeFileSync(pluginPath, pluginContent, 'utf-8');
|
|
31
|
+
// Step 4: Run nuxi prepare
|
|
32
|
+
execSync(`${execCommand} nuxi@latest prepare`, {
|
|
33
|
+
cwd: projectPath,
|
|
34
|
+
stdio: 'inherit',
|
|
35
|
+
});
|
|
36
|
+
// Step 5: Run shadcn-vue init
|
|
37
|
+
execSync(`${execCommand} shadcn-vue@latest init`, {
|
|
38
|
+
cwd: projectPath,
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
});
|
|
41
|
+
// Step 6: Run shadcn-vue add button
|
|
42
|
+
execSync(`${execCommand} shadcn-vue@latest add button`, {
|
|
43
|
+
cwd: projectPath,
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
});
|
|
46
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pni",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for creating Nuxt/Vue projects with Three.js and CSS variables setup",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pni": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/cli.js",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=16"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/alanfrancis442/pni.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/alanfrancis442/pni/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/alanfrancis442/pni#readme",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc && npm run copy-templates",
|
|
24
|
+
"copy-templates": "node scripts/copy-templates.js",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"dev:cli": "tsc && DEV=true node dist/cli.js",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"format:check": "prettier --check .",
|
|
29
|
+
"test": "prettier --check . && xo && ava",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"nuxt",
|
|
37
|
+
"vue",
|
|
38
|
+
"threejs",
|
|
39
|
+
"three.js",
|
|
40
|
+
"cli",
|
|
41
|
+
"scaffold",
|
|
42
|
+
"generator",
|
|
43
|
+
"tailwind",
|
|
44
|
+
"css-variables",
|
|
45
|
+
"shadcn"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"execa": "^8.0.1",
|
|
49
|
+
"ink": "^4.1.0",
|
|
50
|
+
"meow": "^11.0.0",
|
|
51
|
+
"react": "^18.2.0",
|
|
52
|
+
"react-devtools-core": "^4.28.5"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@sindresorhus/tsconfig": "^3.0.1",
|
|
56
|
+
"@types/react": "^18.0.32",
|
|
57
|
+
"@vdemedes/prettier-config": "^2.0.1",
|
|
58
|
+
"ava": "^5.2.0",
|
|
59
|
+
"chalk": "^5.2.0",
|
|
60
|
+
"ink-testing-library": "^3.0.0",
|
|
61
|
+
"prettier": "^2.8.7",
|
|
62
|
+
"ts-node": "^10.9.1",
|
|
63
|
+
"typescript": "^5.0.3",
|
|
64
|
+
"xo": "^1.2.3"
|
|
65
|
+
},
|
|
66
|
+
"ava": {
|
|
67
|
+
"extensions": {
|
|
68
|
+
"ts": "module",
|
|
69
|
+
"tsx": "module"
|
|
70
|
+
},
|
|
71
|
+
"nodeArguments": [
|
|
72
|
+
"--loader=ts-node/esm"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"overrides": {
|
|
76
|
+
"braces": "3.0.3",
|
|
77
|
+
"micromatch": "4.0.8",
|
|
78
|
+
"semver": "7.5.2"
|
|
79
|
+
},
|
|
80
|
+
"prettier": "@vdemedes/prettier-config"
|
|
81
|
+
}
|