@zap-js/client 0.0.2 → 0.0.4
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 +310 -24
- package/bin/zap +0 -0
- package/bin/zap-codegen +0 -0
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.js +282 -0
- package/dist/cli/commands/codegen.d.ts +8 -0
- package/dist/cli/commands/codegen.js +95 -0
- package/dist/cli/commands/dev.d.ts +20 -0
- package/dist/cli/commands/dev.js +78 -0
- package/dist/cli/commands/new.d.ts +9 -0
- package/dist/cli/commands/new.js +307 -0
- package/dist/cli/commands/routes-old.d.ts +9 -0
- package/dist/cli/commands/routes-old.js +106 -0
- package/dist/cli/commands/routes.d.ts +11 -0
- package/dist/cli/commands/routes.js +280 -0
- package/dist/cli/commands/serve.d.ts +17 -0
- package/dist/cli/commands/serve.js +386 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/logger.d.ts +84 -0
- package/dist/cli/utils/logger.js +181 -0
- package/dist/cli/utils/port-finder.d.ts +8 -0
- package/dist/cli/utils/port-finder.js +48 -0
- package/dist/dev-server/codegen-runner.d.ts +41 -0
- package/dist/dev-server/codegen-runner.js +172 -0
- package/dist/dev-server/hot-reload.d.ts +72 -0
- package/dist/dev-server/hot-reload.js +280 -0
- package/dist/dev-server/index.d.ts +8 -0
- package/dist/dev-server/index.js +8 -0
- package/dist/dev-server/route-scanner.d.ts +71 -0
- package/dist/dev-server/route-scanner.js +114 -0
- package/dist/dev-server/rust-builder.d.ts +66 -0
- package/dist/dev-server/rust-builder.js +286 -0
- package/dist/dev-server/server.d.ts +147 -0
- package/dist/dev-server/server.js +658 -0
- package/dist/dev-server/vite-proxy.d.ts +56 -0
- package/dist/dev-server/vite-proxy.js +212 -0
- package/dist/dev-server/watcher.d.ts +48 -0
- package/dist/dev-server/watcher.js +127 -0
- package/dist/router/codegen-enhanced.d.ts +5 -0
- package/dist/router/codegen-enhanced.js +275 -0
- package/dist/router/codegen.d.ts +17 -0
- package/dist/router/codegen.js +654 -0
- package/dist/router/index.d.ts +16 -0
- package/dist/router/index.js +19 -0
- package/dist/router/scanner.d.ts +86 -0
- package/dist/router/scanner.js +689 -0
- package/dist/router/ssg.d.ts +115 -0
- package/dist/router/ssg.js +202 -0
- package/dist/router/types.d.ts +124 -0
- package/dist/router/types.js +9 -0
- package/dist/router/watch.d.ts +38 -0
- package/dist/router/watch.js +135 -0
- package/dist/runtime/csrf.d.ts +146 -0
- package/dist/runtime/csrf.js +166 -0
- package/dist/runtime/error-boundary.d.ts +129 -0
- package/dist/runtime/error-boundary.js +287 -0
- package/dist/runtime/hooks.d.ts +83 -0
- package/dist/runtime/hooks.js +96 -0
- package/dist/runtime/index.d.ts +229 -0
- package/dist/runtime/index.js +449 -0
- package/dist/runtime/ipc-client.d.ts +144 -0
- package/dist/runtime/ipc-client.js +621 -0
- package/dist/runtime/logger.d.ts +71 -0
- package/dist/runtime/logger.js +164 -0
- package/dist/runtime/middleware.d.ts +66 -0
- package/dist/runtime/middleware.js +114 -0
- package/dist/runtime/process-manager.d.ts +51 -0
- package/dist/runtime/process-manager.js +207 -0
- package/dist/runtime/router-simple.d.ts +98 -0
- package/dist/runtime/router-simple.js +330 -0
- package/dist/runtime/router.d.ts +103 -0
- package/dist/runtime/router.js +435 -0
- package/dist/runtime/rpc-client.d.ts +35 -0
- package/dist/runtime/rpc-client.js +140 -0
- package/dist/runtime/streaming-utils.d.ts +86 -0
- package/dist/runtime/streaming-utils.js +150 -0
- package/dist/runtime/types.d.ts +465 -0
- package/dist/runtime/types.js +60 -0
- package/dist/runtime/websockets-utils.d.ts +50 -0
- package/dist/runtime/websockets-utils.js +92 -0
- package/package.json +30 -20
- package/index.js +0 -29
- package/internal/cli/package.json +0 -46
- package/internal/cli/tsconfig.tsbuildinfo +0 -1
- package/internal/dev-server/node_modules/ora/index.d.ts +0 -332
- package/internal/dev-server/node_modules/ora/index.js +0 -416
- package/internal/dev-server/node_modules/ora/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.d.ts +0 -36
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.js +0 -65
- package/internal/dev-server/node_modules/ora/node_modules/string-width/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/README.md +0 -107
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.d.ts +0 -3
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.js +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.mjs +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/package.json +0 -46
- package/internal/dev-server/node_modules/ora/node_modules/string-width/package.json +0 -60
- package/internal/dev-server/node_modules/ora/node_modules/string-width/readme.md +0 -62
- package/internal/dev-server/node_modules/ora/package.json +0 -66
- package/internal/dev-server/node_modules/ora/readme.md +0 -325
- package/internal/dev-server/package.json +0 -41
- package/internal/router/package.json +0 -28
- package/internal/runtime/package.json +0 -41
- package/internal/runtime/src/error-boundary.tsx +0 -476
- package/internal/runtime/src/router-simple.tsx +0 -640
- package/internal/runtime/src/router.tsx +0 -771
- package/internal/runtime/tsconfig.tsbuildinfo +0 -1
- package/src/errors.js +0 -33
- package/src/logger.js +0 -10
- package/src/middleware.js +0 -32
- package/src/router.js +0 -41
- package/src/types.js +0 -39
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { cliLogger } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Find the zap-codegen binary in known locations
|
|
7
|
+
*/
|
|
8
|
+
function findCodegenBinary() {
|
|
9
|
+
const projectDir = process.cwd();
|
|
10
|
+
// Check multiple possible locations
|
|
11
|
+
const possiblePaths = [
|
|
12
|
+
// Project bin directory
|
|
13
|
+
join(projectDir, 'bin', 'zap-codegen'),
|
|
14
|
+
join(projectDir, 'bin', 'zap-codegen.exe'),
|
|
15
|
+
// Workspace target (for monorepo development)
|
|
16
|
+
join(projectDir, '../../target/release/zap-codegen'),
|
|
17
|
+
join(projectDir, '../../target/aarch64-apple-darwin/release/zap-codegen'),
|
|
18
|
+
join(projectDir, '../../target/x86_64-unknown-linux-gnu/release/zap-codegen'),
|
|
19
|
+
join(projectDir, '../../target/debug/zap-codegen'),
|
|
20
|
+
// Local target directory
|
|
21
|
+
join(projectDir, 'target/release/zap-codegen'),
|
|
22
|
+
join(projectDir, 'target/debug/zap-codegen'),
|
|
23
|
+
];
|
|
24
|
+
for (const path of possiblePaths) {
|
|
25
|
+
if (existsSync(path)) {
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Try global zap-codegen as fallback
|
|
30
|
+
try {
|
|
31
|
+
execSync('which zap-codegen', { stdio: 'pipe' });
|
|
32
|
+
return 'zap-codegen';
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Not in PATH
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate TypeScript bindings from Rust exports
|
|
41
|
+
*/
|
|
42
|
+
export async function codegenCommand(options) {
|
|
43
|
+
const outputDir = options.output || './src/api';
|
|
44
|
+
try {
|
|
45
|
+
cliLogger.header('Generating TypeScript Bindings');
|
|
46
|
+
// Find the codegen binary
|
|
47
|
+
const codegenBinary = findCodegenBinary();
|
|
48
|
+
if (!codegenBinary) {
|
|
49
|
+
cliLogger.error('zap-codegen binary not found');
|
|
50
|
+
cliLogger.newline();
|
|
51
|
+
cliLogger.info('Please build the codegen binary first:');
|
|
52
|
+
cliLogger.command('cargo build --release --bin zap-codegen');
|
|
53
|
+
cliLogger.newline();
|
|
54
|
+
cliLogger.info('Or install globally:');
|
|
55
|
+
cliLogger.command('npm install -g @zapjs/codegen');
|
|
56
|
+
cliLogger.newline();
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
cliLogger.spinner('codegen', `Generating bindings to ${outputDir}...`);
|
|
60
|
+
try {
|
|
61
|
+
let cmd = `"${codegenBinary}"`;
|
|
62
|
+
if (options.output) {
|
|
63
|
+
cmd += ` --output-dir ${options.output}`;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
cmd += ` --output-dir ${outputDir}`;
|
|
67
|
+
}
|
|
68
|
+
if (options.input) {
|
|
69
|
+
cmd += ` --input ${options.input}`;
|
|
70
|
+
}
|
|
71
|
+
execSync(cmd, {
|
|
72
|
+
stdio: 'pipe',
|
|
73
|
+
});
|
|
74
|
+
cliLogger.succeedSpinner('codegen', 'TypeScript bindings generated');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
cliLogger.failSpinner('codegen', 'Codegen failed');
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
cliLogger.error('Error details', error.message);
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
cliLogger.newline();
|
|
84
|
+
cliLogger.success('Codegen complete!');
|
|
85
|
+
cliLogger.keyValue('Generated files in', outputDir);
|
|
86
|
+
cliLogger.newline();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
cliLogger.error('Codegen failed');
|
|
90
|
+
if (error instanceof Error) {
|
|
91
|
+
cliLogger.error('Error details', error.message);
|
|
92
|
+
}
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DevOptions {
|
|
2
|
+
port?: string;
|
|
3
|
+
vitePort?: string;
|
|
4
|
+
open?: boolean;
|
|
5
|
+
logLevel?: string;
|
|
6
|
+
release?: boolean;
|
|
7
|
+
skipBuild?: boolean;
|
|
8
|
+
binaryPath?: string;
|
|
9
|
+
codegenBinaryPath?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start development server with hot reload
|
|
13
|
+
*
|
|
14
|
+
* Orchestrates:
|
|
15
|
+
* - Rust backend compilation with file watching
|
|
16
|
+
* - Vite frontend dev server
|
|
17
|
+
* - Automatic TypeScript binding generation
|
|
18
|
+
* - Hot reload signaling
|
|
19
|
+
*/
|
|
20
|
+
export declare function devCommand(options: DevOptions): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { DevServer } from '../../dev-server/index.js';
|
|
4
|
+
import { cliLogger } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Auto-detect pre-built binaries in bin/ directory
|
|
7
|
+
*/
|
|
8
|
+
function detectBinaries(projectDir) {
|
|
9
|
+
const binDir = path.join(projectDir, 'bin');
|
|
10
|
+
const result = {};
|
|
11
|
+
// Check for zap binary
|
|
12
|
+
const zapBinary = path.join(binDir, 'zap');
|
|
13
|
+
const zapBinaryExe = path.join(binDir, 'zap.exe');
|
|
14
|
+
if (existsSync(zapBinary)) {
|
|
15
|
+
result.binaryPath = zapBinary;
|
|
16
|
+
}
|
|
17
|
+
else if (existsSync(zapBinaryExe)) {
|
|
18
|
+
result.binaryPath = zapBinaryExe;
|
|
19
|
+
}
|
|
20
|
+
// Check for zap-codegen binary
|
|
21
|
+
const codegenBinary = path.join(binDir, 'zap-codegen');
|
|
22
|
+
const codegenBinaryExe = path.join(binDir, 'zap-codegen.exe');
|
|
23
|
+
if (existsSync(codegenBinary)) {
|
|
24
|
+
result.codegenBinaryPath = codegenBinary;
|
|
25
|
+
}
|
|
26
|
+
else if (existsSync(codegenBinaryExe)) {
|
|
27
|
+
result.codegenBinaryPath = codegenBinaryExe;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Start development server with hot reload
|
|
33
|
+
*
|
|
34
|
+
* Orchestrates:
|
|
35
|
+
* - Rust backend compilation with file watching
|
|
36
|
+
* - Vite frontend dev server
|
|
37
|
+
* - Automatic TypeScript binding generation
|
|
38
|
+
* - Hot reload signaling
|
|
39
|
+
*/
|
|
40
|
+
export async function devCommand(options) {
|
|
41
|
+
const projectDir = process.cwd();
|
|
42
|
+
// Auto-detect pre-built binaries if not explicitly provided
|
|
43
|
+
const detectedBinaries = detectBinaries(projectDir);
|
|
44
|
+
const config = {
|
|
45
|
+
projectDir,
|
|
46
|
+
rustPort: options.port ? parseInt(options.port, 10) : 3000,
|
|
47
|
+
vitePort: options.vitePort ? parseInt(options.vitePort, 10) : 5173,
|
|
48
|
+
logLevel: options.logLevel || 'info',
|
|
49
|
+
release: options.release || false,
|
|
50
|
+
skipInitialBuild: options.skipBuild || false,
|
|
51
|
+
openBrowser: options.open !== false,
|
|
52
|
+
binaryPath: options.binaryPath || detectedBinaries.binaryPath,
|
|
53
|
+
codegenBinaryPath: options.codegenBinaryPath || detectedBinaries.codegenBinaryPath,
|
|
54
|
+
};
|
|
55
|
+
// Log if using pre-built binaries
|
|
56
|
+
if (config.binaryPath) {
|
|
57
|
+
cliLogger.info('Using pre-built binary', config.binaryPath);
|
|
58
|
+
}
|
|
59
|
+
const server = new DevServer(config);
|
|
60
|
+
// Handle graceful shutdown
|
|
61
|
+
const shutdown = async () => {
|
|
62
|
+
await server.stop();
|
|
63
|
+
process.exit(0);
|
|
64
|
+
};
|
|
65
|
+
process.on('SIGINT', shutdown);
|
|
66
|
+
process.on('SIGTERM', shutdown);
|
|
67
|
+
try {
|
|
68
|
+
await server.start();
|
|
69
|
+
// Keep the process running
|
|
70
|
+
await new Promise(() => { });
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
cliLogger.error('Development server failed', error);
|
|
75
|
+
}
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import fsExtra from 'fs-extra';
|
|
4
|
+
import { join, resolve } from 'path';
|
|
5
|
+
import { cliLogger } from '../utils/logger.js';
|
|
6
|
+
const { ensureDirSync, writeFileSync } = fsExtra;
|
|
7
|
+
/**
|
|
8
|
+
* Create a new ZapJS project
|
|
9
|
+
*/
|
|
10
|
+
export async function newCommand(name, options) {
|
|
11
|
+
if (!name || name.trim() === '') {
|
|
12
|
+
cliLogger.error('Project name is required');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const projectDir = resolve(process.cwd(), name);
|
|
16
|
+
try {
|
|
17
|
+
// Check if directory already exists
|
|
18
|
+
try {
|
|
19
|
+
const fs = await import('fs');
|
|
20
|
+
if (fs.existsSync(projectDir)) {
|
|
21
|
+
cliLogger.error(`Directory "${name}" already exists`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
// Directory doesn't exist, which is good
|
|
27
|
+
}
|
|
28
|
+
cliLogger.header(`Create New ZapJS Project: ${name}`);
|
|
29
|
+
// Prompt for template if not specified
|
|
30
|
+
let template = options.template;
|
|
31
|
+
const templates = ['basic', 'fullstack'];
|
|
32
|
+
if (!templates.includes(template)) {
|
|
33
|
+
const answers = await inquirer.prompt([
|
|
34
|
+
{
|
|
35
|
+
type: 'list',
|
|
36
|
+
name: 'template',
|
|
37
|
+
message: 'Choose a template:',
|
|
38
|
+
choices: templates.map((t) => ({
|
|
39
|
+
name: t.charAt(0).toUpperCase() + t.slice(1),
|
|
40
|
+
value: t,
|
|
41
|
+
})),
|
|
42
|
+
default: 'basic',
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
template = answers.template;
|
|
46
|
+
}
|
|
47
|
+
// Create project directory
|
|
48
|
+
cliLogger.spinner('dir', 'Creating project directory...');
|
|
49
|
+
ensureDirSync(projectDir);
|
|
50
|
+
cliLogger.succeedSpinner('dir', 'Project directory created');
|
|
51
|
+
// Create project files
|
|
52
|
+
cliLogger.spinner('files', `Creating ${template} project...`);
|
|
53
|
+
createMinimalProject(projectDir, name, template);
|
|
54
|
+
cliLogger.succeedSpinner('files', 'Project files created');
|
|
55
|
+
// Install dependencies
|
|
56
|
+
if (options.install !== false) {
|
|
57
|
+
cliLogger.spinner('deps', 'Installing dependencies...');
|
|
58
|
+
try {
|
|
59
|
+
execSync('npm install', { cwd: projectDir, stdio: 'pipe' });
|
|
60
|
+
cliLogger.succeedSpinner('deps', 'Dependencies installed');
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
cliLogger.warn('npm install skipped. Run "npm install" manually.');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Initialize git
|
|
67
|
+
if (options.git !== false) {
|
|
68
|
+
cliLogger.spinner('git', 'Initializing git repository...');
|
|
69
|
+
try {
|
|
70
|
+
execSync('git init', { cwd: projectDir, stdio: 'pipe' });
|
|
71
|
+
execSync('git add .', { cwd: projectDir, stdio: 'pipe' });
|
|
72
|
+
execSync('git commit -m "Initial commit"', {
|
|
73
|
+
cwd: projectDir,
|
|
74
|
+
stdio: 'pipe',
|
|
75
|
+
});
|
|
76
|
+
cliLogger.succeedSpinner('git', 'Git repository initialized');
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
cliLogger.warn('Git initialization skipped');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Success message
|
|
83
|
+
cliLogger.newline();
|
|
84
|
+
cliLogger.success(`Project ${name} created successfully!`);
|
|
85
|
+
cliLogger.newline();
|
|
86
|
+
// Next steps
|
|
87
|
+
cliLogger.info('Next steps:');
|
|
88
|
+
cliLogger.command(`cd ${name}`);
|
|
89
|
+
cliLogger.command('cargo build --release');
|
|
90
|
+
cliLogger.command('zap dev');
|
|
91
|
+
cliLogger.newline();
|
|
92
|
+
cliLogger.info('Happy coding! 🚀');
|
|
93
|
+
cliLogger.newline();
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
cliLogger.error('Project creation failed');
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
cliLogger.error('Error details', error.message);
|
|
99
|
+
}
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create a minimal project structure
|
|
105
|
+
*/
|
|
106
|
+
function createMinimalProject(projectDir, projectName, template) {
|
|
107
|
+
// Create directory structure
|
|
108
|
+
const dirs = [
|
|
109
|
+
'server/src',
|
|
110
|
+
'routes',
|
|
111
|
+
'routes/api',
|
|
112
|
+
'src',
|
|
113
|
+
'src/generated',
|
|
114
|
+
];
|
|
115
|
+
for (const dir of dirs) {
|
|
116
|
+
ensureDirSync(join(projectDir, dir));
|
|
117
|
+
}
|
|
118
|
+
// Create server/src/main.rs
|
|
119
|
+
const mainRs = `use zap::Zap;
|
|
120
|
+
|
|
121
|
+
#[tokio::main]
|
|
122
|
+
async fn main() {
|
|
123
|
+
let mut app = Zap::new()
|
|
124
|
+
.port(3000)
|
|
125
|
+
.hostname("127.0.0.1")
|
|
126
|
+
.cors()
|
|
127
|
+
.logging();
|
|
128
|
+
|
|
129
|
+
// Register your routes here
|
|
130
|
+
app.get("/api/health", || {
|
|
131
|
+
serde_json::json!({ "status": "ok" })
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if let Err(e) = app.listen().await {
|
|
135
|
+
eprintln!("Server error: {}", e);
|
|
136
|
+
std::process::exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
// Create routes/__root.tsx
|
|
141
|
+
const rootTsx = `import React from 'react';
|
|
142
|
+
|
|
143
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
144
|
+
return (
|
|
145
|
+
<html lang="en">
|
|
146
|
+
<head>
|
|
147
|
+
<meta charSet="UTF-8" />
|
|
148
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
149
|
+
<title>ZapJS App</title>
|
|
150
|
+
</head>
|
|
151
|
+
<body>
|
|
152
|
+
{children}
|
|
153
|
+
</body>
|
|
154
|
+
</html>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
// Create routes/index.tsx
|
|
159
|
+
const indexRoute = `export default function HomePage() {
|
|
160
|
+
return (
|
|
161
|
+
<div style={{ fontFamily: 'system-ui, sans-serif', padding: '2rem' }}>
|
|
162
|
+
<h1>Welcome to ZapJS</h1>
|
|
163
|
+
<p>Fullstack Rust + React Framework</p>
|
|
164
|
+
<p>
|
|
165
|
+
Edit <code>routes/index.tsx</code> to get started.
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
// Create routes/api/hello.ts
|
|
172
|
+
const helloApi = `import { server } from '../../src/generated/server';
|
|
173
|
+
|
|
174
|
+
export const GET = async () => {
|
|
175
|
+
return {
|
|
176
|
+
message: 'Hello from ZapJS!',
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const POST = async ({ request }: { request: Request }) => {
|
|
182
|
+
const body = await request.json();
|
|
183
|
+
return {
|
|
184
|
+
received: body,
|
|
185
|
+
message: 'Data received successfully',
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
`;
|
|
189
|
+
// Create routes/api/users.$id.ts
|
|
190
|
+
const usersApi = `export const GET = async ({ params }: { params: { id: string } }) => {
|
|
191
|
+
return {
|
|
192
|
+
id: params.id,
|
|
193
|
+
name: \`User \${params.id}\`,
|
|
194
|
+
email: \`user\${params.id}@example.com\`,
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
`;
|
|
198
|
+
// Create package.json
|
|
199
|
+
const packageJson = {
|
|
200
|
+
name: projectName,
|
|
201
|
+
version: '0.1.0',
|
|
202
|
+
type: 'module',
|
|
203
|
+
scripts: {
|
|
204
|
+
'dev': 'zap dev',
|
|
205
|
+
'build': 'zap build',
|
|
206
|
+
'serve': 'zap serve',
|
|
207
|
+
'routes': 'zap routes',
|
|
208
|
+
},
|
|
209
|
+
dependencies: {
|
|
210
|
+
'react': '^18.0.0',
|
|
211
|
+
'react-dom': '^18.0.0',
|
|
212
|
+
'@zapjs/runtime': '^0.1.0',
|
|
213
|
+
'@zapjs/router': '^0.1.0',
|
|
214
|
+
},
|
|
215
|
+
devDependencies: {
|
|
216
|
+
'@types/react': '^18.0.0',
|
|
217
|
+
'@types/react-dom': '^18.0.0',
|
|
218
|
+
'@zapjs/cli': '^0.1.0',
|
|
219
|
+
'typescript': '^5.0.0',
|
|
220
|
+
'vite': '^5.0.0',
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
// Create Cargo.toml
|
|
224
|
+
const cargoToml = `[package]
|
|
225
|
+
name = "${projectName}"
|
|
226
|
+
version = "0.1.0"
|
|
227
|
+
edition = "2021"
|
|
228
|
+
|
|
229
|
+
[dependencies]
|
|
230
|
+
zap = { path = "../../packages/server" }
|
|
231
|
+
tokio = { version = "1.0", features = ["full"] }
|
|
232
|
+
serde_json = "1.0"
|
|
233
|
+
`;
|
|
234
|
+
// Create zap.config.ts
|
|
235
|
+
const zapConfig = `import { defineConfig } from 'zap';
|
|
236
|
+
|
|
237
|
+
export default defineConfig({
|
|
238
|
+
server: {
|
|
239
|
+
port: 3000,
|
|
240
|
+
hostname: '127.0.0.1',
|
|
241
|
+
},
|
|
242
|
+
dev: {
|
|
243
|
+
apiPort: 3000,
|
|
244
|
+
clientPort: 5173,
|
|
245
|
+
watchRust: true,
|
|
246
|
+
watchTypeScript: true,
|
|
247
|
+
open: true,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
`;
|
|
251
|
+
// Create tsconfig.json
|
|
252
|
+
const tsconfig = {
|
|
253
|
+
compilerOptions: {
|
|
254
|
+
target: 'ES2020',
|
|
255
|
+
useDefineForClassFields: true,
|
|
256
|
+
lib: ['ES2020', 'DOM', 'DOM.Iterable'],
|
|
257
|
+
module: 'ESNext',
|
|
258
|
+
skipLibCheck: true,
|
|
259
|
+
esModuleInterop: true,
|
|
260
|
+
allowSyntheticDefaultImports: true,
|
|
261
|
+
strict: true,
|
|
262
|
+
noEmit: true,
|
|
263
|
+
jsx: 'react-jsx',
|
|
264
|
+
moduleResolution: 'bundler',
|
|
265
|
+
allowImportingTsExtensions: true,
|
|
266
|
+
},
|
|
267
|
+
include: ['client/src'],
|
|
268
|
+
references: [{ path: './tsconfig.node.json' }],
|
|
269
|
+
};
|
|
270
|
+
// Write files
|
|
271
|
+
writeFileSync(join(projectDir, 'server/src/main.rs'), mainRs);
|
|
272
|
+
writeFileSync(join(projectDir, 'routes/__root.tsx'), rootTsx);
|
|
273
|
+
writeFileSync(join(projectDir, 'routes/index.tsx'), indexRoute);
|
|
274
|
+
writeFileSync(join(projectDir, 'routes/api/hello.ts'), helloApi);
|
|
275
|
+
writeFileSync(join(projectDir, 'routes/api/users.$id.ts'), usersApi);
|
|
276
|
+
writeFileSync(join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
277
|
+
writeFileSync(join(projectDir, 'Cargo.toml'), cargoToml);
|
|
278
|
+
writeFileSync(join(projectDir, 'zap.config.ts'), zapConfig);
|
|
279
|
+
writeFileSync(join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
280
|
+
// Create .gitignore
|
|
281
|
+
const gitignore = `# Dependencies
|
|
282
|
+
node_modules/
|
|
283
|
+
package-lock.json
|
|
284
|
+
|
|
285
|
+
# Build output
|
|
286
|
+
/dist
|
|
287
|
+
/target
|
|
288
|
+
/server/target
|
|
289
|
+
|
|
290
|
+
# Rust
|
|
291
|
+
Cargo.lock
|
|
292
|
+
|
|
293
|
+
# IDE
|
|
294
|
+
.vscode/
|
|
295
|
+
.idea/
|
|
296
|
+
*.swp
|
|
297
|
+
*.swo
|
|
298
|
+
|
|
299
|
+
# Environment
|
|
300
|
+
.env
|
|
301
|
+
.env.local
|
|
302
|
+
|
|
303
|
+
# Logs
|
|
304
|
+
*.log
|
|
305
|
+
`;
|
|
306
|
+
writeFileSync(join(projectDir, '.gitignore'), gitignore);
|
|
307
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
4
|
+
import { join, resolve } from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Scan routes and generate route tree
|
|
7
|
+
*/
|
|
8
|
+
export async function routesCommand(options) {
|
|
9
|
+
const spinner = ora();
|
|
10
|
+
try {
|
|
11
|
+
const projectDir = process.cwd();
|
|
12
|
+
const routesDir = resolve(options.routesDir || join(projectDir, 'routes'));
|
|
13
|
+
const outputDir = resolve(options.output || join(projectDir, 'src', 'generated'));
|
|
14
|
+
console.log(chalk.cyan('\n⚡ ZapJS Route Scanner\n'));
|
|
15
|
+
// Check if routes directory exists
|
|
16
|
+
if (!existsSync(routesDir)) {
|
|
17
|
+
console.log(chalk.yellow('No routes directory found.'));
|
|
18
|
+
console.log(chalk.gray(`Expected: ${routesDir}`));
|
|
19
|
+
console.log(chalk.gray('\nCreate a routes/ directory with your route files to get started.'));
|
|
20
|
+
console.log(chalk.gray('\nTanStack-style conventions:'));
|
|
21
|
+
console.log(chalk.gray(' routes/index.tsx → /'));
|
|
22
|
+
console.log(chalk.gray(' routes/about.tsx → /about'));
|
|
23
|
+
console.log(chalk.gray(' routes/$postId.tsx → /:postId'));
|
|
24
|
+
console.log(chalk.gray(' routes/posts.$id.tsx → /posts/:id'));
|
|
25
|
+
console.log(chalk.gray(' routes/api/users.ts → /api/users'));
|
|
26
|
+
console.log(chalk.gray(' routes/_layout.tsx → Layout wrapper'));
|
|
27
|
+
console.log(chalk.gray(' routes/__root.tsx → Root layout\n'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Try to load the router package
|
|
31
|
+
spinner.start('Loading route scanner...');
|
|
32
|
+
let router;
|
|
33
|
+
try {
|
|
34
|
+
// Dynamic import using variable to prevent TypeScript from resolving
|
|
35
|
+
const moduleName = '@zapjs/router';
|
|
36
|
+
router = await (Function('moduleName', 'return import(moduleName)')(moduleName));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
spinner.fail('@zapjs/router not found');
|
|
40
|
+
console.log(chalk.yellow('\nInstall the router package:'));
|
|
41
|
+
console.log(chalk.cyan(' npm install @zapjs/router\n'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
spinner.succeed('Route scanner loaded');
|
|
45
|
+
// Scan routes
|
|
46
|
+
spinner.start(`Scanning ${routesDir}...`);
|
|
47
|
+
const tree = router.scanRoutes(routesDir);
|
|
48
|
+
spinner.succeed('Routes scanned');
|
|
49
|
+
// Output JSON if requested
|
|
50
|
+
if (options.json) {
|
|
51
|
+
console.log(JSON.stringify(tree, null, 2));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Print route summary
|
|
55
|
+
console.log(chalk.white('\n Page Routes:'));
|
|
56
|
+
if (tree.routes.length === 0) {
|
|
57
|
+
console.log(chalk.gray(' (none)'));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
for (const route of tree.routes) {
|
|
61
|
+
const params = route.params.length > 0
|
|
62
|
+
? chalk.gray(` [${route.params.map(p => p.name).join(', ')}]`)
|
|
63
|
+
: '';
|
|
64
|
+
const index = route.isIndex ? chalk.gray(' (index)') : '';
|
|
65
|
+
console.log(chalk.cyan(` ${route.urlPath}`) + params + index);
|
|
66
|
+
console.log(chalk.gray(` → ${route.relativePath}`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
console.log(chalk.white('\n API Routes:'));
|
|
70
|
+
if (tree.apiRoutes.length === 0) {
|
|
71
|
+
console.log(chalk.gray(' (none)'));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
for (const route of tree.apiRoutes) {
|
|
75
|
+
const params = route.params.length > 0
|
|
76
|
+
? chalk.gray(` [${route.params.map(p => p.name).join(', ')}]`)
|
|
77
|
+
: '';
|
|
78
|
+
const methods = route.methods
|
|
79
|
+
? chalk.gray(` (${route.methods.join(', ')})`)
|
|
80
|
+
: '';
|
|
81
|
+
console.log(chalk.green(` ${route.urlPath}`) + params + methods);
|
|
82
|
+
console.log(chalk.gray(` → ${route.relativePath}`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Generate route tree files
|
|
86
|
+
spinner.start('Generating route tree...');
|
|
87
|
+
if (!existsSync(outputDir)) {
|
|
88
|
+
mkdirSync(outputDir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
router.generateRouteTree({
|
|
91
|
+
outputDir,
|
|
92
|
+
routeTree: tree,
|
|
93
|
+
});
|
|
94
|
+
spinner.succeed('Route tree generated');
|
|
95
|
+
// Summary
|
|
96
|
+
console.log(chalk.green(`\n✓ Found ${tree.routes.length} page routes, ${tree.apiRoutes.length} API routes`));
|
|
97
|
+
console.log(chalk.gray(` Output: ${outputDir}\n`));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
spinner.fail('Route scanning failed');
|
|
101
|
+
if (error instanceof Error) {
|
|
102
|
+
console.error(chalk.red(`\nError: ${error.message}\n`));
|
|
103
|
+
}
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface RoutesOptions {
|
|
2
|
+
routesDir?: string;
|
|
3
|
+
output?: string;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
showCode?: boolean;
|
|
6
|
+
verbose?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Enhanced route scanner that shows handler logic
|
|
10
|
+
*/
|
|
11
|
+
export declare function routesCommand(options: RoutesOptions): Promise<void>;
|