arcanajs 2.2.0 → 2.2.2
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 +11 -136
- package/framework/cli/index.js +78 -69
- package/framework/cli/templates.d.ts +6 -0
- package/framework/cli/templates.js +41 -0
- package/framework/cli/webpack.config.js +18 -2
- package/framework/lib/client/index.js +1 -1
- package/framework/lib/index.d.ts +1 -3
- package/framework/lib/index.js +3 -5
- package/framework/lib/server/ArcanaJSMiddleware.d.ts +9 -1
- package/framework/lib/server/ArcanaJSMiddleware.js +18 -3
- package/framework/lib/server/ArcanaJSServer.d.ts +2 -0
- package/framework/lib/server/ArcanaJSServer.js +43 -41
- package/framework/lib/server/DynamicRouter.d.ts +1 -1
- package/framework/lib/server/Router.d.ts +1 -1
- package/framework/lib/server/Router.js +1 -1
- package/framework/lib/server.d.ts +2 -1
- package/framework/lib/server.js +1 -0
- package/framework/lib/shared/views/ErrorPage.js +1 -1
- package/framework/lib/shared/views/NotFoundPage.js +1 -3
- package/framework/templates/HomePage.tsx +29 -10
- package/framework/templates/client-index.tsx +1 -1
- package/framework/templates/package.json +1 -1
- package/framework/templates/server-index.ts +3 -2
- package/package.json +30 -25
- package/framework/lib/shared/hooks/useDynamicComponents.d.ts +0 -1
- package/framework/lib/shared/hooks/useDynamicComponents.js +0 -20
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-
|
|
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
|
-
###
|
|
16
|
+
### Create a new folder
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
|
|
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
|
|
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
|
package/framework/cli/index.js
CHANGED
|
@@ -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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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(
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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.
|
|
146
|
-
|
|
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
|
-
|
|
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
|
|
165
|
-
console.log("2.
|
|
166
|
-
console.log("3.
|
|
167
|
-
console.log("4.
|
|
168
|
-
console.log("5.
|
|
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,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
|
-
|
|
168
|
+
defaultVendors: {
|
|
169
169
|
test: /[\\/]node_modules[\\/]/,
|
|
170
|
-
|
|
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("
|
|
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: [],
|
package/framework/lib/index.d.ts
CHANGED
|
@@ -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";
|
package/framework/lib/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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="
|
|
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
|
};
|
|
@@ -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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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(),
|
|
77
|
-
|
|
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
|
-
//
|
|
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
|
|
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) =>
|
|
2
|
+
export declare const createDynamicRouter: (views: Record<string, any>) => (req: Request, res: Response, next: NextFunction) => Express.Response | undefined;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Express } from "express";
|
|
2
|
-
import {
|
|
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
|
/**
|
package/framework/lib/server.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|
42
|
-
<div className="relative w-
|
|
43
|
-
<div className="absolute inset-0 bg-gradient-to-br from-orange-500 to-red-600 rounded-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
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,24 +272,44 @@ 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
|
|
285
298
|
</div>
|
|
286
299
|
<div className="text-white">npm run dev</div>
|
|
287
300
|
</div>
|
|
288
|
-
<div>
|
|
301
|
+
<div className="mb-4">
|
|
289
302
|
<div className="text-gray-500 mb-1">
|
|
290
303
|
# Build for production
|
|
291
304
|
</div>
|
|
292
305
|
<div className="text-white">npm run build</div>
|
|
293
306
|
</div>
|
|
307
|
+
<div className="mb-4">
|
|
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,10 +1,11 @@
|
|
|
1
1
|
import { ArcanaJSServer } from "arcanajs/server";
|
|
2
2
|
import webRoutes from "./routes/web";
|
|
3
3
|
|
|
4
|
+
const PORT = process.env.PORT || 3000;
|
|
5
|
+
|
|
4
6
|
const server = new ArcanaJSServer({
|
|
7
|
+
port: PORT,
|
|
5
8
|
routes: webRoutes,
|
|
6
9
|
});
|
|
7
10
|
|
|
8
|
-
const PORT = process.env.PORT || 3000;
|
|
9
|
-
|
|
10
11
|
server.start();
|
package/package.json
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcanajs",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "ArcanaJS Framework",
|
|
5
5
|
"main": "framework/lib/index.js",
|
|
6
6
|
"types": "framework/lib/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
58
|
-
|
|
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;
|