create-universal-dapp 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 +165 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +52 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/nextjs.d.ts +3 -0
- package/dist/templates/nextjs.d.ts.map +1 -0
- package/dist/templates/nextjs.js +406 -0
- package/dist/templates/nextjs.js.map +1 -0
- package/dist/templates/vite.d.ts +3 -0
- package/dist/templates/vite.d.ts.map +1 -0
- package/dist/templates/vite.js +784 -0
- package/dist/templates/vite.js.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/package-manager.d.ts +4 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +67 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/prompts.d.ts +3 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +55 -0
- package/dist/utils/prompts.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
export async function createViteApp(options) {
|
|
5
|
+
const spinner = ora('Setting up Vite project...').start();
|
|
6
|
+
try {
|
|
7
|
+
// Create package.json
|
|
8
|
+
await createVitePackageJson(options);
|
|
9
|
+
// Create Vite configuration files
|
|
10
|
+
await createViteConfig(options);
|
|
11
|
+
// Create source structure
|
|
12
|
+
await createViteSourceFiles(options);
|
|
13
|
+
// Create Push Chain integration files
|
|
14
|
+
await createPushChainIntegration(options);
|
|
15
|
+
// Create ESLint config if requested
|
|
16
|
+
if (options.eslint) {
|
|
17
|
+
await createESLintConfig(options);
|
|
18
|
+
}
|
|
19
|
+
// Create Tailwind config (Tailwind-only setup)
|
|
20
|
+
await createTailwindConfig(options);
|
|
21
|
+
spinner.succeed('Vite project structure created');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
spinner.fail('Failed to create Vite project');
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function createVitePackageJson(options) {
|
|
29
|
+
const packageJson = {
|
|
30
|
+
name: options.projectName,
|
|
31
|
+
private: true,
|
|
32
|
+
version: "0.0.0",
|
|
33
|
+
type: "module",
|
|
34
|
+
scripts: {
|
|
35
|
+
dev: "vite",
|
|
36
|
+
build: "tsc && vite build",
|
|
37
|
+
lint: options.eslint ? "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" : undefined,
|
|
38
|
+
preview: "vite preview"
|
|
39
|
+
},
|
|
40
|
+
dependencies: {
|
|
41
|
+
react: "^18.3.1",
|
|
42
|
+
"react-dom": "^18.3.1",
|
|
43
|
+
"@pushchain/ui-kit": "^1.1.33"
|
|
44
|
+
},
|
|
45
|
+
devDependencies: {
|
|
46
|
+
"@types/react": "^18.2.43",
|
|
47
|
+
"@types/react-dom": "^18.2.17",
|
|
48
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
49
|
+
typescript: "^5.2.2",
|
|
50
|
+
vite: "^5.0.8",
|
|
51
|
+
tailwindcss: "^3.3.0",
|
|
52
|
+
autoprefixer: "^10.4.16",
|
|
53
|
+
postcss: "^8.4.31"
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
// Remove undefined values
|
|
57
|
+
Object.keys(packageJson.scripts).forEach(key => {
|
|
58
|
+
const scripts = packageJson.scripts;
|
|
59
|
+
if (scripts[key] === undefined) {
|
|
60
|
+
delete scripts[key];
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
await fs.writeJSON(path.join(options.targetDir, 'package.json'), packageJson, { spaces: 2 });
|
|
64
|
+
}
|
|
65
|
+
async function createViteConfig(options) {
|
|
66
|
+
// Vite config
|
|
67
|
+
const viteConfig = `import { defineConfig } from 'vite'
|
|
68
|
+
import react from '@vitejs/plugin-react'
|
|
69
|
+
import path from 'path'
|
|
70
|
+
|
|
71
|
+
// https://vitejs.dev/config/
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
plugins: [react()],
|
|
74
|
+
resolve: {
|
|
75
|
+
alias: {
|
|
76
|
+
"@": path.resolve(__dirname, "./src"),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
})`;
|
|
80
|
+
await fs.writeFile(path.join(options.targetDir, 'vite.config.ts'), viteConfig);
|
|
81
|
+
// TypeScript config
|
|
82
|
+
const tsConfig = {
|
|
83
|
+
compilerOptions: {
|
|
84
|
+
target: "ES2020",
|
|
85
|
+
useDefineForClassFields: true,
|
|
86
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
87
|
+
module: "ESNext",
|
|
88
|
+
skipLibCheck: true,
|
|
89
|
+
moduleResolution: "bundler",
|
|
90
|
+
allowImportingTsExtensions: true,
|
|
91
|
+
resolveJsonModule: true,
|
|
92
|
+
isolatedModules: true,
|
|
93
|
+
noEmit: true,
|
|
94
|
+
jsx: "react-jsx",
|
|
95
|
+
strict: true,
|
|
96
|
+
noUnusedLocals: true,
|
|
97
|
+
noUnusedParameters: true,
|
|
98
|
+
noFallthroughCasesInSwitch: true,
|
|
99
|
+
baseUrl: ".",
|
|
100
|
+
paths: {
|
|
101
|
+
"@/*": ["./src/*"]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
include: ["src"],
|
|
105
|
+
references: [{ "path": "./tsconfig.node.json" }]
|
|
106
|
+
};
|
|
107
|
+
await fs.writeJSON(path.join(options.targetDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
|
|
108
|
+
// Node TypeScript config
|
|
109
|
+
const tsNodeConfig = {
|
|
110
|
+
compilerOptions: {
|
|
111
|
+
composite: true,
|
|
112
|
+
skipLibCheck: true,
|
|
113
|
+
module: "ESNext",
|
|
114
|
+
moduleResolution: "bundler",
|
|
115
|
+
allowSyntheticDefaultImports: true
|
|
116
|
+
},
|
|
117
|
+
include: ["vite.config.ts"]
|
|
118
|
+
};
|
|
119
|
+
await fs.writeJSON(path.join(options.targetDir, 'tsconfig.node.json'), tsNodeConfig, { spaces: 2 });
|
|
120
|
+
}
|
|
121
|
+
async function createViteSourceFiles(options) {
|
|
122
|
+
// Create src directory structure
|
|
123
|
+
const srcDir = path.join(options.targetDir, 'src');
|
|
124
|
+
const componentsDir = path.join(srcDir, 'components');
|
|
125
|
+
const libDir = path.join(srcDir, 'lib');
|
|
126
|
+
await fs.ensureDir(srcDir);
|
|
127
|
+
await fs.ensureDir(componentsDir);
|
|
128
|
+
await fs.ensureDir(libDir);
|
|
129
|
+
// Create index.html
|
|
130
|
+
const indexHtml = `<!doctype html>
|
|
131
|
+
<html lang="en">
|
|
132
|
+
<head>
|
|
133
|
+
<meta charset="UTF-8" />
|
|
134
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
135
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
136
|
+
<title>${options.projectName}</title>
|
|
137
|
+
</head>
|
|
138
|
+
<body>
|
|
139
|
+
<div id="root"></div>
|
|
140
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
141
|
+
</body>
|
|
142
|
+
</html>`;
|
|
143
|
+
await fs.writeFile(path.join(options.targetDir, 'index.html'), indexHtml);
|
|
144
|
+
// Create main.tsx
|
|
145
|
+
const mainTsx = `import { StrictMode } from 'react'
|
|
146
|
+
import { createRoot } from 'react-dom/client'
|
|
147
|
+
import './index.css'
|
|
148
|
+
import App from './App.tsx'
|
|
149
|
+
import { PushChainProviders } from './providers/PushChainProviders.tsx'
|
|
150
|
+
|
|
151
|
+
createRoot(document.getElementById('root')!).render(
|
|
152
|
+
<StrictMode>
|
|
153
|
+
<PushChainProviders>
|
|
154
|
+
<App />
|
|
155
|
+
</PushChainProviders>
|
|
156
|
+
</StrictMode>,
|
|
157
|
+
)`;
|
|
158
|
+
await fs.writeFile(path.join(srcDir, 'main.tsx'), mainTsx);
|
|
159
|
+
// Create App.tsx
|
|
160
|
+
const appTsx = `import './App.css'
|
|
161
|
+
import { PushUniversalAccountButton, usePushWalletContext } from '@pushchain/ui-kit'
|
|
162
|
+
|
|
163
|
+
function App() {
|
|
164
|
+
const { universalAccount } = usePushWalletContext();
|
|
165
|
+
|
|
166
|
+
console.log("universalAccount", universalAccount);
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div style={{ minHeight: '100vh', padding: '2rem' }}>
|
|
170
|
+
<div style={{ maxWidth: '800px', margin: '0 auto' }}>
|
|
171
|
+
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
|
|
172
|
+
<h1 style={{ fontSize: '2.5rem', fontWeight: 'bold', marginBottom: '1rem' }}>
|
|
173
|
+
Welcome to ${options.projectName}
|
|
174
|
+
</h1>
|
|
175
|
+
<p style={{ fontSize: '1.2rem', color: '#666', marginBottom: '2rem' }}>
|
|
176
|
+
Your Push Chain application is ready to go!
|
|
177
|
+
</p>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div style={{
|
|
181
|
+
maxWidth: '600px',
|
|
182
|
+
margin: '0 auto',
|
|
183
|
+
padding: '2rem',
|
|
184
|
+
border: '1px solid #ddd',
|
|
185
|
+
borderRadius: '8px',
|
|
186
|
+
textAlign: 'center'
|
|
187
|
+
}}>
|
|
188
|
+
<h2 style={{ marginBottom: '1rem' }}>Push Chain Integration</h2>
|
|
189
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
190
|
+
<span style={{
|
|
191
|
+
padding: '4px 8px',
|
|
192
|
+
borderRadius: '4px',
|
|
193
|
+
backgroundColor: universalAccount ? '#22c55e' : '#6b7280',
|
|
194
|
+
color: 'white',
|
|
195
|
+
fontSize: '12px'
|
|
196
|
+
}}>
|
|
197
|
+
{universalAccount ? "Connected" : "Not Connected"}
|
|
198
|
+
</span>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div style={{ marginBottom: '2rem' }}>
|
|
202
|
+
<PushUniversalAccountButton />
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
{universalAccount && (
|
|
206
|
+
<div style={{
|
|
207
|
+
marginTop: '2rem',
|
|
208
|
+
padding: '1rem',
|
|
209
|
+
backgroundColor: '#f5f5f5',
|
|
210
|
+
borderRadius: '6px',
|
|
211
|
+
textAlign: 'left'
|
|
212
|
+
}}>
|
|
213
|
+
<h3 style={{ fontWeight: 'bold', marginBottom: '0.5rem' }}>Account Details:</h3>
|
|
214
|
+
<pre style={{ fontSize: '0.8rem', overflow: 'auto' }}>
|
|
215
|
+
{JSON.stringify(universalAccount, null, 2)}
|
|
216
|
+
</pre>
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
<div style={{ fontSize: '0.9rem', color: '#666', marginTop: '2rem', textAlign: 'left' }}>
|
|
221
|
+
<p><strong>Next steps:</strong></p>
|
|
222
|
+
<ul style={{ margin: '0.5rem 0', paddingLeft: '1.5rem' }}>
|
|
223
|
+
<li>Configure your Push Chain settings in providers/PushChainProviders.tsx</li>
|
|
224
|
+
<li>Customize the app metadata with your branding</li>
|
|
225
|
+
<li>Add your chain configuration and RPC URLs</li>
|
|
226
|
+
<li>Explore the @pushchain/ui-kit for more components</li>
|
|
227
|
+
</ul>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export default App`;
|
|
236
|
+
await fs.writeFile(path.join(srcDir, 'App.tsx'), appTsx);
|
|
237
|
+
// Create App.css
|
|
238
|
+
const appCss = `#root {
|
|
239
|
+
max-width: 1280px;
|
|
240
|
+
margin: 0 auto;
|
|
241
|
+
padding: 2rem;
|
|
242
|
+
text-align: center;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.logo {
|
|
246
|
+
height: 6em;
|
|
247
|
+
padding: 1.5em;
|
|
248
|
+
will-change: filter;
|
|
249
|
+
transition: filter 300ms;
|
|
250
|
+
}
|
|
251
|
+
.logo:hover {
|
|
252
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
253
|
+
}
|
|
254
|
+
.logo.react:hover {
|
|
255
|
+
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@keyframes logo-spin {
|
|
259
|
+
from {
|
|
260
|
+
transform: rotate(0deg);
|
|
261
|
+
}
|
|
262
|
+
to {
|
|
263
|
+
transform: rotate(360deg);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
268
|
+
a:nth-of-type(2) .logo {
|
|
269
|
+
animation: logo-spin infinite 20s linear;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.card {
|
|
274
|
+
padding: 2em;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.read-the-docs {
|
|
278
|
+
color: #888;
|
|
279
|
+
}`;
|
|
280
|
+
await fs.writeFile(path.join(srcDir, 'App.css'), appCss);
|
|
281
|
+
// Create index.css (Tailwind directives only)
|
|
282
|
+
const indexCss = `@tailwind base;
|
|
283
|
+
@tailwind components;
|
|
284
|
+
@tailwind utilities;
|
|
285
|
+
|
|
286
|
+
:root {
|
|
287
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
288
|
+
line-height: 1.5;
|
|
289
|
+
font-weight: 400;
|
|
290
|
+
|
|
291
|
+
color-scheme: light dark;
|
|
292
|
+
color: rgba(255, 255, 255, 0.87);
|
|
293
|
+
background-color: #242424;
|
|
294
|
+
|
|
295
|
+
font-synthesis: none;
|
|
296
|
+
text-rendering: optimizeLegibility;
|
|
297
|
+
-webkit-font-smoothing: antialiased;
|
|
298
|
+
-moz-osx-font-smoothing: grayscale;
|
|
299
|
+
-webkit-text-size-adjust: 100%;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
a {
|
|
303
|
+
font-weight: 500;
|
|
304
|
+
color: #646cff;
|
|
305
|
+
text-decoration: inherit;
|
|
306
|
+
}
|
|
307
|
+
a:hover {
|
|
308
|
+
color: #535bf2;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
body {
|
|
312
|
+
margin: 0;
|
|
313
|
+
display: flex;
|
|
314
|
+
place-items: center;
|
|
315
|
+
min-width: 320px;
|
|
316
|
+
min-height: 100vh;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
h1 {
|
|
320
|
+
font-size: 3.2em;
|
|
321
|
+
line-height: 1.1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
button {
|
|
325
|
+
border-radius: 8px;
|
|
326
|
+
border: 1px solid transparent;
|
|
327
|
+
padding: 0.6em 1.2em;
|
|
328
|
+
font-size: 1em;
|
|
329
|
+
font-weight: 500;
|
|
330
|
+
font-family: inherit;
|
|
331
|
+
background-color: #1a1a1a;
|
|
332
|
+
cursor: pointer;
|
|
333
|
+
transition: border-color 0.25s;
|
|
334
|
+
}
|
|
335
|
+
button:hover {
|
|
336
|
+
border-color: #646cff;
|
|
337
|
+
}
|
|
338
|
+
button:focus,
|
|
339
|
+
button:focus-visible {
|
|
340
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.container {
|
|
344
|
+
max-width: 1200px;
|
|
345
|
+
margin: 0 auto;
|
|
346
|
+
padding: 0 20px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@media (prefers-color-scheme: light) {
|
|
350
|
+
:root {
|
|
351
|
+
color: #213547;
|
|
352
|
+
background-color: #ffffff;
|
|
353
|
+
}
|
|
354
|
+
a:hover {
|
|
355
|
+
color: #747bff;
|
|
356
|
+
}
|
|
357
|
+
button {
|
|
358
|
+
background-color: #f9f9f9;
|
|
359
|
+
}
|
|
360
|
+
}`;
|
|
361
|
+
await fs.writeFile(path.join(srcDir, 'index.css'), indexCss);
|
|
362
|
+
// Create vite-env.d.ts
|
|
363
|
+
const viteEnv = `/// <reference types="vite/client" />`;
|
|
364
|
+
await fs.writeFile(path.join(srcDir, 'vite-env.d.ts'), viteEnv);
|
|
365
|
+
}
|
|
366
|
+
async function createPushChainIntegration(options) {
|
|
367
|
+
// Create providers directory
|
|
368
|
+
const providersDir = path.join(options.targetDir, 'src', 'providers');
|
|
369
|
+
await fs.ensureDir(providersDir);
|
|
370
|
+
// Create PushChainProviders.tsx following your pattern
|
|
371
|
+
const pushchainProviders = `import {
|
|
372
|
+
PushUI,
|
|
373
|
+
PushUniversalWalletProvider,
|
|
374
|
+
type AppMetadata,
|
|
375
|
+
type ProviderConfigProps,
|
|
376
|
+
} from "@pushchain/ui-kit";
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* PushChainProviders Component
|
|
380
|
+
*
|
|
381
|
+
* This component wraps your entire application with Push Chain's Universal Wallet Provider.
|
|
382
|
+
* It configures the wallet connection options, network settings, and app metadata.
|
|
383
|
+
*
|
|
384
|
+
* Configuration Guide:
|
|
385
|
+
* - network: Choose between MAINNET, TESTNET, or DEV
|
|
386
|
+
* - login: Configure authentication methods (email, google, wallet)
|
|
387
|
+
* - modal: Customize the UI appearance and behavior
|
|
388
|
+
* - chainConfig: Add your custom RPC endpoints
|
|
389
|
+
* - appMetadata: Brand your app with logo, title, and description
|
|
390
|
+
*/
|
|
391
|
+
|
|
392
|
+
const PushChainProviders = ({ children }: { children: React.ReactNode }) => {
|
|
393
|
+
// Wallet configuration - customize these settings for your app
|
|
394
|
+
const walletConfig: ProviderConfigProps = {
|
|
395
|
+
// Network selection: MAINNET for production, TESTNET for development
|
|
396
|
+
network: PushUI.CONSTANTS.PUSH_NETWORK.TESTNET,
|
|
397
|
+
|
|
398
|
+
// Login options - enable/disable authentication methods
|
|
399
|
+
login: {
|
|
400
|
+
email: true, // Allow email authentication
|
|
401
|
+
google: true, // Allow Google OAuth
|
|
402
|
+
wallet: {
|
|
403
|
+
enabled: true, // Allow wallet connection (MetaMask, etc.)
|
|
404
|
+
},
|
|
405
|
+
appPreview: true, // Show app preview in login modal
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
// Modal UI customization
|
|
409
|
+
modal: {
|
|
410
|
+
// Layout: SPLIT (side-by-side) or STACKED (vertical)
|
|
411
|
+
loginLayout: PushUI.CONSTANTS.LOGIN.LAYOUT.SPLIT,
|
|
412
|
+
|
|
413
|
+
// Connected wallet display: HOVER (on hover) or FULL (always visible)
|
|
414
|
+
connectedLayout: PushUI.CONSTANTS.CONNECTED.LAYOUT.HOVER,
|
|
415
|
+
|
|
416
|
+
// Show app preview in modals
|
|
417
|
+
appPreview: true,
|
|
418
|
+
|
|
419
|
+
// Background interaction when modal is open: BLUR or NONE
|
|
420
|
+
connectedInteraction: PushUI.CONSTANTS.CONNECTED.INTERACTION.BLUR,
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
// Chain configuration - add your custom RPC endpoints here
|
|
424
|
+
chainConfig: {
|
|
425
|
+
rpcUrls: {
|
|
426
|
+
// Ethereum Sepolia testnet (for testing)
|
|
427
|
+
"eip155:11155111": ["https://sepolia.gateway.tenderly.co/"],
|
|
428
|
+
|
|
429
|
+
// Add more chains as needed:
|
|
430
|
+
// "eip155:1": ["https://mainnet.infura.io/v3/YOUR_PROJECT_ID"], // Ethereum Mainnet
|
|
431
|
+
// "eip155:137": ["https://polygon-rpc.com/"], // Polygon
|
|
432
|
+
// "eip155:42161": ["https://arb1.arbitrum.io/rpc"], // Arbitrum
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// App metadata - customize with your app's branding
|
|
438
|
+
const appMetadata: AppMetadata = {
|
|
439
|
+
// Your app's logo URL (can be a URL or base64 data URI)
|
|
440
|
+
logoUrl: "https://avatars.githubusercontent.com/u/64157541?v=4",
|
|
441
|
+
|
|
442
|
+
// Your app's display name
|
|
443
|
+
title: "${options.projectName}",
|
|
444
|
+
|
|
445
|
+
// Brief description of your app (shown in wallet connection prompts)
|
|
446
|
+
description:
|
|
447
|
+
"Push Chain is a shared state L1 blockchain that allows all chains to unify, enabling apps of any chain to be accessed by users of any chain.",
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<PushUniversalWalletProvider config={walletConfig} app={appMetadata}>
|
|
452
|
+
{children}
|
|
453
|
+
</PushUniversalWalletProvider>
|
|
454
|
+
);
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
export { PushChainProviders };`;
|
|
458
|
+
await fs.writeFile(path.join(providersDir, 'PushChainProviders.tsx'), pushchainProviders);
|
|
459
|
+
// Create .env file with configuration notes
|
|
460
|
+
const envFile = `# Push Chain Configuration
|
|
461
|
+
#
|
|
462
|
+
# This file contains environment variables for your Push Chain application.
|
|
463
|
+
# Copy this file to .env.local for local development.
|
|
464
|
+
#
|
|
465
|
+
# IMPORTANT: Never commit sensitive API keys to version control!
|
|
466
|
+
|
|
467
|
+
# Push Chain Network Configuration
|
|
468
|
+
# Options: mainnet, testnet, dev
|
|
469
|
+
VITE_PUSHCHAIN_NETWORK=testnet
|
|
470
|
+
|
|
471
|
+
# Optional: Custom API endpoints (if you have specific requirements)
|
|
472
|
+
# VITE_PUSHCHAIN_API_ENDPOINT=https://your-custom-endpoint.com
|
|
473
|
+
|
|
474
|
+
# Optional: App-specific configuration
|
|
475
|
+
# VITE_APP_NAME=${options.projectName}
|
|
476
|
+
# VITE_APP_VERSION=1.0.0`;
|
|
477
|
+
await fs.writeFile(path.join(options.targetDir, '.env'), envFile);
|
|
478
|
+
}
|
|
479
|
+
async function createESLintConfig(options) {
|
|
480
|
+
const eslintConfig = {
|
|
481
|
+
root: true,
|
|
482
|
+
env: { browser: true, es2020: true },
|
|
483
|
+
extends: [
|
|
484
|
+
'eslint:recommended',
|
|
485
|
+
'@typescript-eslint/recommended',
|
|
486
|
+
'eslint-plugin-react-hooks/recommended',
|
|
487
|
+
],
|
|
488
|
+
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
|
489
|
+
parser: '@typescript-eslint/parser',
|
|
490
|
+
plugins: ['react-refresh'],
|
|
491
|
+
rules: {
|
|
492
|
+
'react-refresh/only-export-components': [
|
|
493
|
+
'warn',
|
|
494
|
+
{ allowConstantExport: true },
|
|
495
|
+
],
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
await fs.writeJSON(path.join(options.targetDir, '.eslintrc.json'), eslintConfig, { spaces: 2 });
|
|
499
|
+
}
|
|
500
|
+
async function createTailwindConfig(options) {
|
|
501
|
+
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
|
|
502
|
+
export default {
|
|
503
|
+
darkMode: ["class"],
|
|
504
|
+
content: [
|
|
505
|
+
"./index.html",
|
|
506
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
507
|
+
],
|
|
508
|
+
theme: {
|
|
509
|
+
container: {
|
|
510
|
+
center: true,
|
|
511
|
+
padding: "2rem",
|
|
512
|
+
screens: {
|
|
513
|
+
"2xl": "1400px",
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
extend: {
|
|
517
|
+
colors: {
|
|
518
|
+
border: "hsl(var(--border))",
|
|
519
|
+
input: "hsl(var(--input))",
|
|
520
|
+
ring: "hsl(var(--ring))",
|
|
521
|
+
background: "hsl(var(--background))",
|
|
522
|
+
foreground: "hsl(var(--foreground))",
|
|
523
|
+
primary: {
|
|
524
|
+
DEFAULT: "hsl(var(--primary))",
|
|
525
|
+
foreground: "hsl(var(--primary-foreground))",
|
|
526
|
+
},
|
|
527
|
+
secondary: {
|
|
528
|
+
DEFAULT: "hsl(var(--secondary))",
|
|
529
|
+
foreground: "hsl(var(--secondary-foreground))",
|
|
530
|
+
},
|
|
531
|
+
destructive: {
|
|
532
|
+
DEFAULT: "hsl(var(--destructive))",
|
|
533
|
+
foreground: "hsl(var(--destructive-foreground))",
|
|
534
|
+
},
|
|
535
|
+
muted: {
|
|
536
|
+
DEFAULT: "hsl(var(--muted))",
|
|
537
|
+
foreground: "hsl(var(--muted-foreground))",
|
|
538
|
+
},
|
|
539
|
+
accent: {
|
|
540
|
+
DEFAULT: "hsl(var(--accent))",
|
|
541
|
+
foreground: "hsl(var(--accent-foreground))",
|
|
542
|
+
},
|
|
543
|
+
popover: {
|
|
544
|
+
DEFAULT: "hsl(var(--popover))",
|
|
545
|
+
foreground: "hsl(var(--popover-foreground))",
|
|
546
|
+
},
|
|
547
|
+
card: {
|
|
548
|
+
DEFAULT: "hsl(var(--card))",
|
|
549
|
+
foreground: "hsl(var(--card-foreground))",
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
borderRadius: {
|
|
553
|
+
lg: "var(--radius)",
|
|
554
|
+
md: "calc(var(--radius) - 2px)",
|
|
555
|
+
sm: "calc(var(--radius) - 4px)",
|
|
556
|
+
},
|
|
557
|
+
keyframes: {
|
|
558
|
+
"accordion-down": {
|
|
559
|
+
from: { height: 0 },
|
|
560
|
+
to: { height: "var(--radix-accordion-content-height)" },
|
|
561
|
+
},
|
|
562
|
+
"accordion-up": {
|
|
563
|
+
from: { height: "var(--radix-accordion-content-height)" },
|
|
564
|
+
to: { height: 0 },
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
animation: {
|
|
568
|
+
"accordion-down": "accordion-down 0.2s ease-out",
|
|
569
|
+
"accordion-up": "accordion-up 0.2s ease-out",
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
plugins: [require("tailwindcss-animate")],
|
|
574
|
+
}`;
|
|
575
|
+
await fs.writeFile(path.join(options.targetDir, 'tailwind.config.js'), tailwindConfig);
|
|
576
|
+
// PostCSS config
|
|
577
|
+
const postcssConfig = `export default {
|
|
578
|
+
plugins: {
|
|
579
|
+
tailwindcss: {},
|
|
580
|
+
autoprefixer: {},
|
|
581
|
+
},
|
|
582
|
+
}`;
|
|
583
|
+
await fs.writeFile(path.join(options.targetDir, 'postcss.config.js'), postcssConfig);
|
|
584
|
+
}
|
|
585
|
+
async function createShadcnConfig(options) {
|
|
586
|
+
const componentsJson = {
|
|
587
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
588
|
+
"style": "default",
|
|
589
|
+
"rsc": false,
|
|
590
|
+
"tsx": true,
|
|
591
|
+
"tailwind": {
|
|
592
|
+
"config": "tailwind.config.js",
|
|
593
|
+
"css": "src/index.css",
|
|
594
|
+
"baseColor": "slate",
|
|
595
|
+
"cssVariables": true
|
|
596
|
+
},
|
|
597
|
+
"aliases": {
|
|
598
|
+
"components": "@/components",
|
|
599
|
+
"utils": "@/lib/utils"
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
await fs.writeJSON(path.join(options.targetDir, 'components.json'), componentsJson, { spaces: 2 });
|
|
603
|
+
// Create basic shadcn components (same as Next.js version)
|
|
604
|
+
const componentsDir = path.join(options.targetDir, 'src', 'components', 'ui');
|
|
605
|
+
await fs.ensureDir(componentsDir);
|
|
606
|
+
// Button component (same as Next.js)
|
|
607
|
+
const buttonComponent = `import * as React from "react"
|
|
608
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
609
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
610
|
+
|
|
611
|
+
import { cn } from "@/lib/utils"
|
|
612
|
+
|
|
613
|
+
const buttonVariants = cva(
|
|
614
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
615
|
+
{
|
|
616
|
+
variants: {
|
|
617
|
+
variant: {
|
|
618
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
619
|
+
destructive:
|
|
620
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
621
|
+
outline:
|
|
622
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
623
|
+
secondary:
|
|
624
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
625
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
626
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
627
|
+
},
|
|
628
|
+
size: {
|
|
629
|
+
default: "h-10 px-4 py-2",
|
|
630
|
+
sm: "h-9 rounded-md px-3",
|
|
631
|
+
lg: "h-11 rounded-md px-8",
|
|
632
|
+
icon: "h-10 w-10",
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
defaultVariants: {
|
|
636
|
+
variant: "default",
|
|
637
|
+
size: "default",
|
|
638
|
+
},
|
|
639
|
+
}
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
export interface ButtonProps
|
|
643
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
644
|
+
VariantProps<typeof buttonVariants> {
|
|
645
|
+
asChild?: boolean
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
649
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
650
|
+
const Comp = asChild ? Slot : "button"
|
|
651
|
+
return (
|
|
652
|
+
<Comp
|
|
653
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
654
|
+
ref={ref}
|
|
655
|
+
{...props}
|
|
656
|
+
/>
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
Button.displayName = "Button"
|
|
661
|
+
|
|
662
|
+
export { Button, buttonVariants }`;
|
|
663
|
+
await fs.writeFile(path.join(componentsDir, 'button.tsx'), buttonComponent);
|
|
664
|
+
// Card and Badge components (same as Next.js)
|
|
665
|
+
const cardComponent = `import * as React from "react"
|
|
666
|
+
|
|
667
|
+
import { cn } from "@/lib/utils"
|
|
668
|
+
|
|
669
|
+
const Card = React.forwardRef<
|
|
670
|
+
HTMLDivElement,
|
|
671
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
672
|
+
>(({ className, ...props }, ref) => (
|
|
673
|
+
<div
|
|
674
|
+
ref={ref}
|
|
675
|
+
className={cn(
|
|
676
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
677
|
+
className
|
|
678
|
+
)}
|
|
679
|
+
{...props}
|
|
680
|
+
/>
|
|
681
|
+
))
|
|
682
|
+
Card.displayName = "Card"
|
|
683
|
+
|
|
684
|
+
const CardHeader = React.forwardRef<
|
|
685
|
+
HTMLDivElement,
|
|
686
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
687
|
+
>(({ className, ...props }, ref) => (
|
|
688
|
+
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
689
|
+
))
|
|
690
|
+
CardHeader.displayName = "CardHeader"
|
|
691
|
+
|
|
692
|
+
const CardTitle = React.forwardRef<
|
|
693
|
+
HTMLParagraphElement,
|
|
694
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
695
|
+
>(({ className, ...props }, ref) => (
|
|
696
|
+
<h3
|
|
697
|
+
ref={ref}
|
|
698
|
+
className={cn(
|
|
699
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
700
|
+
className
|
|
701
|
+
)}
|
|
702
|
+
{...props}
|
|
703
|
+
/>
|
|
704
|
+
))
|
|
705
|
+
CardTitle.displayName = "CardTitle"
|
|
706
|
+
|
|
707
|
+
const CardDescription = React.forwardRef<
|
|
708
|
+
HTMLParagraphElement,
|
|
709
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
710
|
+
>(({ className, ...props }, ref) => (
|
|
711
|
+
<p
|
|
712
|
+
ref={ref}
|
|
713
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
714
|
+
{...props}
|
|
715
|
+
/>
|
|
716
|
+
))
|
|
717
|
+
CardDescription.displayName = "CardDescription"
|
|
718
|
+
|
|
719
|
+
const CardContent = React.forwardRef<
|
|
720
|
+
HTMLDivElement,
|
|
721
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
722
|
+
>(({ className, ...props }, ref) => (
|
|
723
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
724
|
+
))
|
|
725
|
+
CardContent.displayName = "CardContent"
|
|
726
|
+
|
|
727
|
+
const CardFooter = React.forwardRef<
|
|
728
|
+
HTMLDivElement,
|
|
729
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
730
|
+
>(({ className, ...props }, ref) => (
|
|
731
|
+
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
732
|
+
))
|
|
733
|
+
CardFooter.displayName = "CardFooter"
|
|
734
|
+
|
|
735
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }`;
|
|
736
|
+
await fs.writeFile(path.join(componentsDir, 'card.tsx'), cardComponent);
|
|
737
|
+
const badgeComponent = `import * as React from "react"
|
|
738
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
739
|
+
|
|
740
|
+
import { cn } from "@/lib/utils"
|
|
741
|
+
|
|
742
|
+
const badgeVariants = cva(
|
|
743
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
744
|
+
{
|
|
745
|
+
variants: {
|
|
746
|
+
variant: {
|
|
747
|
+
default:
|
|
748
|
+
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
|
749
|
+
secondary:
|
|
750
|
+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
751
|
+
destructive:
|
|
752
|
+
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
753
|
+
outline: "text-foreground",
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
defaultVariants: {
|
|
757
|
+
variant: "default",
|
|
758
|
+
},
|
|
759
|
+
}
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
export interface BadgeProps
|
|
763
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
764
|
+
VariantProps<typeof badgeVariants> {}
|
|
765
|
+
|
|
766
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
767
|
+
return (
|
|
768
|
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
769
|
+
)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
export { Badge, badgeVariants }`;
|
|
773
|
+
await fs.writeFile(path.join(componentsDir, 'badge.tsx'), badgeComponent);
|
|
774
|
+
// Utils file (same as Next.js)
|
|
775
|
+
const libDir = path.join(options.targetDir, 'src', 'lib');
|
|
776
|
+
const utilsFile = `import { type ClassValue, clsx } from "clsx"
|
|
777
|
+
import { twMerge } from "tailwind-merge"
|
|
778
|
+
|
|
779
|
+
export function cn(...inputs: ClassValue[]) {
|
|
780
|
+
return twMerge(clsx(inputs))
|
|
781
|
+
}`;
|
|
782
|
+
await fs.writeFile(path.join(libDir, 'utils.ts'), utilsFile);
|
|
783
|
+
}
|
|
784
|
+
//# sourceMappingURL=vite.js.map
|