arcanajs 2.2.0 → 2.2.1

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 CHANGED
@@ -6,17 +6,18 @@ ArcanaJS is a modern React framework for building server-side rendered (SSR) app
6
6
 
7
7
  - **Server-Side Rendering (SSR):** Fast initial load times and SEO-friendly pages.
8
8
  - **TypeScript Support:** Built with TypeScript for type safety and better developer experience.
9
- - **File-based Routing:** Intuitive routing based on your file structure.
9
+ - **File-Server-Based Routing:** Intuitive routing based on your server routes and file structure.
10
10
  - **Hot Module Replacement (HMR):** Fast development cycle with instant updates.
11
11
  - **Tailwind CSS v4:** Integrated support for the latest Tailwind CSS with CSS-first configuration.
12
12
  - **Zero Configuration:** Get started quickly with sensible defaults.
13
13
 
14
14
  ## Quick Start
15
15
 
16
- ### Installation
16
+ ### Create a new folder
17
17
 
18
18
  ```bash
19
- npm install arcanajs
19
+ mkdir my-app
20
+ cd my-app
20
21
  ```
21
22
 
22
23
  ### Initialize a New Project
@@ -25,23 +26,14 @@ npm install arcanajs
25
26
  npx arcanajs init
26
27
  ```
27
28
 
28
- This creates the following structure:
29
- ```
30
- your-project/
31
- ├── src/
32
- │ ├── client/
33
- │ │ └── index.tsx # Client-side entry point
34
- │ ├── server/
35
- │ │ └── index.ts # Server-side entry point
36
- │ ├── views/
37
- │ │ └── Home.tsx # Your first page component
38
- │ └── globals.css # Tailwind CSS styles and theme
39
- ├── postcss.config.js # PostCSS configuration
40
- └── package.json
41
- ```
42
-
43
29
  ### Development
44
30
 
31
+ Install dependencies:
32
+
33
+ ```bash
34
+ npm install
35
+ ```
36
+
45
37
  Start the development server:
46
38
 
47
39
  ```bash
@@ -59,126 +51,9 @@ npm run build
59
51
  ### Start Production Server
60
52
 
61
53
  ```bash
62
- npm run start
63
- ```
64
-
65
- ## Tailwind CSS v4 Integration
66
-
67
- ArcanaJS comes with Tailwind CSS v4 pre-configured. Unlike previous versions, Tailwind v4 uses CSS-first configuration.
68
-
69
- ### Theme Customization
70
-
71
- Edit `src/globals.css` to customize your theme:
72
-
73
- ```css
74
- @import "tailwindcss";
75
-
76
- @theme {
77
- --color-primary: #3b82f6;
78
- --font-family-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
79
- --spacing-custom: 2.5rem;
80
- }
81
- ```
82
-
83
- ### Using Tailwind Classes
84
-
85
- Use Tailwind utility classes directly in your components:
86
-
87
- ```tsx
88
- export default function MyComponent() {
89
- return (
90
- <div className="bg-primary text-white p-custom rounded-lg">
91
- <h1 className="text-2xl font-bold">Hello Tailwind v4!</h1>
92
- </div>
93
- );
94
- }
54
+ npm start
95
55
  ```
96
56
 
97
- ### Custom Components
98
-
99
- Define reusable component styles in your `globals.css`:
100
-
101
- ```css
102
- .btn {
103
- @apply px-4 py-2 rounded font-medium transition-colors;
104
- }
105
-
106
- .btn-primary {
107
- @apply btn bg-blue-600 text-white hover:bg-blue-700;
108
- }
109
- ```
110
-
111
- ## Views and Routing
112
-
113
- Create pages by adding components to the `src/views/` directory:
114
-
115
- ```tsx
116
- // src/views/About.tsx
117
- import { Page, Head, Body } from 'arcanajs';
118
-
119
- export default function About() {
120
- return (
121
- <Page>
122
- <Head>
123
- <title>About Us</title>
124
- </Head>
125
- <Body>
126
- <div className="container mx-auto px-4 py-8">
127
- <h1 className="text-4xl font-bold text-gray-900">About Us</h1>
128
- </div>
129
- </Body>
130
- </Page>
131
- );
132
- }
133
- ```
134
-
135
- The page will automatically be available at `/About`.
136
-
137
- ## Server Configuration
138
-
139
- Customize your server in `src/server/index.ts`:
140
-
141
- ```tsx
142
- import express from 'express';
143
- import { createArcanaServer } from 'arcanajs/server';
144
-
145
- const app = express();
146
- const server = createArcanaServer(app, {
147
- // Custom configuration options
148
- });
149
-
150
- const PORT = process.env.PORT || 3000;
151
- server.listen(PORT, () => {
152
- console.log(`🚀 ArcanaJS server running on http://localhost:${PORT}`);
153
- });
154
- ```
155
-
156
- ## Available Commands
157
-
158
- - `arcanajs init` - Initialize a new ArcanaJS project
159
- - `arcanajs dev` - Start development server with hot reload
160
- - `arcanajs build` - Build for production
161
- - `arcanajs start` - Start production server
162
-
163
- ## Components
164
-
165
- ArcanaJS provides several built-in components:
166
-
167
- - `<Page>` - Wrapper component for pages
168
- - `<Head>` - Document head management
169
- - `<Body>` - Body content wrapper
170
- - `<Link>` - Client-side navigation
171
- - `<NavLink>` - Navigation link with active state
172
-
173
- ## Hooks
174
-
175
- - `useRouter()` - Access router functionality
176
- - `useLocation()` - Get current location
177
- - `useParams()` - Get URL parameters
178
- - `useQuery()` - Get query parameters
179
- - `usePage()` - Access page context
180
- - `useHead()` - Manage document head
181
-
182
57
  ## License
183
58
 
184
59
  MIT
@@ -7,6 +7,7 @@ const child_process_1 = require("child_process");
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const webpack_1 = __importDefault(require("webpack"));
10
+ const templates_1 = require("./templates");
10
11
  const webpack_config_1 = require("./webpack.config");
11
12
  const args = process.argv.slice(2);
12
13
  const command = args[0];
@@ -31,16 +32,28 @@ const runCompiler = (compiler) => {
31
32
  });
32
33
  };
33
34
  let serverProcess = null;
34
- const startDevServer = () => {
35
- if (serverProcess) {
36
- serverProcess.kill();
37
- }
38
- const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
39
- serverProcess = (0, child_process_1.spawn)("node", [serverPath], { stdio: "inherit" });
40
- serverProcess.on("close", (code) => {
41
- if (code !== 0 && code !== null) {
42
- console.error(`Dev server exited with code ${code}`);
35
+ const ws_1 = require("ws");
36
+ const startDevServer = (hmrPort) => {
37
+ return new Promise((resolve) => {
38
+ if (serverProcess) {
39
+ serverProcess.kill();
43
40
  }
41
+ const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
42
+ serverProcess = (0, child_process_1.spawn)("node", [serverPath], {
43
+ stdio: ["inherit", "pipe", "inherit"],
44
+ env: { ...process.env, ARCANA_HMR_PORT: hmrPort.toString() },
45
+ });
46
+ serverProcess.stdout?.on("data", (data) => {
47
+ process.stdout.write(data);
48
+ if (data.toString().includes("Server is running")) {
49
+ resolve();
50
+ }
51
+ });
52
+ serverProcess.on("close", (code) => {
53
+ if (code !== 0 && code !== null) {
54
+ console.error(`Dev server exited with code ${code}`);
55
+ }
56
+ });
44
57
  });
45
58
  };
46
59
  const watchCompiler = (compiler, onBuildComplete) => {
@@ -73,14 +86,47 @@ const build = async () => {
73
86
  const dev = async () => {
74
87
  process.env.NODE_ENV = "development";
75
88
  console.log("Starting development server...");
89
+ // Start HMR WebSocket Server
90
+ const HMR_PORT = 3001;
91
+ const wss = new ws_1.WebSocketServer({ port: HMR_PORT });
92
+ console.log(`HMR Server running on port ${HMR_PORT}`);
93
+ const broadcastReload = () => {
94
+ wss.clients.forEach((client) => {
95
+ if (client.readyState === 1) {
96
+ client.send(JSON.stringify({ type: "reload" }));
97
+ }
98
+ });
99
+ };
76
100
  const clientConfig = (0, webpack_config_1.createClientConfig)();
77
101
  const serverConfig = (0, webpack_config_1.createServerConfig)();
102
+ let isServerBuilding = false;
103
+ let pendingReload = false;
104
+ const serverCompiler = (0, webpack_1.default)(serverConfig);
105
+ serverCompiler.hooks.invalid.tap("ArcanaJS", () => {
106
+ isServerBuilding = true;
107
+ });
78
108
  // Watch client
79
- watchCompiler((0, webpack_1.default)(clientConfig));
109
+ watchCompiler((0, webpack_1.default)(clientConfig), () => {
110
+ console.log("Client build complete.");
111
+ if (isServerBuilding) {
112
+ console.log("Server is building. Waiting to reload...");
113
+ pendingReload = true;
114
+ }
115
+ else {
116
+ console.log("Reloading browsers...");
117
+ broadcastReload();
118
+ }
119
+ });
80
120
  // Watch server and restart on build
81
- watchCompiler((0, webpack_1.default)(serverConfig), () => {
121
+ watchCompiler(serverCompiler, async () => {
82
122
  console.log("Server build complete. Restarting server...");
83
- startDevServer();
123
+ await startDevServer(HMR_PORT);
124
+ isServerBuilding = false;
125
+ if (pendingReload) {
126
+ console.log("Pending reload found. Reloading browsers...");
127
+ broadcastReload();
128
+ pendingReload = false;
129
+ }
84
130
  });
85
131
  };
86
132
  const init = () => {
@@ -88,70 +134,32 @@ const init = () => {
88
134
  const cwd = process.cwd();
89
135
  const templatesDir = path_1.default.resolve(__dirname, "../templates");
90
136
  // Create necessary directories
91
- const publicDir = path_1.default.resolve(cwd, "public");
92
- const srcDir = path_1.default.resolve(cwd, "src");
93
- const clientDir = path_1.default.resolve(cwd, "src/client");
94
- const serverDir = path_1.default.resolve(cwd, "src/server");
95
- const routesDir = path_1.default.resolve(cwd, "src/server/routes");
96
- const controllersDir = path_1.default.resolve(cwd, "src/server/controllers");
97
- const viewsDir = path_1.default.resolve(cwd, "src/views");
98
- [
99
- publicDir,
100
- srcDir,
101
- clientDir,
102
- serverDir,
103
- routesDir,
104
- controllersDir,
105
- viewsDir,
106
- ].forEach((dir) => {
107
- if (!fs_1.default.existsSync(dir)) {
108
- fs_1.default.mkdirSync(dir, { recursive: true });
109
- console.log(`Created directory: ${path_1.default.relative(cwd, dir)}`);
137
+ templates_1.requiredDirs.forEach((dir) => {
138
+ const fullPath = path_1.default.resolve(cwd, dir);
139
+ if (!fs_1.default.existsSync(fullPath)) {
140
+ fs_1.default.mkdirSync(fullPath, { recursive: true });
141
+ console.log(`Created directory: ${dir}`);
110
142
  }
111
143
  });
112
144
  // Copy configuration files
113
- const configFiles = [
114
- { src: "package.json", dest: "package.json" },
115
- { src: "postcss.config.js", dest: "postcss.config.js" },
116
- { src: "globals.css", dest: "src/client/globals.css" },
117
- { src: "client-index.tsx", dest: "src/client/index.tsx" },
118
- { src: "server-index.ts", dest: "src/server/index.ts" },
119
- { src: "server-routes-web.ts", dest: "src/server/routes/web.ts" },
120
- {
121
- src: "server-controller-home.ts",
122
- dest: "src/server/controllers/HomeController.ts",
123
- },
124
- {
125
- src: "HomePage.tsx",
126
- dest: "src/views/HomePage.tsx",
127
- },
128
- {
129
- src: "arcanajs.png",
130
- dest: "public/arcanajs.png",
131
- },
132
- {
133
- src: "arcanajs.svg",
134
- dest: "public/arcanajs.svg",
135
- },
136
- {
137
- src: "favicon.ico",
138
- dest: "public/favicon.ico",
139
- },
140
- ];
141
- configFiles.forEach(({ src, dest }) => {
145
+ templates_1.configFiles.forEach(({ src, dest }) => {
142
146
  const srcPath = path_1.default.resolve(templatesDir, src);
143
147
  const destPath = path_1.default.resolve(cwd, dest);
144
148
  if (!fs_1.default.existsSync(destPath)) {
145
- fs_1.default.copyFileSync(srcPath, destPath);
146
- console.log(`Created: ${dest}`);
149
+ if (fs_1.default.existsSync(srcPath)) {
150
+ fs_1.default.copyFileSync(srcPath, destPath);
151
+ console.log(`Created: ${dest}`);
152
+ }
153
+ else {
154
+ console.warn(`Template not found: ${src}`);
155
+ }
147
156
  }
148
157
  else {
149
158
  console.log(`Skipped: ${dest} (already exists)`);
150
159
  }
151
160
  });
152
161
  // Create default error pages
153
- const errorPages = ["NotFoundPage.tsx", "ErrorPage.tsx"];
154
- errorPages.forEach((page) => {
162
+ templates_1.errorPages.forEach((page) => {
155
163
  const viewPath = path_1.default.resolve(cwd, `src/views/${page}`);
156
164
  const templatePath = path_1.default.resolve(templatesDir, page);
157
165
  if (!fs_1.default.existsSync(viewPath) && fs_1.default.existsSync(templatePath)) {
@@ -161,11 +169,12 @@ const init = () => {
161
169
  });
162
170
  console.log("\n✅ ArcanaJS project initialized successfully!");
163
171
  console.log("\nNext steps:");
164
- console.log("1. Run 'npm run dev' to start development");
165
- console.log("2. Visit http://localhost:3000 to see your app");
166
- console.log("3. Edit src/views/HomePage.tsx to customize your homepage");
167
- console.log("4. Customize your theme in src/client/globals.css");
168
- console.log("5. Add your Tailwind classes and enjoy!");
172
+ console.log("1. Run 'npm install' to install dependencies");
173
+ console.log("2. Run 'npm run dev' to start development");
174
+ console.log("3. Visit http://localhost:3000 to see your app");
175
+ console.log("4. Edit src/views/HomePage.tsx to customize your homepage");
176
+ console.log("5. Customize your theme in src/client/globals.css");
177
+ console.log("6. Add your Tailwind classes and enjoy!");
169
178
  };
170
179
  const start = () => {
171
180
  process.env.NODE_ENV = "production";
@@ -0,0 +1,6 @@
1
+ export declare const configFiles: {
2
+ src: string;
3
+ dest: string;
4
+ }[];
5
+ export declare const errorPages: string[];
6
+ export declare const requiredDirs: string[];
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requiredDirs = exports.errorPages = exports.configFiles = void 0;
4
+ exports.configFiles = [
5
+ { src: "package.json", dest: "package.json" },
6
+ { src: "postcss.config.js", dest: "postcss.config.js" },
7
+ { src: "globals.css", dest: "src/client/globals.css" },
8
+ { src: "client-index.tsx", dest: "src/client/index.tsx" },
9
+ { src: "server-index.ts", dest: "src/server/index.ts" },
10
+ { src: "server-routes-web.ts", dest: "src/server/routes/web.ts" },
11
+ {
12
+ src: "server-controller-home.ts",
13
+ dest: "src/server/controllers/HomeController.ts",
14
+ },
15
+ {
16
+ src: "HomePage.tsx",
17
+ dest: "src/views/HomePage.tsx",
18
+ },
19
+ {
20
+ src: "arcanajs.png",
21
+ dest: "public/arcanajs.png",
22
+ },
23
+ {
24
+ src: "arcanajs.svg",
25
+ dest: "public/arcanajs.svg",
26
+ },
27
+ {
28
+ src: "favicon.ico",
29
+ dest: "public/favicon.ico",
30
+ },
31
+ ];
32
+ exports.errorPages = ["NotFoundPage.tsx", "ErrorPage.tsx"];
33
+ exports.requiredDirs = [
34
+ "public",
35
+ "src",
36
+ "src/client",
37
+ "src/server",
38
+ "src/server/routes",
39
+ "src/server/controllers",
40
+ "src/views",
41
+ ];
@@ -165,14 +165,30 @@ const createClientConfig = () => {
165
165
  splitChunks: {
166
166
  chunks: "all",
167
167
  cacheGroups: {
168
- vendor: {
168
+ defaultVendors: {
169
169
  test: /[\\/]node_modules[\\/]/,
170
- name: "vendors",
170
+ priority: -10,
171
+ reuseExistingChunk: true,
172
+ },
173
+ default: {
174
+ minChunks: 2,
175
+ priority: -20,
176
+ reuseExistingChunk: true,
177
+ },
178
+ react: {
179
+ test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
180
+ name: "react-vendor",
171
181
  chunks: "all",
182
+ priority: 10,
172
183
  },
173
184
  },
174
185
  },
175
186
  },
187
+ performance: {
188
+ maxEntrypointSize: 512000,
189
+ maxAssetSize: 512000,
190
+ hints: isProduction ? "warning" : false,
191
+ },
176
192
  devtool: isProduction ? "source-map" : "eval-source-map",
177
193
  };
178
194
  };
@@ -29,7 +29,7 @@ const hydrateArcanaJS = (viewsOrContext, layout) => {
29
29
  views["ErrorPage"] = ErrorPage_1.default;
30
30
  }
31
31
  const container = document.getElementById("root");
32
- const dataScript = document.getElementById("__ArcanaJS_DATA__");
32
+ const dataScript = document.getElementById("__ARCANAJS_DATA__");
33
33
  // Client-side HeadManager (noop for push, as Head handles client updates via useEffect)
34
34
  const headManager = {
35
35
  tags: [],
@@ -1,4 +1,3 @@
1
- export * from "./client";
2
1
  export * from "./shared/components/Body";
3
2
  export * from "./shared/components/Head";
4
3
  export * from "./shared/components/Link";
@@ -8,12 +7,11 @@ export * from "./shared/context/HeadContext";
8
7
  export * from "./shared/context/PageContext";
9
8
  export * from "./shared/context/RouterContext";
10
9
  export * from "./shared/core/ArcanaJSApp";
11
- export * from "./shared/hooks/useDynamicComponents";
12
10
  export * from "./shared/hooks/useHead";
13
11
  export * from "./shared/hooks/useLocation";
14
12
  export * from "./shared/hooks/usePage";
15
13
  export * from "./shared/hooks/useParams";
16
14
  export * from "./shared/hooks/useQuery";
17
15
  export * from "./shared/hooks/useRouter";
18
- export { default as NotFoundPage } from "./shared/views/NotFoundPage";
19
16
  export { default as ErrorPage } from "./shared/views/ErrorPage";
17
+ export { default as NotFoundPage } from "./shared/views/NotFoundPage";
@@ -17,8 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.ErrorPage = exports.NotFoundPage = void 0;
21
- __exportStar(require("./client"), exports);
20
+ exports.NotFoundPage = exports.ErrorPage = void 0;
22
21
  __exportStar(require("./shared/components/Body"), exports);
23
22
  __exportStar(require("./shared/components/Head"), exports);
24
23
  __exportStar(require("./shared/components/Link"), exports);
@@ -28,7 +27,6 @@ __exportStar(require("./shared/context/HeadContext"), exports);
28
27
  __exportStar(require("./shared/context/PageContext"), exports);
29
28
  __exportStar(require("./shared/context/RouterContext"), exports);
30
29
  __exportStar(require("./shared/core/ArcanaJSApp"), exports);
31
- __exportStar(require("./shared/hooks/useDynamicComponents"), exports);
32
30
  __exportStar(require("./shared/hooks/useHead"), exports);
33
31
  __exportStar(require("./shared/hooks/useLocation"), exports);
34
32
  __exportStar(require("./shared/hooks/usePage"), exports);
@@ -36,7 +34,7 @@ __exportStar(require("./shared/hooks/useParams"), exports);
36
34
  __exportStar(require("./shared/hooks/useQuery"), exports);
37
35
  __exportStar(require("./shared/hooks/useRouter"), exports);
38
36
  // Default error views
39
- var NotFoundPage_1 = require("./shared/views/NotFoundPage");
40
- Object.defineProperty(exports, "NotFoundPage", { enumerable: true, get: function () { return __importDefault(NotFoundPage_1).default; } });
41
37
  var ErrorPage_1 = require("./shared/views/ErrorPage");
42
38
  Object.defineProperty(exports, "ErrorPage", { enumerable: true, get: function () { return __importDefault(ErrorPage_1).default; } });
39
+ var NotFoundPage_1 = require("./shared/views/NotFoundPage");
40
+ Object.defineProperty(exports, "NotFoundPage", { enumerable: true, get: function () { return __importDefault(NotFoundPage_1).default; } });
@@ -3,7 +3,15 @@ import React from "react";
3
3
  declare global {
4
4
  namespace Express {
5
5
  interface Response {
6
- renderPage(page: string, data?: any, params?: Record<string, string>): void;
6
+ /**
7
+ * Renders a React page using ArcanaJS SSR.
8
+ *
9
+ * @param page - The name of the page component to render.
10
+ * @param data - Initial data to pass to the page component (default: {}).
11
+ * @param params - Route parameters (default: {}).
12
+ * @returns The Express Response object.
13
+ */
14
+ renderPage(page: string, data?: any, params?: Record<string, string>): Response;
7
15
  }
8
16
  }
9
17
  }
@@ -80,18 +80,33 @@ const createArcanaJSMiddleware = (options) => {
80
80
  params,
81
81
  csrfToken,
82
82
  });
83
- const scriptTag = `<script id="__ArcanaJS_DATA__" type="application/json">${scriptContent}</script>`;
83
+ const scriptTag = `<script id="__ARCANAJS_DATA__" type="application/json">${scriptContent}</script>`;
84
+ const hmrScript = process.env.ARCANA_HMR_PORT
85
+ ? `
86
+ <script>
87
+ (function() {
88
+ const socket = new WebSocket("ws://localhost:${process.env.ARCANA_HMR_PORT}");
89
+ socket.onmessage = function(event) {
90
+ const data = JSON.parse(event.data);
91
+ if (data.type === "reload") {
92
+ window.location.reload();
93
+ }
94
+ };
95
+ })();
96
+ </script>`
97
+ : "";
84
98
  const html = htmlData
85
99
  .replace("<!--HEAD_CONTENT-->", headHtml)
86
100
  .replace("<!--APP_CONTENT-->", appHtml)
87
- .replace("<!--ARCANAJS_DATA_SCRIPT-->", scriptTag);
101
+ .replace("<!--ARCANAJS_DATA_SCRIPT-->", scriptTag + hmrScript);
88
102
  res.send(html);
89
103
  });
90
104
  }
91
105
  catch (error) {
92
106
  console.error("SSR Error:", error);
93
- res.status(500).send("Internal Server Error");
107
+ return res.status(500).send("Internal Server Error");
94
108
  }
109
+ return res;
95
110
  };
96
111
  next();
97
112
  };
@@ -16,6 +16,8 @@ export declare class ArcanaJSServer {
16
16
  private config;
17
17
  constructor(config: ArcanaJSConfig);
18
18
  private initialize;
19
+ private loadViewsFromContext;
20
+ private loadViewsFromAlias;
19
21
  private discoverViews;
20
22
  start(): void;
21
23
  }
@@ -10,12 +10,12 @@ const express_1 = __importDefault(require("express"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const helmet_1 = __importDefault(require("helmet"));
12
12
  const path_1 = __importDefault(require("path"));
13
+ const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
14
+ const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
13
15
  const ArcanaJSMiddleware_1 = require("./ArcanaJSMiddleware");
14
16
  const CsrfMiddleware_1 = require("./CsrfMiddleware");
15
17
  const DynamicRouter_1 = require("./DynamicRouter");
16
18
  const ResponseHandlerMiddleware_1 = require("./ResponseHandlerMiddleware");
17
- const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
18
- const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
19
19
  class ArcanaJSServer {
20
20
  constructor(config) {
21
21
  this.config = config;
@@ -24,42 +24,25 @@ class ArcanaJSServer {
24
24
  }
25
25
  initialize() {
26
26
  let { staticDir = "public", distDir = "dist/public", indexFile = "dist/public/index.html", views, viewsContext, routes, layout, } = this.config;
27
+ // 1. Load views from config or context (highest priority)
27
28
  if (!views && viewsContext) {
28
- views = {};
29
- viewsContext.keys().forEach((key) => {
30
- const viewName = key.replace(/^\.\/(.*)\.tsx$/, "$1");
31
- views[viewName] = viewsContext(key).default;
32
- });
29
+ views = this.loadViewsFromContext(viewsContext);
33
30
  }
31
+ // 2. Load views from injected alias (Webpack)
34
32
  if (!views) {
35
- // Try to load from injected alias (Webpack)
36
- try {
37
- // @ts-ignore - This alias is injected by Webpack
38
- const injectedViews = require("arcana-views");
39
- if (injectedViews) {
40
- views = {};
41
- injectedViews.keys().forEach((key) => {
42
- const viewName = key.replace(/^\.\/(.*)\.tsx$/, "$1");
43
- views[viewName] = injectedViews(key).default;
44
- });
45
- }
46
- }
47
- catch (e) {
48
- // Fallback to auto-discovery using fs (Server-side only, non-bundled)
49
- views = this.discoverViews();
50
- }
33
+ views = this.loadViewsFromAlias();
34
+ }
35
+ // 3. Fallback to auto-discovery (Server-side only, non-bundled)
36
+ if (!views) {
37
+ views = this.discoverViews();
51
38
  }
52
39
  if (!views || Object.keys(views).length === 0) {
53
40
  console.warn("No views found. Please check your views directory.");
54
41
  views = {};
55
42
  }
56
43
  // Add default error views if not already present
57
- if (!views.NotFoundPage) {
58
- views.NotFoundPage = NotFoundPage_1.default;
59
- }
60
- if (!views.ErrorPage) {
61
- views.ErrorPage = ErrorPage_1.default;
62
- }
44
+ views.NotFoundPage = views.NotFoundPage || NotFoundPage_1.default;
45
+ views.ErrorPage = views.ErrorPage || ErrorPage_1.default;
63
46
  // Security and Performance
64
47
  this.app.use((0, helmet_1.default)({
65
48
  contentSecurityPolicy: false,
@@ -69,14 +52,13 @@ class ArcanaJSServer {
69
52
  this.app.use((0, CsrfMiddleware_1.createCsrfMiddleware)());
70
53
  this.app.use(ResponseHandlerMiddleware_1.responseHandler);
71
54
  // Static files
72
- this.app.use(express_1.default.static(path_1.default.resolve(process.cwd(), distDir), {
55
+ const isProduction = process.env.NODE_ENV === "production";
56
+ const staticOptions = {
73
57
  index: false,
74
- maxAge: "1y",
75
- }));
76
- this.app.use(express_1.default.static(path_1.default.resolve(process.cwd(), staticDir), {
77
- index: false,
78
- maxAge: "1d",
79
- }));
58
+ maxAge: isProduction ? "1y" : "0",
59
+ };
60
+ this.app.use(express_1.default.static(path_1.default.resolve(process.cwd(), distDir), staticOptions));
61
+ this.app.use(express_1.default.static(path_1.default.resolve(process.cwd(), staticDir), staticOptions));
80
62
  // ArcanaJS Middleware
81
63
  this.app.use((0, ArcanaJSMiddleware_1.createArcanaJSMiddleware)({
82
64
  views,
@@ -107,14 +89,35 @@ class ArcanaJSServer {
107
89
  res.status(500).renderPage("ErrorPage", { message });
108
90
  });
109
91
  }
92
+ loadViewsFromContext(context) {
93
+ const views = {};
94
+ context.keys().forEach((key) => {
95
+ const viewName = key.replace(/^\.\/(.*)\.tsx$/, "$1");
96
+ views[viewName] = context(key).default;
97
+ });
98
+ return views;
99
+ }
100
+ loadViewsFromAlias() {
101
+ try {
102
+ // @ts-ignore - This alias is injected by Webpack
103
+ const injectedViews = require("arcana-views");
104
+ if (injectedViews) {
105
+ return this.loadViewsFromContext(injectedViews);
106
+ }
107
+ }
108
+ catch (e) {
109
+ // Ignore
110
+ }
111
+ return undefined;
112
+ }
110
113
  discoverViews() {
111
114
  const views = {};
112
115
  const viewsDir = this.config.viewsDir
113
116
  ? path_1.default.resolve(process.cwd(), this.config.viewsDir)
114
117
  : path_1.default.resolve(process.cwd(), "src/views");
118
+ if (!fs_1.default.existsSync(viewsDir))
119
+ return views;
115
120
  const traverse = (dir) => {
116
- if (!fs_1.default.existsSync(dir))
117
- return;
118
121
  const files = fs_1.default.readdirSync(dir);
119
122
  files.forEach((file) => {
120
123
  const fullPath = path_1.default.join(dir, file);
@@ -131,14 +134,13 @@ class ArcanaJSServer {
131
134
  const requireFunc = typeof __non_webpack_require__ !== "undefined"
132
135
  ? __non_webpack_require__
133
136
  : module.require;
134
- // We need to register ts-node if we are requiring .tsx files directly
135
- // This is a simplified approach. In a real framework, we'd handle compilation.
137
+ // Register ts-node if needed
136
138
  if (file.endsWith(".tsx") || file.endsWith(".ts")) {
137
139
  try {
138
140
  requireFunc("ts-node/register");
139
141
  }
140
142
  catch (e) {
141
- // Ignore if already registered or not found (might be pre-compiled)
143
+ // Ignore
142
144
  }
143
145
  }
144
146
  const pageModule = requireFunc(fullPath);
@@ -1,2 +1,2 @@
1
1
  import { NextFunction, Request, Response } from "express";
2
- export declare const createDynamicRouter: (views: Record<string, any>) => (req: Request, res: Response, next: NextFunction) => void;
2
+ export declare const createDynamicRouter: (views: Record<string, any>) => (req: Request, res: Response, next: NextFunction) => Express.Response | undefined;
@@ -73,7 +73,7 @@ export declare class Router {
73
73
  private _buildHandler;
74
74
  }
75
75
  /**
76
- * Static Route class for Laravel-like syntax
76
+ * Static Route class for ArcanaJS Routing
77
77
  */
78
78
  export declare class Route {
79
79
  private static _router;
@@ -155,7 +155,7 @@ class Router {
155
155
  }
156
156
  exports.Router = Router;
157
157
  /**
158
- * Static Route class for Laravel-like syntax
158
+ * Static Route class for ArcanaJS Routing
159
159
  */
160
160
  class Route {
161
161
  static create() {
@@ -1,9 +1,10 @@
1
1
  import { Express } from "express";
2
- import { ArcanaJSServer, ArcanaJSConfig } from "./server/ArcanaJSServer";
2
+ import { ArcanaJSConfig, ArcanaJSServer } from "./server/ArcanaJSServer";
3
3
  export * from "./server/ArcanaJSMiddleware";
4
4
  export * from "./server/ArcanaJSServer";
5
5
  export { default as ControllerBinder } from "./server/ControllerBinder";
6
6
  export * from "./server/DynamicRouter";
7
+ export * from "./server/ResponseHandlerMiddleware";
7
8
  export * from "./server/Router";
8
9
  export { default as Route } from "./server/Router";
9
10
  /**
@@ -25,6 +25,7 @@ __exportStar(require("./server/ArcanaJSServer"), exports);
25
25
  var ControllerBinder_1 = require("./server/ControllerBinder");
26
26
  Object.defineProperty(exports, "ControllerBinder", { enumerable: true, get: function () { return __importDefault(ControllerBinder_1).default; } });
27
27
  __exportStar(require("./server/DynamicRouter"), exports);
28
+ __exportStar(require("./server/ResponseHandlerMiddleware"), exports);
28
29
  __exportStar(require("./server/Router"), exports);
29
30
  var Router_1 = require("./server/Router");
30
31
  Object.defineProperty(exports, "Route", { enumerable: true, get: function () { return __importDefault(Router_1).default; } });
@@ -7,5 +7,5 @@ const Head_1 = require("../components/Head");
7
7
  const Body_1 = require("../components/Body");
8
8
  function ErrorPage({ message = "Something went wrong", statusCode = 500, stack, }) {
9
9
  const isDevelopment = process.env.NODE_ENV === "development";
10
- return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsxs)("title", { children: [statusCode, " - Server Error"] }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "An error occurred on the server" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsx)("div", { className: "min-h-screen bg-red-50 flex flex-col justify-center items-center px-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl w-full text-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-6xl mb-4", children: (0, jsx_runtime_1.jsx)("span", { className: "text-red-500", children: "\u26A0\uFE0F" }) }), (0, jsx_runtime_1.jsx)("h1", { className: "text-6xl font-bold text-red-500 mb-4", children: statusCode }), (0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-semibold text-gray-900 mb-2", children: "Server Error" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-600 mb-8", children: message })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.location.reload(), className: "block w-full px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 inline mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }), "Try Again"] })] }), isDevelopment && stack && ((0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-left", children: (0, jsx_runtime_1.jsxs)("details", { className: "bg-gray-100 rounded-lg p-4", children: [(0, jsx_runtime_1.jsx)("summary", { className: "cursor-pointer font-medium text-gray-700 mb-2", children: "Stack Trace (Development Only)" }), (0, jsx_runtime_1.jsx)("pre", { className: "text-xs text-gray-600 overflow-x-auto whitespace-pre-wrap", children: stack })] }) })), (0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-sm text-gray-500", children: (0, jsx_runtime_1.jsxs)("p", { children: ["If this problem persists, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-red-600 hover:text-red-500 underline", children: "contact support" }), "."] }) })] }) }) })] }));
10
+ return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsxs)("title", { children: [statusCode, " - Server Error"] }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "An error occurred on the server" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsxs)("div", { className: "relative min-h-screen overflow-hidden bg-black text-white flex flex-col justify-center items-center px-4 font-sans", children: [(0, jsx_runtime_1.jsxs)("div", { className: "fixed inset-0 z-0 overflow-hidden pointer-events-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 grid-pattern opacity-30" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute top-1/4 right-1/4 w-96 h-96 bg-red-600 rounded-full opacity-20 blur-3xl animate-glow" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-1/4 left-1/4 w-96 h-96 bg-orange-600 rounded-full opacity-10 blur-3xl animate-glow", style: { animationDelay: "2s" } }), (0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 hero-gradient" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "relative z-10 max-w-2xl w-full text-center animate-scale-in", children: [(0, jsx_runtime_1.jsxs)("div", { className: "glass-card rounded-3xl p-12 border border-white/10 shadow-2xl", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-6xl mb-6 animate-float", children: (0, jsx_runtime_1.jsx)("span", { className: "text-red-500 drop-shadow-lg filter", children: "\u26A0\uFE0F" }) }), (0, jsx_runtime_1.jsx)("h1", { className: "text-8xl font-bold text-transparent bg-clip-text bg-gradient-to-br from-red-500 to-orange-500 mb-4", children: statusCode }), (0, jsx_runtime_1.jsx)("h2", { className: "text-3xl font-bold text-white mb-4", children: "Server Error" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-400 text-lg leading-relaxed", children: message })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col sm:flex-row gap-4 justify-center mb-8", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "btn-primary px-8 py-3.5 text-white font-semibold rounded-xl inline-flex items-center justify-center gap-2 w-full sm:w-auto bg-gradient-to-r from-red-600 to-orange-600 hover:from-red-500 hover:to-orange-500 border-none shadow-red-900/20", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.location.reload(), className: "btn-secondary px-8 py-3.5 text-white font-semibold rounded-xl inline-flex items-center justify-center gap-2 w-full sm:w-auto", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }), "Try Again"] })] }), isDevelopment && stack && ((0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-left animate-slide-up", children: (0, jsx_runtime_1.jsxs)("details", { className: "bg-black/40 border border-white/10 rounded-xl overflow-hidden group", children: [(0, jsx_runtime_1.jsxs)("summary", { className: "cursor-pointer font-medium text-gray-300 p-4 hover:bg-white/5 transition-colors flex items-center justify-between select-none", children: [(0, jsx_runtime_1.jsx)("span", { children: "Stack Trace (Development Only)" }), (0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 text-gray-500 group-open:rotate-180 transition-transform", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), (0, jsx_runtime_1.jsx)("pre", { className: "text-xs text-red-300/80 p-4 overflow-x-auto whitespace-pre-wrap font-mono border-t border-white/5 bg-black/20", children: stack })] }) }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-8 text-gray-500 text-sm", children: ["If this problem persists, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-red-400 hover:text-red-300 underline transition-colors", children: "contact support" }), "."] })] })] }) })] }));
11
11
  }
@@ -6,7 +6,5 @@ const Page_1 = require("../components/Page");
6
6
  const Head_1 = require("../components/Head");
7
7
  const Body_1 = require("../components/Body");
8
8
  function NotFoundPage({ url }) {
9
- return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsx)("title", { children: "404 - Page Not Found" }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "The page you're looking for doesn't exist" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsx)("div", { className: "min-h-screen bg-gray-50 flex flex-col justify-center items-center px-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-md w-full text-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-9xl font-bold text-gray-200 mb-4", children: "404" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-semibold text-gray-900 mb-2", children: "Page Not Found" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-600 mb-8", children: url
10
- ? `The page "${url}" you're looking for doesn't exist.`
11
- : "The page you're looking for doesn't exist." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.history.back(), className: "block w-full px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5 inline mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 19l-7-7m0 0l7-7m-7 7h18" }) }), "Go Back"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "mt-8 text-sm text-gray-500", children: (0, jsx_runtime_1.jsxs)("p", { children: ["If you think this is an error, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-blue-600 hover:text-blue-500 underline", children: "contact us" }), "."] }) })] }) }) })] }));
9
+ return ((0, jsx_runtime_1.jsxs)(Page_1.Page, { children: [(0, jsx_runtime_1.jsxs)(Head_1.Head, { children: [(0, jsx_runtime_1.jsx)("title", { children: "404 - Page Not Found" }), (0, jsx_runtime_1.jsx)("meta", { name: "description", content: "The page you're looking for doesn't exist" })] }), (0, jsx_runtime_1.jsx)(Body_1.Body, { children: (0, jsx_runtime_1.jsxs)("div", { className: "relative min-h-screen overflow-hidden bg-black text-white flex flex-col justify-center items-center px-4 font-sans", children: [(0, jsx_runtime_1.jsxs)("div", { className: "fixed inset-0 z-0 overflow-hidden pointer-events-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 grid-pattern opacity-30" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute top-1/4 left-1/4 w-96 h-96 bg-orange-500 rounded-full opacity-20 blur-3xl animate-glow" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-1/4 right-1/4 w-96 h-96 bg-purple-500 rounded-full opacity-10 blur-3xl animate-glow", style: { animationDelay: "2s" } }), (0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 hero-gradient" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "relative z-10 max-w-lg w-full text-center animate-scale-in", children: [(0, jsx_runtime_1.jsxs)("div", { className: "glass-card rounded-3xl p-12 border border-white/10 shadow-2xl", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-9xl font-bold text-transparent bg-clip-text bg-gradient-to-br from-white to-gray-500 mb-4 animate-float", children: "404" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-3xl font-bold text-white mb-4", children: "Page Not Found" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-400 text-lg leading-relaxed", children: url ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["The page ", (0, jsx_runtime_1.jsxs)("span", { className: "text-orange-400", children: ["\"", url, "\""] }), " ", "you're looking for doesn't exist."] })) : ("The page you're looking for doesn't exist.") })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col sm:flex-row gap-4 justify-center", children: [(0, jsx_runtime_1.jsxs)("a", { href: "/", className: "btn-primary px-8 py-3.5 text-white font-semibold rounded-xl inline-flex items-center justify-center gap-2 w-full sm:w-auto", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), "Go Home"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => window.history.back(), className: "btn-secondary px-8 py-3.5 text-white font-semibold rounded-xl inline-flex items-center justify-center gap-2 w-full sm:w-auto", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 19l-7-7m0 0l7-7m-7 7h18" }) }), "Go Back"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-8 text-gray-500 text-sm", children: ["If you believe this is an error, please", " ", (0, jsx_runtime_1.jsx)("a", { href: "/contact", className: "text-orange-400 hover:text-orange-300 underline transition-colors", children: "contact support" }), "."] })] })] }) })] }));
12
10
  }
@@ -38,16 +38,15 @@ export default function HomePage() {
38
38
  {/* Navigation */}
39
39
  <nav className="fixed top-0 w-full z-50 nav-blur animate-slide-down border-b border-white/5 backdrop-blur-sm">
40
40
  <div className="container mx-auto px-6 py-4 flex items-center justify-between">
41
- <div className="flex items-center space-x-3 group cursor-pointer">
42
- <div className="relative w-10 h-10">
43
- <div className="absolute inset-0 bg-gradient-to-br from-orange-500 to-red-600 rounded-lg blur opacity-75 group-hover:opacity-100 transition-opacity"></div>
44
- <div className="relative w-full h-full bg-gradient-to-br from-orange-500 to-red-600 rounded-lg flex items-center justify-center shadow-lg">
45
- <span className="text-white font-bold text-xl">A</span>
46
- </div>
41
+ <div className="flex items-center gap-3 group cursor-pointer">
42
+ <div className="relative w-16 h-16">
43
+ <div className="absolute inset-0 bg-gradient-to-br from-orange-500 to-red-600 rounded-full blur-lg opacity-40 group-hover:opacity-100 transition-opacity"></div>
44
+ <img
45
+ src="arcanajs.png"
46
+ alt="ArcanaJS Logo"
47
+ className="relative w-full h-full object-contain"
48
+ />
47
49
  </div>
48
- <span className="text-xl font-bold text-white group-hover:text-orange-400 transition-colors tracking-tight">
49
- ArcanaJS
50
- </span>
51
50
  </div>
52
51
  <div className="hidden md:flex space-x-8">
53
52
  <a
@@ -273,12 +272,26 @@ export default function HomePage() {
273
272
  Quick Start
274
273
  </h2>
275
274
  <div className="bg-black/50 rounded-xl p-6 text-sm font-mono text-gray-300 overflow-x-auto border border-white/10 shadow-inner">
275
+ <div className="mb-4">
276
+ <div className="text-gray-500 mb-1">
277
+ # Create a new folder
278
+ </div>
279
+ <div className="text-orange-400">
280
+ mkdir my-app cd my-app
281
+ </div>
282
+ </div>
276
283
  <div className="mb-4">
277
284
  <div className="text-gray-500 mb-1">
278
285
  # Initialize a new project
279
286
  </div>
280
287
  <div className="text-orange-400">npx arcanajs init</div>
281
288
  </div>
289
+ <div className="mb-4">
290
+ <div className="text-gray-500 mb-1">
291
+ # Install dependencies
292
+ </div>
293
+ <div className="text-white">npm install</div>
294
+ </div>
282
295
  <div className="mb-4">
283
296
  <div className="text-gray-500 mb-1">
284
297
  # Start development server
@@ -291,6 +304,12 @@ export default function HomePage() {
291
304
  </div>
292
305
  <div className="text-white">npm run build</div>
293
306
  </div>
307
+ <div>
308
+ <div className="text-gray-500 mb-1">
309
+ # Start production server
310
+ </div>
311
+ <div className="text-white">npm start</div>
312
+ </div>
294
313
  </div>
295
314
  </section>
296
315
  </div>
@@ -1,4 +1,4 @@
1
- import { hydrateArcanaJS } from "arcanajs";
1
+ import { hydrateArcanaJS } from "arcanajs/client";
2
2
  import "./globals.css";
3
3
 
4
4
  // @ts-ignore
@@ -8,7 +8,7 @@
8
8
  "start": "arcanajs start"
9
9
  },
10
10
  "dependencies": {
11
- "arcanajs": "^2.1.6",
11
+ "arcanajs": "^2.2.1",
12
12
  "react": "^19.2.0",
13
13
  "react-dom": "^19.2.0"
14
14
  }
package/package.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
2
  "name": "arcanajs",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "ArcanaJS Framework",
5
5
  "main": "framework/lib/index.js",
6
6
  "types": "framework/lib/index.d.ts",
7
7
  "exports": {
8
- ".": "./framework/lib/index.js",
9
- "./server": "./framework/lib/server.js"
8
+ ".": {
9
+ "types": "./framework/lib/index.d.ts",
10
+ "default": "./framework/lib/index.js"
11
+ },
12
+ "./server": {
13
+ "types": "./framework/lib/server.d.ts",
14
+ "default": "./framework/lib/server.js"
15
+ },
16
+ "./client": {
17
+ "types": "./framework/lib/client/index.d.ts",
18
+ "default": "./framework/lib/client/index.js"
19
+ }
10
20
  },
11
21
  "bin": {
12
22
  "arcanajs": "./bin/arcanajs.js"
@@ -16,50 +26,45 @@
16
26
  "bin"
17
27
  ],
18
28
  "scripts": {
19
- "build": "tsc -p tsconfig.json && cp src/lib/server/default-index.html framework/lib/server/ && cp src/lib/global.d.ts framework/lib/ && cp -r src/templates framework/",
20
- "build:app": "node bin/arcanajs.js build",
21
- "dev:app": "node bin/arcanajs.js dev",
22
- "start:app": "node bin/arcanajs.js start",
23
- "lint": "eslint"
29
+ "build": "tsc -p tsconfig.json && cp src/lib/server/default-index.html framework/lib/server/ && cp src/lib/global.d.ts framework/lib/ && cp -r src/templates framework/"
24
30
  },
25
31
  "dependencies": {
26
- "compression": "^1.8.1",
27
- "cookie-parser": "^1.4.7",
28
- "cross-env": "^10.1.0",
29
- "express": "^5.1.0",
30
- "helmet": "^8.1.0",
31
- "react": "^19.2.0",
32
- "react-dom": "^19.2.0",
33
32
  "@babel/core": "^7.23.0",
34
33
  "@babel/preset-env": "^7.23.0",
35
34
  "@babel/preset-react": "^7.22.15",
36
35
  "@babel/preset-typescript": "^7.23.0",
37
36
  "@tailwindcss/postcss": "^4.1.17",
37
+ "@types/compression": "^1.8.1",
38
+ "@types/cookie-parser": "^1.4.10",
39
+ "@types/express": "^5.0.5",
40
+ "@types/node": "^24.10.1",
41
+ "@types/react": "^19.2.7",
42
+ "@types/react-dom": "^19.2.3",
43
+ "@types/ws": "^8.18.1",
38
44
  "autoprefixer": "^10.4.22",
39
45
  "babel-loader": "^10.0.0",
40
46
  "clean-webpack-plugin": "^4.0.0",
41
- "concurrently": "^9.2.1",
47
+ "compression": "^1.8.1",
48
+ "cookie-parser": "^1.4.7",
49
+ "cross-env": "^10.1.0",
42
50
  "css-loader": "^7.1.2",
51
+ "express": "^5.1.0",
52
+ "helmet": "^8.1.0",
43
53
  "html-webpack-plugin": "^5.6.5",
44
54
  "mini-css-extract-plugin": "^2.9.4",
45
55
  "null-loader": "^4.0.1",
46
56
  "postcss": "^8.5.6",
47
57
  "postcss-loader": "^8.2.0",
58
+ "react": "^19.2.0",
59
+ "react-dom": "^19.2.0",
48
60
  "style-loader": "^4.0.0",
49
61
  "tailwindcss": "^4.1.17",
50
- "ts-loader": "^9.4.4",
51
62
  "ts-node": "^10.9.2",
52
63
  "tsx": "^4.20.6",
53
64
  "typescript": "^5.9.3",
54
65
  "webpack": "^5.103.0",
55
66
  "webpack-cli": "^6.0.1",
56
67
  "webpack-node-externals": "^3.0.0",
57
- "@types/express": "^5.0.5",
58
- "@types/node": "^24.10.1",
59
- "@types/compression": "^1.8.1",
60
- "@types/cookie-parser": "^1.4.10",
61
- "@types/react": "^19.2.7",
62
- "@types/react-dom": "^19.2.3"
63
- },
64
- "devDependencies": {}
68
+ "ws": "^8.18.3"
69
+ }
65
70
  }
@@ -1 +0,0 @@
1
- export declare const useDynamicComponents: (loader: () => Promise<any>) => any;
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useDynamicComponents = void 0;
4
- const react_1 = require("react");
5
- const useDynamicComponents = (loader) => {
6
- const [component, setComponent] = (0, react_1.useState)(null);
7
- (0, react_1.useEffect)(() => {
8
- let mounted = true;
9
- loader().then((mod) => {
10
- if (mounted) {
11
- setComponent(mod.default || mod);
12
- }
13
- });
14
- return () => {
15
- mounted = false;
16
- };
17
- }, []); // loader dependency omitted to avoid loops if loader is inline
18
- return component;
19
- };
20
- exports.useDynamicComponents = useDynamicComponents;