create-vista-app 0.2.12 → 0.2.13
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/bin/cli.js +261 -31
- package/bin/flash-template/app/index.tsx +91 -0
- package/bin/flash-template/app/root.tsx +32 -0
- package/package.json +1 -1
- package/template/README.md +36 -7
- package/template/app/globals.css +9 -4
- package/template/app/index.tsx +61 -19
- package/template/app/root.tsx +7 -2
- package/template/vista-env.d.ts +1 -1
- package/template/vista.config.ts +4 -0
- package/template-typed/vista.config.ts +4 -0
package/bin/cli.js
CHANGED
|
@@ -6,37 +6,95 @@ const { execSync } = require('child_process');
|
|
|
6
6
|
const prompts = require('prompts');
|
|
7
7
|
|
|
8
8
|
const usageCommand = 'npx create-vista-app@latest <project-name>';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
10
|
+
|
|
11
|
+
// Detect which package manager invoked us (npm, pnpm, yarn, bun)
|
|
12
|
+
function normalizePackageManager(value) {
|
|
13
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
14
|
+
return SUPPORTED_PACKAGE_MANAGERS.includes(normalized) ? normalized : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function detectPackageManager(userAgent = process.env.npm_config_user_agent || '') {
|
|
18
|
+
const ua = String(userAgent || '');
|
|
19
|
+
if (ua.startsWith('pnpm')) return 'pnpm';
|
|
20
|
+
if (ua.startsWith('yarn')) return 'yarn';
|
|
21
|
+
if (ua.startsWith('bun')) return 'bun';
|
|
22
|
+
return 'npm';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getExplicitPackageManagerFromArgs(args) {
|
|
26
|
+
const explicitValue = normalizePackageManager(getFlagValue('--package-manager'));
|
|
27
|
+
const explicitFlags = SUPPORTED_PACKAGE_MANAGERS.filter((manager) => args.includes(`--${manager}`));
|
|
28
|
+
|
|
29
|
+
if (explicitFlags.length > 1) {
|
|
30
|
+
console.error('Error: use only one package manager flag: --npm, --pnpm, --yarn, or --bun.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (getFlagValue('--package-manager') && !explicitValue) {
|
|
35
|
+
console.error(
|
|
36
|
+
`Error: unsupported package manager "${getFlagValue('--package-manager')}". Use npm, pnpm, yarn, or bun.`
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (explicitValue && explicitFlags.length > 0 && explicitFlags[0] !== explicitValue) {
|
|
42
|
+
console.error('Error: package manager flags conflict. Use only one package manager selector.');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return explicitValue || explicitFlags[0];
|
|
47
|
+
}
|
|
48
|
+
|
|
20
49
|
const rawArgs = process.argv.slice(2);
|
|
21
50
|
const useTypedApiStarter = rawArgs.includes('--typed-api') || rawArgs.includes('--typed');
|
|
22
51
|
const skipInstall = rawArgs.includes('--skip-install');
|
|
23
52
|
const skipGit = rawArgs.includes('--no-git');
|
|
24
53
|
const assumeYes = rawArgs.includes('--yes') || rawArgs.includes('-y');
|
|
25
54
|
const canPrompt = !!(process.stdin.isTTY && process.stdout.isTTY);
|
|
55
|
+
const detectedPackageManager = detectPackageManager();
|
|
56
|
+
|
|
57
|
+
function getFlagValue(flag) {
|
|
58
|
+
const index = rawArgs.indexOf(flag);
|
|
59
|
+
if (index !== -1) {
|
|
60
|
+
const next = rawArgs[index + 1];
|
|
61
|
+
if (next && !next.startsWith('-')) return next;
|
|
62
|
+
}
|
|
63
|
+
const inline = rawArgs.find((arg) => arg.startsWith(`${flag}=`));
|
|
64
|
+
if (inline) return inline.slice(flag.length + 1);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const explicitFlashpack = rawArgs.includes('--flashpack');
|
|
69
|
+
const explicitDefaultEngine = rawArgs.includes('--default-engine');
|
|
70
|
+
const explicitEngine = getFlagValue('--engine');
|
|
71
|
+
const explicitPackageManager = getExplicitPackageManagerFromArgs(rawArgs);
|
|
26
72
|
|
|
27
73
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
28
74
|
console.log(`
|
|
29
75
|
Usage:
|
|
30
|
-
${usageCommand} [--typed-api] [--skip-install] [--no-git] [--yes]
|
|
76
|
+
${usageCommand} [--typed-api] [--skip-install] [--no-git] [--yes] [--engine <default|flashpack>] [--flashpack] [--default-engine] [--package-manager <npm|pnpm|yarn|bun>] [--npm|--pnpm|--yarn|--bun]
|
|
31
77
|
|
|
32
78
|
Example:
|
|
33
79
|
npx create-vista-app@latest my-vista-app
|
|
34
80
|
npx create-vista-app@latest
|
|
35
81
|
npx create-vista-app@latest my-vista-app --typed-api
|
|
82
|
+
npx create-vista-app@latest my-vista-app --flashpack
|
|
83
|
+
npx create-vista-app@latest my-vista-app --package-manager pnpm
|
|
36
84
|
`);
|
|
37
85
|
process.exit(0);
|
|
38
86
|
}
|
|
39
87
|
|
|
88
|
+
if (explicitFlashpack && explicitDefaultEngine) {
|
|
89
|
+
console.error('Error: use only one of --flashpack or --default-engine.');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (explicitEngine && !['default', 'flashpack'].includes(explicitEngine)) {
|
|
94
|
+
console.error(`Error: unsupported engine "${explicitEngine}". Use "default" or "flashpack".`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
40
98
|
async function resolveProjectName() {
|
|
41
99
|
const args = rawArgs.filter((arg) => !arg.startsWith('-'));
|
|
42
100
|
if (args[0]) return args[0];
|
|
@@ -65,17 +123,109 @@ async function resolveProjectName() {
|
|
|
65
123
|
}
|
|
66
124
|
return value;
|
|
67
125
|
}
|
|
68
|
-
async function confirmProceed(projectName, projectDir) {
|
|
126
|
+
async function confirmProceed(projectName, projectDir, engine, packageManager) {
|
|
69
127
|
if (assumeYes || !canPrompt) return true;
|
|
70
128
|
const response = await prompts({
|
|
71
129
|
type: 'confirm',
|
|
72
130
|
name: 'proceed',
|
|
73
|
-
message: `Create Vista app "${projectName}" in ${projectDir}?`,
|
|
131
|
+
message: `Create Vista app "${projectName}" in ${projectDir} (engine: ${engine}, package manager: ${packageManager})?`,
|
|
74
132
|
initial: true,
|
|
75
133
|
});
|
|
76
134
|
return response.proceed !== false;
|
|
77
135
|
}
|
|
78
136
|
|
|
137
|
+
async function resolveEngineChoice() {
|
|
138
|
+
if (explicitEngine) return explicitEngine;
|
|
139
|
+
if (explicitFlashpack) return 'flashpack';
|
|
140
|
+
if (explicitDefaultEngine) return 'default';
|
|
141
|
+
if (assumeYes || !canPrompt) return 'default';
|
|
142
|
+
|
|
143
|
+
const response = await prompts({
|
|
144
|
+
type: 'select',
|
|
145
|
+
name: 'engine',
|
|
146
|
+
message: 'Select engine',
|
|
147
|
+
choices: [
|
|
148
|
+
{
|
|
149
|
+
title: 'default (recommended)',
|
|
150
|
+
value: 'default',
|
|
151
|
+
description: 'Stable webpack-first path',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
title: 'flashpack',
|
|
155
|
+
value: 'flashpack',
|
|
156
|
+
description: 'Rust-first engine path',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
initial: 0,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const value = String(response.engine || '').trim();
|
|
163
|
+
if (!value) {
|
|
164
|
+
console.log('Aborted.');
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function resolvePackageManagerChoice() {
|
|
171
|
+
if (explicitPackageManager) return explicitPackageManager;
|
|
172
|
+
if (assumeYes || !canPrompt) return detectedPackageManager;
|
|
173
|
+
|
|
174
|
+
const response = await prompts({
|
|
175
|
+
type: 'select',
|
|
176
|
+
name: 'packageManager',
|
|
177
|
+
message: 'Select package manager',
|
|
178
|
+
choices: [
|
|
179
|
+
{
|
|
180
|
+
title: 'npm',
|
|
181
|
+
value: 'npm',
|
|
182
|
+
description: 'Widely available default',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
title: 'pnpm',
|
|
186
|
+
value: 'pnpm',
|
|
187
|
+
description: 'Fast installs with shared store',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
title: 'yarn',
|
|
191
|
+
value: 'yarn',
|
|
192
|
+
description: 'Classic Yarn workflow',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: 'bun',
|
|
196
|
+
value: 'bun',
|
|
197
|
+
description: 'Fast Bun-based install/runtime',
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
initial: Math.max(SUPPORTED_PACKAGE_MANAGERS.indexOf(detectedPackageManager), 0),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const value = normalizePackageManager(response.packageManager);
|
|
204
|
+
if (!value) {
|
|
205
|
+
console.log('Aborted.');
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function getInstallCommand(packageManager) {
|
|
213
|
+
if (packageManager === 'yarn') return 'yarn';
|
|
214
|
+
if (packageManager === 'bun') return 'bun install';
|
|
215
|
+
return `${packageManager} install`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function getRunCommand(packageManager) {
|
|
219
|
+
return packageManager === 'npm' ? 'npm run' : packageManager;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function getCreateCommand(packageManager) {
|
|
223
|
+
if (packageManager === 'pnpm') return 'pnpm create vista-app';
|
|
224
|
+
if (packageManager === 'yarn') return 'yarn create vista-app';
|
|
225
|
+
if (packageManager === 'bun') return 'bun create vista-app';
|
|
226
|
+
return 'npx create-vista-app@latest';
|
|
227
|
+
}
|
|
228
|
+
|
|
79
229
|
function copyRecursiveSync(src, dest) {
|
|
80
230
|
const exists = fs.existsSync(src);
|
|
81
231
|
const stats = exists && fs.statSync(src);
|
|
@@ -90,13 +240,74 @@ function copyRecursiveSync(src, dest) {
|
|
|
90
240
|
}
|
|
91
241
|
}
|
|
92
242
|
|
|
243
|
+
function injectEngineBlock(source, selectedEngine) {
|
|
244
|
+
// Prefer preserving existing formatting when an engine block already exists.
|
|
245
|
+
if (/\bengine\s*:\s*\{[\s\S]*?\bvariant\s*:\s*['"][^'"]+['"]/m.test(source)) {
|
|
246
|
+
return source.replace(
|
|
247
|
+
/(\bengine\s*:\s*\{[\s\S]*?\bvariant\s*:\s*['"])([^'"]+)(['"])/m,
|
|
248
|
+
`$1${selectedEngine}$3`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Replace existing scalar engine config
|
|
253
|
+
if (/\bengine\s*:\s*['"][^'"]+['"],?/m.test(source)) {
|
|
254
|
+
return source.replace(/\bengine\s*:\s*['"][^'"]+['"],?/m, `engine: '${selectedEngine}',`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Insert right after "const config = {"
|
|
258
|
+
const engineBlock = ` engine: {\n variant: '${selectedEngine}',\n },`;
|
|
259
|
+
const marker = 'const config = {';
|
|
260
|
+
const markerIndex = source.indexOf(marker);
|
|
261
|
+
if (markerIndex !== -1) {
|
|
262
|
+
const insertAt = markerIndex + marker.length;
|
|
263
|
+
return `${source.slice(0, insertAt)}\n${engineBlock}${source.slice(insertAt)}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Fallback to a minimal config when template structure is unexpected
|
|
267
|
+
return `const config = {\n${engineBlock}\n};\n\nexport default config;\n`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function applyEngineToVistaConfig(projectDir, selectedEngine) {
|
|
271
|
+
const configPath = path.join(projectDir, 'vista.config.ts');
|
|
272
|
+
if (!fs.existsSync(configPath)) return;
|
|
273
|
+
|
|
274
|
+
const source = fs.readFileSync(configPath, 'utf8');
|
|
275
|
+
const patched = injectEngineBlock(source, selectedEngine);
|
|
276
|
+
fs.writeFileSync(configPath, patched);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function applyReadmeSelections(projectDir, selectedEngine, useTypedApi) {
|
|
280
|
+
const readmePath = path.join(projectDir, 'README.md');
|
|
281
|
+
if (!fs.existsSync(readmePath)) return;
|
|
282
|
+
|
|
283
|
+
const source = fs.readFileSync(readmePath, 'utf8');
|
|
284
|
+
const patched = source
|
|
285
|
+
.replace(/__VISTA_ENGINE__/g, selectedEngine)
|
|
286
|
+
.replace(/__VISTA_TYPED_API__/g, useTypedApi ? 'enabled' : 'disabled');
|
|
287
|
+
fs.writeFileSync(readmePath, patched);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function applyFlashpackStarterTheme(projectDir) {
|
|
291
|
+
const flashTemplateDir = path.join(__dirname, 'flash-template');
|
|
292
|
+
if (fs.existsSync(flashTemplateDir)) {
|
|
293
|
+
copyRecursiveSync(flashTemplateDir, projectDir);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
93
297
|
async function main() {
|
|
94
298
|
const useLocal = rawArgs.includes('--local');
|
|
95
299
|
const currentDir = process.cwd();
|
|
96
300
|
const projectName = await resolveProjectName();
|
|
301
|
+
const selectedEngine = await resolveEngineChoice();
|
|
302
|
+
const selectedPackageManager = await resolvePackageManagerChoice();
|
|
97
303
|
const projectDir = path.join(currentDir, projectName);
|
|
98
304
|
|
|
99
|
-
const proceed = await confirmProceed(
|
|
305
|
+
const proceed = await confirmProceed(
|
|
306
|
+
projectName,
|
|
307
|
+
projectDir,
|
|
308
|
+
selectedEngine,
|
|
309
|
+
selectedPackageManager
|
|
310
|
+
);
|
|
100
311
|
if (!proceed) {
|
|
101
312
|
console.log('Aborted.');
|
|
102
313
|
process.exit(0);
|
|
@@ -121,6 +332,12 @@ async function main() {
|
|
|
121
332
|
console.log('Added typed API starter files.');
|
|
122
333
|
}
|
|
123
334
|
|
|
335
|
+
applyEngineToVistaConfig(projectDir, selectedEngine);
|
|
336
|
+
applyReadmeSelections(projectDir, selectedEngine, useTypedApiStarter);
|
|
337
|
+
if (selectedEngine === 'flashpack') {
|
|
338
|
+
applyFlashpackStarterTheme(projectDir);
|
|
339
|
+
}
|
|
340
|
+
|
|
124
341
|
console.log('Scaffolding complete.');
|
|
125
342
|
|
|
126
343
|
// 3. Setup Dependencies (production-ready)
|
|
@@ -143,6 +360,7 @@ async function main() {
|
|
|
143
360
|
'postcss-cli': '^11.0.0',
|
|
144
361
|
tailwindcss: '^4.0.0',
|
|
145
362
|
'@tailwindcss/postcss': '^4.0.0',
|
|
363
|
+
webpack: '^5.90.0',
|
|
146
364
|
// Node 20+ SSR compatibility
|
|
147
365
|
'@swc-node/register': '^1.9.0',
|
|
148
366
|
'@swc/core': '^1.4.0',
|
|
@@ -165,7 +383,7 @@ node_modules/
|
|
|
165
383
|
# Build outputs
|
|
166
384
|
dist/
|
|
167
385
|
.vista/
|
|
168
|
-
.
|
|
386
|
+
.flash/
|
|
169
387
|
out/
|
|
170
388
|
|
|
171
389
|
# Rust artifacts
|
|
@@ -235,9 +453,11 @@ coverage/
|
|
|
235
453
|
}
|
|
236
454
|
|
|
237
455
|
// 6. Install Dependencies
|
|
238
|
-
const installCmd =
|
|
456
|
+
const installCmd = getInstallCommand(selectedPackageManager);
|
|
239
457
|
if (!skipInstall) {
|
|
240
|
-
console.log(
|
|
458
|
+
console.log(
|
|
459
|
+
`\nInstalling dependencies with ${selectedPackageManager}... This may take a moment.\n`
|
|
460
|
+
);
|
|
241
461
|
try {
|
|
242
462
|
execSync(installCmd, { cwd: projectDir, stdio: 'inherit' });
|
|
243
463
|
console.log(`\n✓ Dependencies installed successfully!`);
|
|
@@ -250,18 +470,13 @@ coverage/
|
|
|
250
470
|
console.log('\nSkipped dependency installation (--skip-install).');
|
|
251
471
|
}
|
|
252
472
|
|
|
253
|
-
const runCmd =
|
|
254
|
-
const createCmd =
|
|
255
|
-
pkgManager === 'pnpm'
|
|
256
|
-
? 'pnpm create vista-app'
|
|
257
|
-
: pkgManager === 'yarn'
|
|
258
|
-
? 'yarn create vista-app'
|
|
259
|
-
: pkgManager === 'bun'
|
|
260
|
-
? 'bun create vista-app'
|
|
261
|
-
: 'npx create-vista-app@latest';
|
|
473
|
+
const runCmd = getRunCommand(selectedPackageManager);
|
|
474
|
+
const createCmd = getCreateCommand(selectedPackageManager);
|
|
262
475
|
|
|
263
476
|
console.log(`
|
|
264
477
|
✨ Success! Created ${projectName} at ${projectDir}
|
|
478
|
+
Engine: ${selectedEngine}
|
|
479
|
+
Package manager: ${selectedPackageManager}
|
|
265
480
|
|
|
266
481
|
Get started by running:
|
|
267
482
|
|
|
@@ -275,7 +490,22 @@ Happy Hacking! 🚀
|
|
|
275
490
|
`);
|
|
276
491
|
}
|
|
277
492
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
493
|
+
module.exports = {
|
|
494
|
+
main,
|
|
495
|
+
detectPackageManager,
|
|
496
|
+
normalizePackageManager,
|
|
497
|
+
getInstallCommand,
|
|
498
|
+
getRunCommand,
|
|
499
|
+
getCreateCommand,
|
|
500
|
+
injectEngineBlock,
|
|
501
|
+
applyEngineToVistaConfig,
|
|
502
|
+
applyReadmeSelections,
|
|
503
|
+
applyFlashpackStarterTheme,
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
if (require.main === module) {
|
|
507
|
+
main().catch((error) => {
|
|
508
|
+
console.error('create-vista-app failed:', error);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import Image from 'vista/image';
|
|
2
|
+
|
|
3
|
+
export default function Index() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="relative flex min-h-[100dvh] items-center overflow-hidden bg-black text-zinc-100 selection:bg-primary/20 selection:text-primary">
|
|
6
|
+
<div className="pointer-events-none absolute top-0 right-0 h-[380px] w-[380px] translate-x-1/4 -translate-y-1/4 rounded-full bg-primary opacity-20 blur-[110px]" />
|
|
7
|
+
<div className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-primary/45 to-transparent" />
|
|
8
|
+
|
|
9
|
+
<section className="relative z-10 mx-auto w-full max-w-5xl px-6 py-10 md:px-10 lg:px-12">
|
|
10
|
+
<div className="grid items-center gap-10 lg:grid-cols-[minmax(0,1.05fr)_minmax(320px,0.95fr)]">
|
|
11
|
+
<div className="max-w-2xl">
|
|
12
|
+
<div className="inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-2 text-[11px] font-medium uppercase tracking-[0.28em] text-primary">
|
|
13
|
+
Flashpack starter
|
|
14
|
+
</div>
|
|
15
|
+
<h1 className="mt-6 max-w-2xl text-balance text-[clamp(2.9rem,5.8vw,5rem)] font-semibold leading-[0.94] tracking-tight text-zinc-50">
|
|
16
|
+
Stay in flow while the app keeps moving.
|
|
17
|
+
</h1>
|
|
18
|
+
<p className="mt-5 max-w-xl text-pretty text-base leading-8 text-zinc-400 md:text-lg">
|
|
19
|
+
Flashpack keeps the Vista workflow familiar, but gives you a tighter edit loop, clearer runtime traces,
|
|
20
|
+
and a cleaner place to inspect what happened during dev, build, and start.
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<div className="mt-6 flex flex-wrap items-center gap-3 text-sm text-zinc-300">
|
|
24
|
+
<span className="rounded-full border border-zinc-800 bg-zinc-950/80 px-4 py-2">
|
|
25
|
+
Same <code>vista dev</code> workflow
|
|
26
|
+
</span>
|
|
27
|
+
<span className="rounded-full border border-zinc-800 bg-zinc-950/80 px-4 py-2">
|
|
28
|
+
Traceable output in <code>.flash/</code>
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div className="mt-7 flex flex-wrap items-center gap-3 text-sm text-zinc-400">
|
|
33
|
+
<span>
|
|
34
|
+
Edit <code className="rounded bg-zinc-900 px-2 py-1 text-zinc-200">app/index.tsx</code> to shape this
|
|
35
|
+
screen.
|
|
36
|
+
</span>
|
|
37
|
+
<a
|
|
38
|
+
href="https://vista.xyz/docs/env"
|
|
39
|
+
className="rounded-full border border-zinc-800 bg-zinc-950/70 px-4 py-2 text-zinc-200 transition-colors hover:border-primary/50 hover:text-primary"
|
|
40
|
+
>
|
|
41
|
+
Read the env guide
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<aside className="space-y-4">
|
|
47
|
+
<div className="rounded-[1.9rem] border border-zinc-800/80 bg-zinc-950/60 p-6 shadow-[0_28px_75px_rgba(0,0,0,0.45)] backdrop-blur-sm">
|
|
48
|
+
<div className="mb-6 flex items-center justify-between text-[11px] uppercase tracking-[0.24em] text-zinc-500">
|
|
49
|
+
<span>Vista</span>
|
|
50
|
+
<span>Flashpack</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div className="flex justify-center">
|
|
53
|
+
<Image
|
|
54
|
+
src="/vista.svg"
|
|
55
|
+
alt="Vista Logo"
|
|
56
|
+
width={600}
|
|
57
|
+
height={600}
|
|
58
|
+
priority
|
|
59
|
+
unoptimized
|
|
60
|
+
className="h-auto w-[210px] invert opacity-95 sm:w-[250px] lg:w-[280px]"
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="grid gap-4 sm:grid-cols-2">
|
|
66
|
+
<article className="rounded-[1.6rem] border border-zinc-800/80 bg-zinc-950/45 p-5">
|
|
67
|
+
<p className="text-sm font-medium uppercase tracking-[0.22em] text-primary">Quicker feedback</p>
|
|
68
|
+
<p className="mt-3 text-sm leading-7 text-zinc-400">
|
|
69
|
+
Keep editing without feeling pushed into long restart cycles every time the route tree shifts.
|
|
70
|
+
</p>
|
|
71
|
+
</article>
|
|
72
|
+
<article className="rounded-[1.6rem] border border-zinc-800/80 bg-zinc-950/45 p-5">
|
|
73
|
+
<p className="text-sm font-medium uppercase tracking-[0.22em] text-primary">Readable artifacts</p>
|
|
74
|
+
<p className="mt-3 text-sm leading-7 text-zinc-400">
|
|
75
|
+
Open graph, runtime, and log output when you want to understand how the engine moved.
|
|
76
|
+
</p>
|
|
77
|
+
</article>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div className="rounded-[1.6rem] border border-zinc-800/80 bg-zinc-950/45 p-5">
|
|
81
|
+
<p className="text-sm font-medium uppercase tracking-[0.22em] text-primary">Why teams pick it</p>
|
|
82
|
+
<p className="mt-3 text-sm leading-7 text-zinc-400">
|
|
83
|
+
Flashpack keeps the command surface stable while making build artifacts and dev behavior easier to read.
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
</aside>
|
|
87
|
+
</div>
|
|
88
|
+
</section>
|
|
89
|
+
</main>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Metadata } from 'vista';
|
|
2
|
+
import { Geist, Geist_Mono } from 'vista/font/google';
|
|
3
|
+
import './globals.css';
|
|
4
|
+
|
|
5
|
+
const geistSans = Geist({
|
|
6
|
+
variable: '--font-geist-sans',
|
|
7
|
+
subsets: ['latin'],
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const geistMono = Geist_Mono({
|
|
11
|
+
variable: '--font-geist-mono',
|
|
12
|
+
subsets: ['latin'],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const metadata: Metadata = {
|
|
16
|
+
title: 'My Vista App',
|
|
17
|
+
description: 'Built with Vista Framework',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
21
|
+
return (
|
|
22
|
+
<html lang="en" suppressHydrationWarning>
|
|
23
|
+
<head />
|
|
24
|
+
<body
|
|
25
|
+
className={`${geistSans.variable} ${geistMono.variable} min-h-screen overflow-x-hidden bg-black text-zinc-100 antialiased`}
|
|
26
|
+
suppressHydrationWarning
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
# My Vista App
|
|
2
|
-
|
|
3
|
-
Built with [Vista.js](https://github.com/vistagen/Vista-Js) — the React framework powered by Rust.
|
|
1
|
+
# My Vista App
|
|
2
|
+
|
|
3
|
+
Built with [Vista.js](https://github.com/vistagen/Vista-Js) — the React framework powered by Rust.
|
|
4
|
+
|
|
5
|
+
Selected engine for this app: `__VISTA_ENGINE__`
|
|
6
|
+
|
|
7
|
+
Typed API starter: `__VISTA_TYPED_API__`
|
|
4
8
|
|
|
5
9
|
## Getting Started
|
|
6
10
|
|
|
@@ -41,17 +45,42 @@ vista.config.ts # Framework configuration
|
|
|
41
45
|
- **`'use client'`** — Add this directive to make a component interactive (client-side).
|
|
42
46
|
- **Server Components** — All components are server components by default (zero JS sent to browser).
|
|
43
47
|
|
|
44
|
-
## Available Commands
|
|
48
|
+
## Available Commands
|
|
45
49
|
|
|
46
50
|
| Command | Description |
|
|
47
51
|
| ------------- | --------------------------------- |
|
|
48
|
-
| `vista dev` | Start dev server with
|
|
49
|
-
| `vista build` | Create production build
|
|
50
|
-
| `vista start` | Start production server
|
|
52
|
+
| `vista dev` | Start dev server with the engine selected in `vista.config.ts` |
|
|
53
|
+
| `vista build` | Create production build with the engine selected in `vista.config.ts` |
|
|
54
|
+
| `vista start` | Start production server with the engine selected in `vista.config.ts` |
|
|
51
55
|
| `vista g api-init` | Generate typed API starter files |
|
|
52
56
|
| `vista g router <name>` | Generate a typed router file |
|
|
53
57
|
| `vista g procedure <name> [get\|post]` | Generate a typed procedure file |
|
|
54
58
|
|
|
59
|
+
## Engine Selection
|
|
60
|
+
|
|
61
|
+
`create-vista-app` supports both engine variants:
|
|
62
|
+
|
|
63
|
+
- `default` (webpack path)
|
|
64
|
+
- `flashpack` (Rust-first path)
|
|
65
|
+
|
|
66
|
+
You can choose at scaffold time, but the generated app still uses the same `npm run dev`, `npm run build`, and `npm run start` scripts. Vista reads the selected engine from `vista.config.ts`.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx create-vista-app@latest my-vista-app --engine flashpack
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The generated config looks like:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
engine: {
|
|
78
|
+
variant: 'flashpack'
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Flashpack engine runtime/cache artifacts are stored in `.flash/`.
|
|
83
|
+
|
|
55
84
|
## Typed API Rollback
|
|
56
85
|
|
|
57
86
|
Typed API is experimental and can be disabled anytime from `vista.config.ts`:
|
package/template/app/globals.css
CHANGED
|
@@ -11,14 +11,16 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
html {
|
|
14
|
-
|
|
14
|
+
min-height: 100%;
|
|
15
|
+
overflow-x: hidden;
|
|
16
|
+
scrollbar-gutter: stable;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
:root {
|
|
18
|
-
--background: #
|
|
19
|
-
--foreground: #
|
|
20
|
+
--background: #f3e8d8;
|
|
21
|
+
--foreground: #171717;
|
|
20
22
|
--primary: #ff4c30;
|
|
21
|
-
color-scheme:
|
|
23
|
+
color-scheme: light;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
:root.light {
|
|
@@ -36,7 +38,10 @@ html {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
body {
|
|
41
|
+
min-height: 100vh;
|
|
42
|
+
margin: 0;
|
|
39
43
|
background: var(--background);
|
|
40
44
|
color: var(--foreground);
|
|
41
45
|
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
|
|
46
|
+
overflow-x: hidden;
|
|
42
47
|
}
|
package/template/app/index.tsx
CHANGED
|
@@ -2,23 +2,65 @@ import Image from 'vista/image';
|
|
|
2
2
|
|
|
3
3
|
export default function Index() {
|
|
4
4
|
return (
|
|
5
|
-
<main className="flex min-h-
|
|
6
|
-
<
|
|
7
|
-
<div className="
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
<main className="flex min-h-[100dvh] items-center justify-center bg-[#f7f4ee] px-6 py-10 text-zinc-950 selection:bg-primary/15 selection:text-primary md:px-10">
|
|
6
|
+
<section className="w-full max-w-4xl rounded-[2rem] border border-zinc-900/10 bg-white/85 p-7 shadow-[0_24px_80px_rgba(27,18,7,0.08)] backdrop-blur-sm md:p-10">
|
|
7
|
+
<div className="grid items-center gap-10 lg:grid-cols-[minmax(0,1.05fr)_minmax(300px,0.95fr)]">
|
|
8
|
+
<div className="max-w-xl">
|
|
9
|
+
<div className="inline-flex items-center gap-2 rounded-full border border-zinc-900/10 bg-[#fbf7f1] px-4 py-2 text-[11px] font-medium uppercase tracking-[0.28em] text-primary">
|
|
10
|
+
Vista starter
|
|
11
|
+
</div>
|
|
12
|
+
<h1 className="mt-6 max-w-xl text-balance text-[clamp(2.6rem,5vw,4.6rem)] font-semibold tracking-tight text-zinc-950">
|
|
13
|
+
Start by editing <code className="font-mono text-[0.82em]">app/index.tsx</code>.
|
|
14
|
+
</h1>
|
|
15
|
+
<p className="mt-5 max-w-lg text-pretty text-base leading-8 text-zinc-700 md:text-lg">
|
|
16
|
+
The default starter keeps the first screen calm: one route, a clean app shell, and the core Vista flow
|
|
17
|
+
already wired so you can start building instead of cleaning up scaffolding.
|
|
18
|
+
</p>
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
<div className="mt-7 flex flex-wrap items-center gap-3">
|
|
21
|
+
<a
|
|
22
|
+
href="https://vista.xyz/docs/env"
|
|
23
|
+
className="inline-flex items-center justify-center rounded-full bg-zinc-950 px-5 py-3 text-sm font-medium text-white transition-colors hover:bg-zinc-800"
|
|
24
|
+
>
|
|
25
|
+
Open env guide
|
|
26
|
+
</a>
|
|
27
|
+
<span className="rounded-full border border-zinc-900/10 bg-[#fbf7f1] px-4 py-3 text-sm text-zinc-700">
|
|
28
|
+
Engine lives in <code>vista.config.ts</code>
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<aside className="rounded-[1.7rem] border border-zinc-900/10 bg-[#fcfaf6] p-6">
|
|
34
|
+
<div className="flex items-center justify-between text-[11px] uppercase tracking-[0.24em] text-zinc-500">
|
|
35
|
+
<span>Default engine</span>
|
|
36
|
+
<span>Vista</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div className="mt-6 flex justify-center">
|
|
40
|
+
<Image
|
|
41
|
+
src="/vista.svg"
|
|
42
|
+
alt="Vista Logo"
|
|
43
|
+
width={600}
|
|
44
|
+
height={600}
|
|
45
|
+
priority
|
|
46
|
+
unoptimized
|
|
47
|
+
className="h-auto w-[220px] opacity-95 sm:w-[250px]"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div className="mt-7 space-y-3 text-sm text-zinc-700">
|
|
52
|
+
<div className="rounded-2xl border border-zinc-900/8 bg-white p-4">
|
|
53
|
+
<p className="font-medium text-zinc-950">Stable default path</p>
|
|
54
|
+
<p className="mt-2 leading-7">Use the familiar <code>vista dev</code>, <code>vista build</code>, and <code>vista start</code> flow from day one.</p>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="rounded-2xl border border-zinc-900/8 bg-white p-4">
|
|
57
|
+
<p className="font-medium text-zinc-950">Config-first workflow</p>
|
|
58
|
+
<p className="mt-2 leading-7">Adjust engine and framework behavior in <code>vista.config.ts</code> instead of rewriting scripts.</p>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</aside>
|
|
62
|
+
</div>
|
|
63
|
+
</section>
|
|
64
|
+
</main>
|
|
65
|
+
);
|
|
66
|
+
}
|
package/template/app/root.tsx
CHANGED
|
@@ -19,9 +19,14 @@ export const metadata: Metadata = {
|
|
|
19
19
|
|
|
20
20
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
21
21
|
return (
|
|
22
|
-
<html lang="en">
|
|
22
|
+
<html lang="en" suppressHydrationWarning>
|
|
23
23
|
<head />
|
|
24
|
-
<body
|
|
24
|
+
<body
|
|
25
|
+
className={`${geistSans.variable} ${geistMono.variable} min-h-screen overflow-x-hidden antialiased bg-background text-foreground`}
|
|
26
|
+
suppressHydrationWarning
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</body>
|
|
25
30
|
</html>
|
|
26
31
|
);
|
|
27
32
|
}
|
package/template/vista-env.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// <reference types="vista/image/image-types/global" />
|
|
3
3
|
|
|
4
4
|
// NOTE: This file should not be edited
|
|
5
|
-
// see https://vista.
|
|
5
|
+
// see https://vista.xyz/docs/env for more information.
|
|
6
6
|
|
|
7
7
|
declare module '*.module.css' {
|
|
8
8
|
const classes: { readonly [key: string]: string };
|
package/template/vista.config.ts
CHANGED