arcanajs 2.1.5 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -2
- package/framework/cli/index.js +89 -1
- package/framework/cli/webpack.config.js +35 -16
- package/framework/lib/client/index.js +12 -0
- package/framework/lib/index.d.ts +2 -0
- package/framework/lib/index.js +9 -0
- package/framework/lib/server/ArcanaJSServer.js +9 -0
- package/framework/lib/server.d.ts +9 -0
- package/framework/lib/server.js +12 -0
- package/framework/lib/shared/views/ErrorPage.d.ts +7 -0
- package/framework/lib/shared/views/ErrorPage.js +11 -0
- package/framework/lib/shared/views/NotFoundPage.d.ts +5 -0
- package/framework/lib/shared/views/NotFoundPage.js +12 -0
- package/framework/templates/ErrorPage.tsx +137 -0
- package/framework/templates/HomePage.tsx +325 -0
- package/framework/templates/NotFoundPage.tsx +109 -0
- package/framework/templates/arcanajs.png +0 -0
- package/framework/templates/arcanajs.svg +12 -0
- package/framework/templates/client-index.tsx +7 -0
- package/framework/templates/favicon.ico +0 -0
- package/framework/templates/globals.css +198 -0
- package/framework/templates/package.json +15 -0
- package/framework/templates/postcss.config.js +6 -0
- package/framework/templates/server-controller-home.ts +7 -0
- package/framework/templates/server-index.ts +10 -0
- package/framework/templates/server-routes-web.ts +7 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArcanaJS Framework
|
|
2
2
|
|
|
3
|
-
ArcanaJS is a modern React framework for building server-side rendered (SSR) applications with ease. It combines the power of Express, React, and
|
|
3
|
+
ArcanaJS is a modern React framework for building server-side rendered (SSR) applications with ease. It combines the power of Express, React, TypeScript, and Tailwind CSS v4 to provide a seamless development experience.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -8,9 +8,176 @@ ArcanaJS is a modern React framework for building server-side rendered (SSR) app
|
|
|
8
8
|
- **TypeScript Support:** Built with TypeScript for type safety and better developer experience.
|
|
9
9
|
- **File-based Routing:** Intuitive routing based on your file structure.
|
|
10
10
|
- **Hot Module Replacement (HMR):** Fast development cycle with instant updates.
|
|
11
|
-
- **Tailwind CSS:** Integrated support for
|
|
11
|
+
- **Tailwind CSS v4:** Integrated support for the latest Tailwind CSS with CSS-first configuration.
|
|
12
|
+
- **Zero Configuration:** Get started quickly with sensible defaults.
|
|
12
13
|
|
|
14
|
+
## Quick Start
|
|
13
15
|
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install arcanajs
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Initialize a New Project
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx arcanajs init
|
|
26
|
+
```
|
|
27
|
+
|
|
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
|
+
### Development
|
|
44
|
+
|
|
45
|
+
Start the development server:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Visit `http://localhost:3000` to see your application.
|
|
52
|
+
|
|
53
|
+
### Build for Production
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Start Production Server
|
|
60
|
+
|
|
61
|
+
```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
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
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
|
|
14
181
|
|
|
15
182
|
## License
|
|
16
183
|
|
package/framework/cli/index.js
CHANGED
|
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
8
|
const path_1 = __importDefault(require("path"));
|
|
8
9
|
const webpack_1 = __importDefault(require("webpack"));
|
|
9
10
|
const webpack_config_1 = require("./webpack.config");
|
|
10
11
|
const args = process.argv.slice(2);
|
|
11
12
|
const command = args[0];
|
|
12
13
|
if (!command) {
|
|
13
|
-
console.error("Please specify a command: dev, build, start");
|
|
14
|
+
console.error("Please specify a command: init, dev, build, start");
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
16
17
|
const runCompiler = (compiler) => {
|
|
@@ -82,6 +83,90 @@ const dev = async () => {
|
|
|
82
83
|
startDevServer();
|
|
83
84
|
});
|
|
84
85
|
};
|
|
86
|
+
const init = () => {
|
|
87
|
+
console.log("Initializing ArcanaJS project with Tailwind CSS...");
|
|
88
|
+
const cwd = process.cwd();
|
|
89
|
+
const templatesDir = path_1.default.resolve(__dirname, "../templates");
|
|
90
|
+
// 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)}`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// 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 }) => {
|
|
142
|
+
const srcPath = path_1.default.resolve(templatesDir, src);
|
|
143
|
+
const destPath = path_1.default.resolve(cwd, dest);
|
|
144
|
+
if (!fs_1.default.existsSync(destPath)) {
|
|
145
|
+
fs_1.default.copyFileSync(srcPath, destPath);
|
|
146
|
+
console.log(`Created: ${dest}`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.log(`Skipped: ${dest} (already exists)`);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
// Create default error pages
|
|
153
|
+
const errorPages = ["NotFoundPage.tsx", "ErrorPage.tsx"];
|
|
154
|
+
errorPages.forEach((page) => {
|
|
155
|
+
const viewPath = path_1.default.resolve(cwd, `src/views/${page}`);
|
|
156
|
+
const templatePath = path_1.default.resolve(templatesDir, page);
|
|
157
|
+
if (!fs_1.default.existsSync(viewPath) && fs_1.default.existsSync(templatePath)) {
|
|
158
|
+
fs_1.default.copyFileSync(templatePath, viewPath);
|
|
159
|
+
console.log(`Created: src/views/${page}`);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
console.log("\n✅ ArcanaJS project initialized successfully!");
|
|
163
|
+
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!");
|
|
169
|
+
};
|
|
85
170
|
const start = () => {
|
|
86
171
|
process.env.NODE_ENV = "production";
|
|
87
172
|
const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
|
|
@@ -92,6 +177,9 @@ const start = () => {
|
|
|
92
177
|
});
|
|
93
178
|
};
|
|
94
179
|
switch (command) {
|
|
180
|
+
case "init":
|
|
181
|
+
init();
|
|
182
|
+
break;
|
|
95
183
|
case "build":
|
|
96
184
|
build();
|
|
97
185
|
break;
|
|
@@ -41,8 +41,25 @@ const findEntry = (searchPaths) => {
|
|
|
41
41
|
return exampleServer;
|
|
42
42
|
throw new Error(`Could not find entry point. Searched in: ${searchPaths.join(", ")}`);
|
|
43
43
|
};
|
|
44
|
+
const getViewsLoaderPath = () => {
|
|
45
|
+
const viewsDir = path_1.default.resolve(cwd, "src/views");
|
|
46
|
+
const hasViews = fs_1.default.existsSync(viewsDir);
|
|
47
|
+
const viewsLoaderPath = path_1.default.resolve(__dirname, "../../node_modules/.cache/arcanajs/views-loader.js");
|
|
48
|
+
// Ensure cache directory exists
|
|
49
|
+
const cacheDir = path_1.default.dirname(viewsLoaderPath);
|
|
50
|
+
if (!fs_1.default.existsSync(cacheDir)) {
|
|
51
|
+
fs_1.default.mkdirSync(cacheDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Generate the loader file
|
|
54
|
+
const loaderContent = hasViews
|
|
55
|
+
? `module.exports = require.context('${viewsDir}', true, /\\.(tsx|jsx)$/);`
|
|
56
|
+
: `module.exports = null;`;
|
|
57
|
+
fs_1.default.writeFileSync(viewsLoaderPath, loaderContent);
|
|
58
|
+
return viewsLoaderPath;
|
|
59
|
+
};
|
|
44
60
|
const createClientConfig = () => {
|
|
45
61
|
const isProduction = process.env.NODE_ENV === "production";
|
|
62
|
+
const viewsLoaderPath = getViewsLoaderPath();
|
|
46
63
|
const clientEntry = findEntry([
|
|
47
64
|
"src/client",
|
|
48
65
|
"src/client/index",
|
|
@@ -65,6 +82,9 @@ const createClientConfig = () => {
|
|
|
65
82
|
},
|
|
66
83
|
resolve: {
|
|
67
84
|
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
85
|
+
alias: {
|
|
86
|
+
"arcana-views": viewsLoaderPath,
|
|
87
|
+
},
|
|
68
88
|
},
|
|
69
89
|
resolveLoader: {
|
|
70
90
|
modules: ["node_modules", path_1.default.resolve(__dirname, "../../node_modules")],
|
|
@@ -94,8 +114,20 @@ const createClientConfig = () => {
|
|
|
94
114
|
isProduction
|
|
95
115
|
? mini_css_extract_plugin_1.default.loader
|
|
96
116
|
: resolveLoader("style-loader"),
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
{
|
|
118
|
+
loader: resolveLoader("css-loader"),
|
|
119
|
+
options: {
|
|
120
|
+
importLoaders: 1,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
loader: resolveLoader("postcss-loader"),
|
|
125
|
+
options: {
|
|
126
|
+
postcssOptions: {
|
|
127
|
+
config: path_1.default.resolve(cwd, "postcss.config.js"),
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
99
131
|
],
|
|
100
132
|
},
|
|
101
133
|
{
|
|
@@ -152,20 +184,7 @@ const createServerConfig = () => {
|
|
|
152
184
|
"src/server/index",
|
|
153
185
|
"src/server/main",
|
|
154
186
|
]);
|
|
155
|
-
|
|
156
|
-
const viewsDir = path_1.default.resolve(cwd, "src/views");
|
|
157
|
-
const hasViews = fs_1.default.existsSync(viewsDir);
|
|
158
|
-
const viewsLoaderPath = path_1.default.resolve(__dirname, "../../node_modules/.cache/arcanajs/views-loader.js");
|
|
159
|
-
// Ensure cache directory exists
|
|
160
|
-
const cacheDir = path_1.default.dirname(viewsLoaderPath);
|
|
161
|
-
if (!fs_1.default.existsSync(cacheDir)) {
|
|
162
|
-
fs_1.default.mkdirSync(cacheDir, { recursive: true });
|
|
163
|
-
}
|
|
164
|
-
// Generate the loader file
|
|
165
|
-
const loaderContent = hasViews
|
|
166
|
-
? `module.exports = require.context('${viewsDir}', true, /\\.(tsx|jsx)$/);`
|
|
167
|
-
: `module.exports = null;`;
|
|
168
|
-
fs_1.default.writeFileSync(viewsLoaderPath, loaderContent);
|
|
187
|
+
const viewsLoaderPath = getViewsLoaderPath();
|
|
169
188
|
return {
|
|
170
189
|
mode: isProduction ? "production" : "development",
|
|
171
190
|
target: "node",
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.hydrateArcanaJS = void 0;
|
|
4
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
8
|
const client_1 = require("react-dom/client");
|
|
6
9
|
const HeadContext_1 = require("../shared/context/HeadContext");
|
|
7
10
|
const ArcanaJSApp_1 = require("../shared/core/ArcanaJSApp");
|
|
11
|
+
const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
|
|
12
|
+
const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
|
|
8
13
|
const hydrateArcanaJS = (viewsOrContext, layout) => {
|
|
9
14
|
let views = {};
|
|
10
15
|
if (viewsOrContext.keys && typeof viewsOrContext.keys === "function") {
|
|
@@ -16,6 +21,13 @@ const hydrateArcanaJS = (viewsOrContext, layout) => {
|
|
|
16
21
|
else {
|
|
17
22
|
views = viewsOrContext;
|
|
18
23
|
}
|
|
24
|
+
// Add default error views if not present
|
|
25
|
+
if (!views["NotFoundPage"]) {
|
|
26
|
+
views["NotFoundPage"] = NotFoundPage_1.default;
|
|
27
|
+
}
|
|
28
|
+
if (!views["ErrorPage"]) {
|
|
29
|
+
views["ErrorPage"] = ErrorPage_1.default;
|
|
30
|
+
}
|
|
19
31
|
const container = document.getElementById("root");
|
|
20
32
|
const dataScript = document.getElementById("__ArcanaJS_DATA__");
|
|
21
33
|
// Client-side HeadManager (noop for push, as Head handles client updates via useEffect)
|
package/framework/lib/index.d.ts
CHANGED
|
@@ -15,3 +15,5 @@ export * from "./shared/hooks/usePage";
|
|
|
15
15
|
export * from "./shared/hooks/useParams";
|
|
16
16
|
export * from "./shared/hooks/useQuery";
|
|
17
17
|
export * from "./shared/hooks/useRouter";
|
|
18
|
+
export { default as NotFoundPage } from "./shared/views/NotFoundPage";
|
|
19
|
+
export { default as ErrorPage } from "./shared/views/ErrorPage";
|
package/framework/lib/index.js
CHANGED
|
@@ -13,7 +13,11 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
13
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
16
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.ErrorPage = exports.NotFoundPage = void 0;
|
|
17
21
|
__exportStar(require("./client"), exports);
|
|
18
22
|
__exportStar(require("./shared/components/Body"), exports);
|
|
19
23
|
__exportStar(require("./shared/components/Head"), exports);
|
|
@@ -31,3 +35,8 @@ __exportStar(require("./shared/hooks/usePage"), exports);
|
|
|
31
35
|
__exportStar(require("./shared/hooks/useParams"), exports);
|
|
32
36
|
__exportStar(require("./shared/hooks/useQuery"), exports);
|
|
33
37
|
__exportStar(require("./shared/hooks/useRouter"), exports);
|
|
38
|
+
// 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
|
+
var ErrorPage_1 = require("./shared/views/ErrorPage");
|
|
42
|
+
Object.defineProperty(exports, "ErrorPage", { enumerable: true, get: function () { return __importDefault(ErrorPage_1).default; } });
|
|
@@ -14,6 +14,8 @@ const ArcanaJSMiddleware_1 = require("./ArcanaJSMiddleware");
|
|
|
14
14
|
const CsrfMiddleware_1 = require("./CsrfMiddleware");
|
|
15
15
|
const DynamicRouter_1 = require("./DynamicRouter");
|
|
16
16
|
const ResponseHandlerMiddleware_1 = require("./ResponseHandlerMiddleware");
|
|
17
|
+
const NotFoundPage_1 = __importDefault(require("../shared/views/NotFoundPage"));
|
|
18
|
+
const ErrorPage_1 = __importDefault(require("../shared/views/ErrorPage"));
|
|
17
19
|
class ArcanaJSServer {
|
|
18
20
|
constructor(config) {
|
|
19
21
|
this.config = config;
|
|
@@ -51,6 +53,13 @@ class ArcanaJSServer {
|
|
|
51
53
|
console.warn("No views found. Please check your views directory.");
|
|
52
54
|
views = {};
|
|
53
55
|
}
|
|
56
|
+
// 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
|
+
}
|
|
54
63
|
// Security and Performance
|
|
55
64
|
this.app.use((0, helmet_1.default)({
|
|
56
65
|
contentSecurityPolicy: false,
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { Express } from "express";
|
|
2
|
+
import { ArcanaJSServer, ArcanaJSConfig } from "./server/ArcanaJSServer";
|
|
1
3
|
export * from "./server/ArcanaJSMiddleware";
|
|
2
4
|
export * from "./server/ArcanaJSServer";
|
|
3
5
|
export { default as ControllerBinder } from "./server/ControllerBinder";
|
|
4
6
|
export * from "./server/DynamicRouter";
|
|
5
7
|
export * from "./server/Router";
|
|
6
8
|
export { default as Route } from "./server/Router";
|
|
9
|
+
/**
|
|
10
|
+
* Create an ArcanaJS server with the given Express app
|
|
11
|
+
* @param app Express application instance
|
|
12
|
+
* @param config Optional ArcanaJS configuration
|
|
13
|
+
* @returns ArcanaJSServer instance
|
|
14
|
+
*/
|
|
15
|
+
export declare function createArcanaServer(app: Express, config?: Partial<ArcanaJSConfig>): ArcanaJSServer;
|
package/framework/lib/server.js
CHANGED
|
@@ -18,6 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.Route = exports.ControllerBinder = void 0;
|
|
21
|
+
exports.createArcanaServer = createArcanaServer;
|
|
22
|
+
const ArcanaJSServer_1 = require("./server/ArcanaJSServer");
|
|
21
23
|
__exportStar(require("./server/ArcanaJSMiddleware"), exports);
|
|
22
24
|
__exportStar(require("./server/ArcanaJSServer"), exports);
|
|
23
25
|
var ControllerBinder_1 = require("./server/ControllerBinder");
|
|
@@ -26,3 +28,13 @@ __exportStar(require("./server/DynamicRouter"), exports);
|
|
|
26
28
|
__exportStar(require("./server/Router"), exports);
|
|
27
29
|
var Router_1 = require("./server/Router");
|
|
28
30
|
Object.defineProperty(exports, "Route", { enumerable: true, get: function () { return __importDefault(Router_1).default; } });
|
|
31
|
+
/**
|
|
32
|
+
* Create an ArcanaJS server with the given Express app
|
|
33
|
+
* @param app Express application instance
|
|
34
|
+
* @param config Optional ArcanaJS configuration
|
|
35
|
+
* @returns ArcanaJSServer instance
|
|
36
|
+
*/
|
|
37
|
+
function createArcanaServer(app, config) {
|
|
38
|
+
const server = new ArcanaJSServer_1.ArcanaJSServer({ ...config });
|
|
39
|
+
return server;
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = ErrorPage;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const Page_1 = require("../components/Page");
|
|
6
|
+
const Head_1 = require("../components/Head");
|
|
7
|
+
const Body_1 = require("../components/Body");
|
|
8
|
+
function ErrorPage({ message = "Something went wrong", statusCode = 500, stack, }) {
|
|
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" }), "."] }) })] }) }) })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = NotFoundPage;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const Page_1 = require("../components/Page");
|
|
6
|
+
const Head_1 = require("../components/Head");
|
|
7
|
+
const Body_1 = require("../components/Body");
|
|
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" }), "."] }) })] }) }) })] }));
|
|
12
|
+
}
|