create-rari-app 0.1.3

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/index.js ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
+ import { dirname, join } from "node:path";
5
+ import process from "node:process";
6
+ import { cancel, confirm, intro, isCancel, outro, select, spinner, text } from "@clack/prompts";
7
+ import pc from "picocolors";
8
+
9
+ //#region src/index.ts
10
+ const templates = { default: {
11
+ name: "Default",
12
+ description: "A clean starter with React Server Components"
13
+ } };
14
+ const packageManagers = {
15
+ pnpm: "pnpm",
16
+ npm: "npm",
17
+ yarn: "yarn",
18
+ bun: "bun"
19
+ };
20
+ async function main() {
21
+ console.clear();
22
+ intro(pc.bgCyan(pc.black(" create-rari-app ")));
23
+ const projectName = await text({
24
+ message: "What is your project named?",
25
+ placeholder: "my-rari-app",
26
+ validate: (value) => {
27
+ if (!value) return "Please enter a project name.";
28
+ if (value.includes(" ")) return "Project name cannot contain spaces.";
29
+ if (!/^[\w-]+$/.test(value)) return "Project name can only contain letters, numbers, hyphens, and underscores.";
30
+ }
31
+ });
32
+ if (isCancel(projectName)) {
33
+ cancel("Operation cancelled.");
34
+ process.exit(0);
35
+ }
36
+ const template = await select({
37
+ message: "Which template would you like to use?",
38
+ options: Object.entries(templates).map(([key, { name, description }]) => ({
39
+ value: key,
40
+ label: name,
41
+ hint: description
42
+ }))
43
+ });
44
+ if (isCancel(template)) {
45
+ cancel("Operation cancelled.");
46
+ process.exit(0);
47
+ }
48
+ const packageManager = await select({
49
+ message: "Which package manager would you like to use?",
50
+ options: Object.entries(packageManagers).map(([key, value]) => ({
51
+ value: key,
52
+ label: value
53
+ }))
54
+ });
55
+ if (isCancel(packageManager)) {
56
+ cancel("Operation cancelled.");
57
+ process.exit(0);
58
+ }
59
+ const installDeps = await confirm({
60
+ message: "Install dependencies?",
61
+ initialValue: true
62
+ });
63
+ if (isCancel(installDeps)) {
64
+ cancel("Operation cancelled.");
65
+ process.exit(0);
66
+ }
67
+ const options = {
68
+ name: projectName,
69
+ template,
70
+ packageManager,
71
+ installDeps
72
+ };
73
+ await createProject(options);
74
+ outro(pc.green("🎉 Project created successfully!"));
75
+ console.log();
76
+ console.log(pc.cyan("Next steps:"));
77
+ console.log(pc.gray(` cd ${options.name}`));
78
+ if (!options.installDeps) console.log(pc.gray(` ${options.packageManager} install`));
79
+ console.log(pc.gray(` ${options.packageManager} run dev`));
80
+ console.log();
81
+ }
82
+ async function createProject(options) {
83
+ const projectPath = join(process.cwd(), options.name);
84
+ const templatePath = join(import.meta.dirname, "..", "templates", options.template);
85
+ const s = spinner();
86
+ try {
87
+ s.start("Creating project structure...");
88
+ await mkdir(projectPath, { recursive: true });
89
+ await copyTemplate(templatePath, projectPath, options);
90
+ s.stop("Project structure created.");
91
+ if (options.installDeps) {
92
+ s.start("Installing dependencies...");
93
+ await installDependencies(projectPath, options.packageManager);
94
+ s.stop("Dependencies installed.");
95
+ }
96
+ } catch (error) {
97
+ s.stop("Error occurred.");
98
+ throw error;
99
+ }
100
+ }
101
+ async function copyTemplate(templatePath, projectPath, options) {
102
+ const templateFiles = [
103
+ "package.json",
104
+ "vite.config.ts",
105
+ "tsconfig.json",
106
+ "tsconfig.app.json",
107
+ "tsconfig.node.json",
108
+ "index.html",
109
+ "src/main.tsx",
110
+ "src/App.tsx",
111
+ "src/vite-env.d.ts",
112
+ "src/styles/index.css",
113
+ "src/components/Welcome.tsx",
114
+ "src/components/ServerTime.tsx",
115
+ ".gitignore"
116
+ ];
117
+ await mkdir(join(projectPath, "src", "components"), { recursive: true });
118
+ await mkdir(join(projectPath, "src", "styles"), { recursive: true });
119
+ for (const file of templateFiles) {
120
+ const sourcePath = join(templatePath, file);
121
+ const destPath = join(projectPath, file);
122
+ try {
123
+ let content = await readFile(sourcePath, "utf-8");
124
+ content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.name).replace(/\{\{PACKAGE_MANAGER\}\}/g, options.packageManager);
125
+ await mkdir(dirname(destPath), { recursive: true });
126
+ await writeFile(destPath, content);
127
+ } catch (error) {
128
+ console.warn(`Warning: Could not copy ${file}:`, error);
129
+ }
130
+ }
131
+ }
132
+ async function installDependencies(projectPath, packageManager) {
133
+ return new Promise((resolve, reject) => {
134
+ const child = spawn(packageManager, ["install"], {
135
+ cwd: projectPath,
136
+ stdio: "pipe"
137
+ });
138
+ child.on("close", (code) => {
139
+ if (code === 0) resolve();
140
+ else reject(/* @__PURE__ */ new Error(`${packageManager} install failed with code ${code}`));
141
+ });
142
+ child.on("error", reject);
143
+ });
144
+ }
145
+ main().catch((error) => {
146
+ console.error(pc.red("Error:"), error.message);
147
+ process.exit(1);
148
+ });
149
+
150
+ //#endregion
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "create-rari-app",
3
+ "type": "module",
4
+ "version": "0.1.3",
5
+ "description": "Create Runtime Accelerated Rendering Infrastructure (Rari) applications with no build configuration",
6
+ "author": "Ryan Skinner",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/rari-build/rari#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/rari-build/rari.git",
12
+ "directory": "packages/create-rari-app"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/rari-build/rari/issues"
16
+ },
17
+ "keywords": [
18
+ "rari",
19
+ "react",
20
+ "scaffolding",
21
+ "cli",
22
+ "create-app",
23
+ "generator",
24
+ "boilerplate",
25
+ "template",
26
+ "server-components",
27
+ "rust",
28
+ "framework",
29
+ "zero-config",
30
+ "starter",
31
+ "project-template"
32
+ ],
33
+ "bin": {
34
+ "create-rari-app": "dist/index.js"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "templates"
39
+ ],
40
+ "scripts": {
41
+ "build": "pnpm clean && pnpm typecheck && tsdown",
42
+ "dev": "tsdown --watch",
43
+ "clean": "rm -rf dist",
44
+ "typecheck": "tsgo",
45
+ "lint": "oxlint && eslint",
46
+ "lint:fix": "oxlint --fix && eslint --fix"
47
+ },
48
+ "dependencies": {
49
+ "@clack/prompts": "^0.8.2",
50
+ "picocolors": "^1.1.1"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^24.1.0",
54
+ "@typescript/native-preview": "7.0.0-dev.20250611.1",
55
+ "eslint": "^9.32.0",
56
+ "oxlint": "^1.8.0",
57
+ "tsdown": "^0.12.9"
58
+ }
59
+ }
@@ -0,0 +1,122 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ A high-performance React Server Components application powered by [Rari](https://rari.dev).
4
+
5
+ ## 🚀 Getting Started
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ {{INSTALL_COMMAND}}
10
+
11
+ # Start development server
12
+ {{PACKAGE_MANAGER}} run dev
13
+ ```
14
+
15
+ Visit [http://localhost:5173](http://localhost:5173) to see your app.
16
+
17
+ ## 🚀 Deploy to the Cloud
18
+
19
+ This Rari application is pre-configured for cloud deployment.
20
+
21
+ ### 🚂 Railway
22
+
23
+ ### Quick Deploy
24
+
25
+ 1. **Push to GitHub**:
26
+ ```bash
27
+ git add .
28
+ git commit -m "Initial commit"
29
+ git push origin main
30
+ ```
31
+
32
+ 2. **Deploy to Railway**:
33
+ - Go to [railway.app](https://railway.app)
34
+ - Create new project → "Deploy from GitHub repo"
35
+ - Select your repository
36
+ - Click "Deploy Now"
37
+
38
+ 3. **Generate Domain**:
39
+ - In Railway dashboard → Settings → Networking
40
+ - Click "Generate Domain"
41
+ - Your app will be live! 🎉
42
+
43
+ ### Alternative: Setup Railway from CLI
44
+
45
+ ```bash
46
+ # Configure Railway deployment files
47
+ {{PACKAGE_MANAGER}} run deploy:railway
48
+
49
+ # Follow the instructions to deploy
50
+ ```
51
+
52
+ ### 🎨 Render
53
+
54
+ 1. **Push to GitHub**:
55
+ ```bash
56
+ git add .
57
+ git commit -m "Initial commit"
58
+ git push origin main
59
+ ```
60
+
61
+ 2. **Deploy to Render**:
62
+ - Go to [render.com](https://render.com)
63
+ - Create new "Web Service"
64
+ - Connect your GitHub repository
65
+ - Render auto-detects Node.js and uses `render.yaml`
66
+ - Click "Create Web Service"
67
+
68
+ ### Alternative: Setup Render from CLI
69
+
70
+ ```bash
71
+ # Configure Render deployment files
72
+ {{PACKAGE_MANAGER}} run deploy:render
73
+
74
+ # Follow the instructions to deploy
75
+ ```
76
+
77
+ ## 📜 Available Scripts
78
+
79
+ ```bash
80
+ # Development
81
+ {{PACKAGE_MANAGER}} run dev # Start development server
82
+ {{PACKAGE_MANAGER}} run build # Build for production
83
+
84
+ # Production
85
+ {{PACKAGE_MANAGER}} start # Start production server
86
+ {{PACKAGE_MANAGER}} run start:local # Start local production server
87
+
88
+ # Deployment
89
+ {{PACKAGE_MANAGER}} run deploy:railway # Setup Railway deployment
90
+ {{PACKAGE_MANAGER}} run deploy:render # Setup Render deployment
91
+
92
+ # Code Quality
93
+ {{PACKAGE_MANAGER}} run lint # Run linters
94
+ {{PACKAGE_MANAGER}} run typecheck # Run TypeScript checks
95
+ ```
96
+
97
+ ## 🌍 Environment Variables
98
+
99
+ Cloud platforms automatically provide:
100
+ - `PORT` - Server port (platform assigns this)
101
+ - `NODE_ENV=production` - Production mode
102
+
103
+ Optional variables you can set:
104
+ - `RUST_LOG=debug` - Rust logging level
105
+
106
+ ## 🏗️ Architecture
107
+
108
+ - **⚡ Rust Runtime**: Native performance with zero-cost abstractions
109
+ - **🚀 React Server Components**: True server-side rendering
110
+ - **📁 File-based Routing**: Automatic route generation
111
+ - **🎯 Zero Configuration**: Works out of the box
112
+
113
+ ## 📚 Learn More
114
+
115
+ - [Rari Documentation](https://rari.dev)
116
+ - [Railway Documentation](https://docs.railway.app)
117
+ - [Render Documentation](https://render.com/docs)
118
+ - [React Server Components](https://react.dev/reference/react/use-server)
119
+
120
+ ---
121
+
122
+ Built with ❤️ using [Rari](https://rari.dev)
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{PROJECT_NAME}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "private": true,
6
+ "description": "A Runtime Accelerated Rendering Infrastructure (Rari) application",
7
+ "engines": {
8
+ "node": ">=20.0.0"
9
+ },
10
+ "scripts": {
11
+ "build": "{{PACKAGE_MANAGER}} clean && {{PACKAGE_MANAGER}} typecheck && vite build",
12
+ "dev": "NODE_OPTIONS='--max-http-header-size=1048576' vite",
13
+ "start": "rari start",
14
+ "start:local": "rari start",
15
+ "deploy:railway": "rari deploy railway",
16
+ "deploy:render": "rari deploy render",
17
+ "clean": "rm -rf dist",
18
+ "lint": "oxlint && eslint",
19
+ "lint:fix": "oxlint --fix && eslint --fix",
20
+ "typecheck": "tsgo"
21
+ },
22
+ "dependencies": {
23
+ "rari": "latest",
24
+ "react": "^19.1.0",
25
+ "react-dom": "^19.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@tailwindcss/vite": "^4.1.11",
29
+ "@types/node": "^24.1.0",
30
+ "@types/react": "^19.1.8",
31
+ "@types/react-dom": "^19.1.6",
32
+ "@typescript/native-preview": "7.0.0-dev.20250611.1",
33
+ "@vitejs/plugin-react-oxc": "^0.2.3",
34
+ "eslint": "^9.32.0",
35
+ "oxlint": "^1.8.0",
36
+ "rolldown-vite": "^7.0.11",
37
+ "tailwindcss": "^4.1.11"
38
+ }
39
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://railway.app/railway.schema.json",
3
+ "build": {
4
+ "builder": "NIXPACKS"
5
+ },
6
+ "deploy": {
7
+ "startCommand": "npm start",
8
+ "healthcheckPath": "/",
9
+ "healthcheckTimeout": 300,
10
+ "restartPolicyType": "ALWAYS"
11
+ },
12
+ "environments": {
13
+ "production": {
14
+ "variables": {
15
+ "NODE_ENV": "production",
16
+ "RUST_LOG": "info"
17
+ }
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ services:
2
+ - type: web
3
+ name: '{{PROJECT_NAME}}'
4
+ runtime: node
5
+ env: node
6
+ plan: free
7
+ buildCommand: '{{INSTALL_COMMAND}}'
8
+ startCommand: '{{PACKAGE_MANAGER}} start'
9
+ healthCheckPath: /
10
+ envVars:
11
+ - key: NODE_ENV
12
+ value: production
13
+ - key: RUST_LOG
14
+ value: info
@@ -0,0 +1,19 @@
1
+ import { RouterProvider } from 'rari/client'
2
+ import { routes } from './.rari/routes'
3
+
4
+ function App() {
5
+ return (
6
+ <RouterProvider
7
+ routes={routes}
8
+ config={{
9
+ basePath: '',
10
+ useHash: false,
11
+ caseSensitive: false,
12
+ }}
13
+ >
14
+ <div id="app" />
15
+ </RouterProvider>
16
+ )
17
+ }
18
+
19
+ export default App
@@ -0,0 +1,28 @@
1
+ 'use server'
2
+
3
+ // This is a React Server Component
4
+ export default async function ServerTime() {
5
+ // This runs on the server!
6
+ const timestamp = new Date().toISOString()
7
+
8
+ // Simulate some async work
9
+ await new Promise(resolve => setTimeout(resolve, 100))
10
+
11
+ return (
12
+ <div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-xl p-8 shadow-sm border border-green-200">
13
+ <h2 className="text-2xl font-semibold mb-4 text-gray-900">
14
+ ⚡ Server Component
15
+ </h2>
16
+ <p className="text-gray-600 mb-4">
17
+ This component renders on the server with Rari's high-performance Rust runtime.
18
+ </p>
19
+ <div className="bg-white rounded-lg p-4 border">
20
+ <p className="text-sm text-gray-500 mb-1">Server timestamp:</p>
21
+ <p className="font-mono text-lg text-gray-900">{timestamp}</p>
22
+ </div>
23
+ <p className="text-xs text-gray-500 mt-4">
24
+ 💡 This timestamp was generated on the server and won't change on refresh.
25
+ </p>
26
+ </div>
27
+ )
28
+ }
@@ -0,0 +1,39 @@
1
+ export default function Welcome() {
2
+ return (
3
+ <div className="bg-white rounded-xl p-8 shadow-sm">
4
+ <h2 className="text-2xl font-semibold mb-4 text-gray-900">
5
+ 🎉 Welcome to Rari!
6
+ </h2>
7
+ <p className="text-gray-600 mb-4">
8
+ You've successfully created a new Rari application. This is a client component
9
+ that renders on both server and client.
10
+ </p>
11
+ <div className="space-y-2 text-sm text-gray-500">
12
+ <p>
13
+ 🚀
14
+ <strong>High-performance</strong>
15
+ {' '}
16
+ React Server Components
17
+ </p>
18
+ <p>
19
+
20
+ <strong>Optimized</strong>
21
+ {' '}
22
+ Rust runtime
23
+ </p>
24
+ <p>
25
+ 🔥
26
+ <strong>Hot module</strong>
27
+ {' '}
28
+ reloading
29
+ </p>
30
+ <p>
31
+ 📦
32
+ <strong>Zero config</strong>
33
+ {' '}
34
+ setup
35
+ </p>
36
+ </div>
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App'
4
+ import './styles/index.css'
5
+
6
+ import 'virtual:rsc-integration'
7
+ import 'virtual:rsc-client-components'
8
+
9
+ export function RouterErrorBoundary({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode
13
+ }) {
14
+ const [hasError, setHasError] = React.useState(false)
15
+ const [error, setError] = React.useState<Error | null>(null)
16
+
17
+ React.useEffect(() => {
18
+ const handleError = (event: ErrorEvent) => {
19
+ if (
20
+ event.error?.message?.includes('router')
21
+ || event.error?.message?.includes('navigation')
22
+ ) {
23
+ setHasError(true)
24
+ setError(event.error)
25
+ }
26
+ }
27
+
28
+ window.addEventListener('error', handleError)
29
+ return () => window.removeEventListener('error', handleError)
30
+ }, [])
31
+
32
+ if (hasError) {
33
+ return (
34
+ <div className="min-h-screen bg-red-50 flex items-center justify-center p-8">
35
+ <div className="bg-white rounded-xl p-8 shadow-sm border border-red-200 max-w-2xl">
36
+ <h1 className="text-2xl font-bold text-red-900 mb-4">Router Error</h1>
37
+ <p className="text-red-700 mb-4">
38
+ There was an error with the routing system:
39
+ </p>
40
+ <pre className="bg-red-100 p-4 rounded-lg text-sm text-red-800 overflow-auto">
41
+ {error?.message || 'Unknown router error'}
42
+ </pre>
43
+ <button
44
+ type="button"
45
+ onClick={() => {
46
+ setHasError(false)
47
+ setError(null)
48
+ window.location.reload()
49
+ }}
50
+ className="mt-4 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
51
+ >
52
+ Reload Page
53
+ </button>
54
+ </div>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ return <>{children}</>
60
+ }
61
+
62
+ ReactDOM.createRoot(document.getElementById('root')!).render(
63
+ <React.StrictMode>
64
+ <RouterErrorBoundary>
65
+ <App />
66
+ </RouterErrorBoundary>
67
+ </React.StrictMode>,
68
+ )