create-vulajs-app 1.1.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/bin/index.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const projectName = process.argv[2];
6
+
7
+ if (!projectName) {
8
+ console.error('\x1b[31mPlease specify the project directory:\x1b[0m');
9
+ console.error(' npx create-vulajs-app <project-directory>');
10
+ process.exit(1);
11
+ }
12
+
13
+ const targetDir = path.join(process.cwd(), projectName);
14
+
15
+ if (fs.existsSync(targetDir)) {
16
+ console.error(`\x1b[31mDirectory ${projectName} already exists.\x1b[0m`);
17
+ process.exit(1);
18
+ }
19
+
20
+ const templateDir = path.join(__dirname, '../template');
21
+
22
+ console.log(`\x1b[36mCreating a new Vula.js app in ${targetDir}...\x1b[0m`);
23
+
24
+ // Copy template files
25
+ fs.cpSync(templateDir, targetDir, { recursive: true });
26
+
27
+ // Update package.json name
28
+ const packageJsonPath = path.join(targetDir, 'package.json');
29
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
30
+ packageJson.name = projectName;
31
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
32
+
33
+ console.log('\x1b[32mSuccess! Created', projectName, '\x1b[0m');
34
+ console.log('Inside that directory, you can run several commands:\n');
35
+ console.log(' \x1b[36mnpm install\x1b[0m');
36
+ console.log(' Installs dependencies.\n');
37
+ console.log(' \x1b[36mnpm run dev\x1b[0m');
38
+ console.log(' Starts the development server.\n');
39
+ console.log('We suggest that you begin by typing:\n');
40
+ console.log(` \x1b[36mcd ${projectName}\x1b[0m`);
41
+ console.log(' \x1b[36mnpm install\x1b[0m');
42
+ console.log(' \x1b[36mnpm run dev\x1b[0m');
package/package.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "create-vulajs-app",
3
+ "version": "1.1.0",
4
+ "description": "Create a new Vula.js application",
5
+ "bin": {
6
+ "create-vulajs-app": "./bin/index.js"
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Server component: counter
3
+ * Runs on the server only. Called from the client via Vula.server().
4
+ */
5
+ export default async function counter(currentValue: number) {
6
+ return currentValue + 1;
7
+ }
@@ -0,0 +1,3 @@
1
+ export async function GET(req: any, res: any) {
2
+ res.json({ message: "Hello from Vula.js API!" });
3
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+
3
+ export default function Home() {
4
+ return (
5
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', padding: '2rem', backgroundColor: '#0f172a', color: '#f8fafc', fontFamily: 'system-ui, sans-serif' }}>
6
+
7
+ {/* Vula Logo / Hero */}
8
+ <div style={{ marginBottom: '2rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
9
+ <div style={{ width: '100px', height: '100px', borderRadius: '24px', background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: '1.5rem', boxShadow: '0 10px 25px -5px rgba(139, 92, 246, 0.5)' }}>
10
+ <span style={{ fontSize: '3rem', fontWeight: 'bold', color: 'white' }}>V</span>
11
+ </div>
12
+ <h1 style={{ fontSize: '3.5rem', fontWeight: '800', margin: '0', background: '-webkit-linear-gradient(0deg, #60a5fa, #c084fc)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
13
+ Vula.js
14
+ </h1>
15
+ <h2 style={{ fontSize: '1.5rem', fontWeight: '500', color: '#94a3b8', marginTop: '0.5rem', margin: '0' }}>
16
+ Swavoti/vulajs
17
+ </h2>
18
+ </div>
19
+
20
+ {/* Introduction */}
21
+ <p style={{ fontSize: '1.25rem', color: '#cbd5e1', textAlign: 'center', maxWidth: '600px', lineHeight: '1.6', marginBottom: '3rem' }}>
22
+ The blazingly fast, minimal file-based React framework powered by Rspack.
23
+ Experience instant hot module replacement, zero-config SSR, and beautiful out-of-the-box defaults.
24
+ </p>
25
+
26
+ {/* Call to Actions */}
27
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap', justifyContent: 'center' }}>
28
+ <a href="https://github.com/nguyenblacks/vulajs" target="_blank" rel="noreferrer" style={{ padding: '1rem 2rem', backgroundColor: '#3b82f6', borderRadius: '9999px', textDecoration: 'none', color: '#ffffff', fontWeight: '600', fontSize: '1.125rem', transition: 'background-color 0.2s', boxShadow: '0 4px 6px -1px rgba(59, 130, 246, 0.4)' }}>
29
+ View Docs on GitHub
30
+ </a>
31
+ <a href="/api/hello" style={{ padding: '1rem 2rem', backgroundColor: 'transparent', border: '2px solid #475569', borderRadius: '9999px', textDecoration: 'none', color: '#f8fafc', fontWeight: '600', fontSize: '1.125rem', transition: 'border-color 0.2s' }}>
32
+ Test API Route
33
+ </a>
34
+ </div>
35
+
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,11 @@
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>Vula App</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "vulajs-template",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "dev": "vula dev",
6
+ "build": "vula build"
7
+ },
8
+ "dependencies": {
9
+ "react": "^19.2.6",
10
+ "react-dom": "^19.2.6",
11
+ "@swavoti/vula": "latest",
12
+ "@vula/video-opti": "latest"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^19.0.0",
16
+ "@types/react-dom": "^19.0.0",
17
+ "babel-loader": "^9.1.3",
18
+ "@babel/core": "^7.23.0",
19
+ "babel-plugin-react-compiler": "latest"
20
+ }
21
+ }
@@ -0,0 +1,65 @@
1
+ const path = require('path');
2
+ const { defineConfig } = require('@rspack/cli');
3
+ const { ReactRefreshRspackPlugin } = require('@rspack/plugin-react-refresh');
4
+ const rspack = require('@rspack/core');
5
+
6
+ module.exports = defineConfig({
7
+ entry: {
8
+ main: './src/index.tsx',
9
+ },
10
+ resolve: {
11
+ extensions: ['.ts', '.tsx', '.js', '.jsx'],
12
+ alias: {
13
+ '@app': path.resolve(__dirname, 'app'),
14
+ },
15
+ },
16
+ module: {
17
+ rules: [
18
+ {
19
+ test: /\.(j|t)sx?$/,
20
+ exclude: [/[\\/]node_modules[\\/]/],
21
+ use: [
22
+ {
23
+ loader: 'builtin:swc-loader',
24
+ options: {
25
+ jsc: {
26
+ parser: { syntax: 'typescript', tsx: true },
27
+ transform: { react: { runtime: 'automatic', refresh: true } },
28
+ },
29
+ },
30
+ },
31
+ {
32
+ loader: 'babel-loader',
33
+ options: {
34
+ plugins: [['babel-plugin-react-compiler', { target: '19' }]],
35
+ },
36
+ },
37
+ ],
38
+ type: 'javascript/auto',
39
+ },
40
+ {
41
+ test: /\.css$/,
42
+ type: 'css',
43
+ },
44
+ ],
45
+ },
46
+ plugins: [
47
+ new ReactRefreshRspackPlugin(),
48
+ new rspack.HtmlRspackPlugin({ template: './index.html' }),
49
+ ],
50
+ devServer: {
51
+ port: 3000,
52
+ hot: true,
53
+ historyApiFallback: true,
54
+ static: { directory: path.join(__dirname, 'public') },
55
+ },
56
+ output: {
57
+ clean: true,
58
+ path: path.resolve(__dirname, 'dist'),
59
+ },
60
+ optimization: {
61
+ splitChunks: {
62
+ chunks: 'all',
63
+ },
64
+ },
65
+ });
@@ -0,0 +1,11 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
5
+ background-color: #f9fafb;
6
+ color: #111827;
7
+ }
8
+
9
+ h1, h2, h3, p {
10
+ margin: 0;
11
+ }
@@ -0,0 +1,50 @@
1
+ import React, { Suspense, lazy } from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { match } from 'path-to-regexp';
4
+ import './globals.css';
5
+
6
+ // Route manifest generated by the Vula CLI
7
+ // @ts-ignore
8
+ import manifest from '../.vula-routes.json';
9
+
10
+ function Router() {
11
+ const pathname = window.location.pathname;
12
+
13
+ let matchedRoute: any = null;
14
+ let matchedParams: Record<string, string> = {};
15
+
16
+ for (const route of manifest.routes) {
17
+ const fn = match(route.path, { decode: decodeURIComponent });
18
+ const result = fn(pathname);
19
+ if (result) {
20
+ matchedRoute = route;
21
+ matchedParams = result.params as Record<string, string>;
22
+ break;
23
+ }
24
+ }
25
+
26
+ if (!matchedRoute) {
27
+ return (
28
+ <div style={{ padding: '4rem', textAlign: 'center', fontFamily: 'system-ui' }}>
29
+ <h1 style={{ fontSize: '3rem', marginBottom: '1rem' }}>404</h1>
30
+ <p>Page not found.</p>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ const relativePath = matchedRoute.componentPath.split('app')[1];
36
+ const Component = lazy(() => import(`@app${relativePath}`));
37
+
38
+ return (
39
+ <Suspense fallback={<div style={{ padding: '2rem', textAlign: 'center' }}>Loading...</div>}>
40
+ <Component params={matchedParams} />
41
+ </Suspense>
42
+ );
43
+ }
44
+
45
+ const root = ReactDOM.createRoot(document.getElementById('root')!);
46
+ root.render(
47
+ <React.StrictMode>
48
+ <Router />
49
+ </React.StrictMode>
50
+ );
@@ -0,0 +1,25 @@
1
+ export class Vula {
2
+ static async server(name: string, ...args: any[]) {
3
+ const res = await fetch('/_vula/server-component', {
4
+ method: 'POST',
5
+ headers: { 'Content-Type': 'application/json' },
6
+ body: JSON.stringify({ name, args }),
7
+ });
8
+ if (!res.ok) {
9
+ const err = await res.json().catch(() => ({ error: res.statusText }));
10
+ throw new Error(err.error || `Server component "${name}" failed`);
11
+ }
12
+ const { result } = await res.json();
13
+ return result;
14
+ }
15
+
16
+ static async api(path: string, options: RequestInit = {}) {
17
+ const url = path.startsWith('/') ? path : `/api/${path}`;
18
+ const res = await fetch(url, options);
19
+ if (!res.ok) {
20
+ const err = await res.json().catch(() => ({ error: res.statusText }));
21
+ throw new Error(err.error || `API ${path} returned ${res.status}`);
22
+ }
23
+ return res.json();
24
+ }
25
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "@app/*": ["app/*"]
19
+ }
20
+ },
21
+ "include": ["src", "app"],
22
+ "exclude": ["node_modules"]
23
+ }
@@ -0,0 +1,48 @@
1
+ # vulaism.config.yaml
2
+ # Vula.js project configuration file.
3
+ # All options are optional - defaults will apply when omitted.
4
+
5
+ app:
6
+ name: "My Vula App"
7
+ # Path to favicon/icon, relative to project root (from assets/ folder)
8
+ icon: "assets/icon.png"
9
+ # Default HTML language attribute
10
+ lang: "en"
11
+
12
+ optimization:
13
+ # Enable text optimizations (minification hints, compression headers)
14
+ text-opt: true
15
+ # Preload critical fonts via <link rel="preload">
16
+ font-preload: true
17
+ # CSS font-display strategy: swap | block | fallback | optional
18
+ font-display: "swap"
19
+ # Remove unused glyphs from fonts in production builds
20
+ font-subset: true
21
+ # Enable image compression hints during build
22
+ image-compression: true
23
+ # Minify injected HTML tags
24
+ minify-html: false
25
+
26
+ security:
27
+ # Enable CSRF origin protection middleware
28
+ csrf: true
29
+ # Whitelist origins for CSRF (empty = allow all in dev)
30
+ allowed-origins: []
31
+ # Inject Content-Security-Policy headers
32
+ content-security-policy: false
33
+
34
+ proxy:
35
+ # Enable the built-in HTTP proxy
36
+ enabled: false
37
+ # Target URL to proxy requests to
38
+ target: "http://localhost:8080"
39
+ # URL prefix to trigger the proxy
40
+ prefix: "/proxy"
41
+
42
+ server:
43
+ # Port to run the dev server on
44
+ port: 3000
45
+ # Enable gzip compression for responses
46
+ compression: true
47
+ # Trust X-Forwarded-* headers (set true when behind a reverse proxy)
48
+ trust-proxy: false