arcanajs 2.1.6 → 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.
Files changed (52) hide show
  1. package/README.md +169 -2
  2. package/framework/cli/index.js +110 -17
  3. package/framework/cli/webpack.config.js +69 -42
  4. package/framework/lib/client/index.js +22 -6
  5. package/framework/lib/index.d.ts +2 -0
  6. package/framework/lib/index.js +42 -17
  7. package/framework/lib/server/ArcanaJSMiddleware.js +17 -10
  8. package/framework/lib/server/ArcanaJSServer.js +45 -29
  9. package/framework/lib/server/ControllerBinder.js +4 -1
  10. package/framework/lib/server/CsrfMiddleware.js +10 -3
  11. package/framework/lib/server/DynamicRouter.js +5 -1
  12. package/framework/lib/server/ResponseHandlerMiddleware.js +5 -1
  13. package/framework/lib/server/Router.js +15 -7
  14. package/framework/lib/server.d.ts +9 -0
  15. package/framework/lib/server.js +40 -6
  16. package/framework/lib/shared/components/Body.js +7 -3
  17. package/framework/lib/shared/components/Head.js +47 -10
  18. package/framework/lib/shared/components/Link.js +9 -5
  19. package/framework/lib/shared/components/NavLink.js +10 -6
  20. package/framework/lib/shared/components/Page.js +9 -5
  21. package/framework/lib/shared/context/HeadContext.js +5 -2
  22. package/framework/lib/shared/context/PageContext.js +5 -2
  23. package/framework/lib/shared/context/RouterContext.js +9 -5
  24. package/framework/lib/shared/core/ArcanaJSApp.js +18 -14
  25. package/framework/lib/shared/hooks/useDynamicComponents.js +8 -4
  26. package/framework/lib/shared/hooks/useHead.d.ts +1 -1
  27. package/framework/lib/shared/hooks/useHead.js +7 -3
  28. package/framework/lib/shared/hooks/useLocation.js +7 -3
  29. package/framework/lib/shared/hooks/usePage.js +7 -3
  30. package/framework/lib/shared/hooks/useParams.js +8 -4
  31. package/framework/lib/shared/hooks/useQuery.js +7 -3
  32. package/framework/lib/shared/hooks/useRouter.d.ts +1 -1
  33. package/framework/lib/shared/hooks/useRouter.js +8 -4
  34. package/framework/lib/shared/utils/createSingletonContext.js +6 -3
  35. package/framework/lib/shared/views/ErrorPage.d.ts +7 -0
  36. package/framework/lib/shared/views/ErrorPage.js +11 -0
  37. package/framework/lib/shared/views/NotFoundPage.d.ts +5 -0
  38. package/framework/lib/shared/views/NotFoundPage.js +12 -0
  39. package/framework/templates/ErrorPage.tsx +137 -0
  40. package/framework/templates/HomePage.tsx +325 -0
  41. package/framework/templates/NotFoundPage.tsx +109 -0
  42. package/framework/templates/arcanajs.png +0 -0
  43. package/framework/templates/arcanajs.svg +12 -0
  44. package/framework/templates/client-index.tsx +7 -0
  45. package/framework/templates/favicon.ico +0 -0
  46. package/framework/templates/globals.css +198 -0
  47. package/framework/templates/package.json +15 -0
  48. package/framework/templates/postcss.config.js +6 -0
  49. package/framework/templates/server-controller-home.ts +7 -0
  50. package/framework/templates/server-index.ts +10 -0
  51. package/framework/templates/server-routes-web.ts +7 -0
  52. 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 TypeScript to provide a seamless development experience.
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 utility-first CSS.
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
 
@@ -1,11 +1,17 @@
1
- import { spawn } from "child_process";
2
- import path from "path";
3
- import webpack from "webpack";
4
- import { createClientConfig, createServerConfig } from "./webpack.config";
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const child_process_1 = require("child_process");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const webpack_1 = __importDefault(require("webpack"));
10
+ const webpack_config_1 = require("./webpack.config");
5
11
  const args = process.argv.slice(2);
6
12
  const command = args[0];
7
13
  if (!command) {
8
- console.error("Please specify a command: dev, build, start");
14
+ console.error("Please specify a command: init, dev, build, start");
9
15
  process.exit(1);
10
16
  }
11
17
  const runCompiler = (compiler) => {
@@ -29,8 +35,8 @@ const startDevServer = () => {
29
35
  if (serverProcess) {
30
36
  serverProcess.kill();
31
37
  }
32
- const serverPath = path.resolve(process.cwd(), "dist/server.js");
33
- serverProcess = spawn("node", [serverPath], { stdio: "inherit" });
38
+ const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
39
+ serverProcess = (0, child_process_1.spawn)("node", [serverPath], { stdio: "inherit" });
34
40
  serverProcess.on("close", (code) => {
35
41
  if (code !== 0 && code !== null) {
36
42
  console.error(`Dev server exited with code ${code}`);
@@ -52,11 +58,11 @@ const watchCompiler = (compiler, onBuildComplete) => {
52
58
  const build = async () => {
53
59
  process.env.NODE_ENV = "production";
54
60
  console.log("Building for production...");
55
- const clientConfig = createClientConfig();
56
- const serverConfig = createServerConfig();
61
+ const clientConfig = (0, webpack_config_1.createClientConfig)();
62
+ const serverConfig = (0, webpack_config_1.createServerConfig)();
57
63
  try {
58
- await runCompiler(webpack(clientConfig));
59
- await runCompiler(webpack(serverConfig));
64
+ await runCompiler((0, webpack_1.default)(clientConfig));
65
+ await runCompiler((0, webpack_1.default)(serverConfig));
60
66
  console.log("Build complete.");
61
67
  }
62
68
  catch (error) {
@@ -67,26 +73,113 @@ const build = async () => {
67
73
  const dev = async () => {
68
74
  process.env.NODE_ENV = "development";
69
75
  console.log("Starting development server...");
70
- const clientConfig = createClientConfig();
71
- const serverConfig = createServerConfig();
76
+ const clientConfig = (0, webpack_config_1.createClientConfig)();
77
+ const serverConfig = (0, webpack_config_1.createServerConfig)();
72
78
  // Watch client
73
- watchCompiler(webpack(clientConfig));
79
+ watchCompiler((0, webpack_1.default)(clientConfig));
74
80
  // Watch server and restart on build
75
- watchCompiler(webpack(serverConfig), () => {
81
+ watchCompiler((0, webpack_1.default)(serverConfig), () => {
76
82
  console.log("Server build complete. Restarting server...");
77
83
  startDevServer();
78
84
  });
79
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
+ };
80
170
  const start = () => {
81
171
  process.env.NODE_ENV = "production";
82
- const serverPath = path.resolve(process.cwd(), "dist/server.js");
172
+ const serverPath = path_1.default.resolve(process.cwd(), "dist/server.js");
83
173
  console.log(`Starting server at ${serverPath}...`);
84
- const child = spawn("node", [serverPath], { stdio: "inherit" });
174
+ const child = (0, child_process_1.spawn)("node", [serverPath], { stdio: "inherit" });
85
175
  child.on("close", (code) => {
86
176
  process.exit(code || 0);
87
177
  });
88
178
  };
89
179
  switch (command) {
180
+ case "init":
181
+ init();
182
+ break;
90
183
  case "build":
91
184
  build();
92
185
  break;
@@ -1,42 +1,65 @@
1
- import { CleanWebpackPlugin } from "clean-webpack-plugin";
2
- import HtmlWebpackPlugin from "html-webpack-plugin";
3
- import MiniCssExtractPlugin from "mini-css-extract-plugin";
4
- import path from "path";
5
- import nodeExternals from "webpack-node-externals";
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createServerConfig = exports.createClientConfig = void 0;
7
+ const clean_webpack_plugin_1 = require("clean-webpack-plugin");
8
+ const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin"));
9
+ const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const webpack_node_externals_1 = __importDefault(require("webpack-node-externals"));
6
12
  const cwd = process.cwd();
7
13
  // Helper to resolve loaders from the framework's node_modules
8
14
  const resolveLoader = (loader) => require.resolve(loader);
9
- import fs from "fs";
15
+ const fs_1 = __importDefault(require("fs"));
10
16
  // Helper to find entry file with supported extensions
11
17
  const findEntry = (searchPaths) => {
12
18
  const extensions = [".ts", ".tsx", ".js", ".jsx"];
13
19
  for (const basePath of searchPaths) {
14
20
  for (const ext of extensions) {
15
- const fullPath = path.resolve(cwd, basePath + ext);
16
- if (fs.existsSync(fullPath)) {
21
+ const fullPath = path_1.default.resolve(cwd, basePath + ext);
22
+ if (fs_1.default.existsSync(fullPath)) {
17
23
  return fullPath;
18
24
  }
19
25
  // Also check for index files in directories
20
- const indexPath = path.resolve(cwd, basePath, "index" + ext);
21
- if (fs.existsSync(indexPath)) {
26
+ const indexPath = path_1.default.resolve(cwd, basePath, "index" + ext);
27
+ if (fs_1.default.existsSync(indexPath)) {
22
28
  return indexPath;
23
29
  }
24
30
  }
25
31
  }
26
32
  // Fallback to example if not found (for internal framework dev) or throw error
27
33
  // For now, we'll try the example paths as a last resort before failing
28
- const exampleClient = path.resolve(cwd, "src/example/client/index.tsx");
29
- const exampleServer = path.resolve(cwd, "src/example/server/index.ts");
34
+ const exampleClient = path_1.default.resolve(cwd, "src/example/client/index.tsx");
35
+ const exampleServer = path_1.default.resolve(cwd, "src/example/server/index.ts");
30
36
  if (searchPaths.some((p) => p.includes("client")) &&
31
- fs.existsSync(exampleClient))
37
+ fs_1.default.existsSync(exampleClient))
32
38
  return exampleClient;
33
39
  if (searchPaths.some((p) => p.includes("server")) &&
34
- fs.existsSync(exampleServer))
40
+ fs_1.default.existsSync(exampleServer))
35
41
  return exampleServer;
36
42
  throw new Error(`Could not find entry point. Searched in: ${searchPaths.join(", ")}`);
37
43
  };
38
- export const createClientConfig = () => {
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
+ };
60
+ const createClientConfig = () => {
39
61
  const isProduction = process.env.NODE_ENV === "production";
62
+ const viewsLoaderPath = getViewsLoaderPath();
40
63
  const clientEntry = findEntry([
41
64
  "src/client",
42
65
  "src/client/index",
@@ -50,7 +73,7 @@ export const createClientConfig = () => {
50
73
  client: clientEntry,
51
74
  },
52
75
  output: {
53
- path: path.resolve(cwd, "dist/public"),
76
+ path: path_1.default.resolve(cwd, "dist/public"),
54
77
  filename: isProduction
55
78
  ? "[name].[contenthash].bundle.js"
56
79
  : "[name].bundle.js",
@@ -59,9 +82,12 @@ export const createClientConfig = () => {
59
82
  },
60
83
  resolve: {
61
84
  extensions: [".ts", ".tsx", ".js", ".jsx"],
85
+ alias: {
86
+ "arcana-views": viewsLoaderPath,
87
+ },
62
88
  },
63
89
  resolveLoader: {
64
- modules: ["node_modules", path.resolve(__dirname, "../../node_modules")],
90
+ modules: ["node_modules", path_1.default.resolve(__dirname, "../../node_modules")],
65
91
  },
66
92
  module: {
67
93
  rules: [
@@ -86,10 +112,22 @@ export const createClientConfig = () => {
86
112
  test: /\.css$/,
87
113
  use: [
88
114
  isProduction
89
- ? MiniCssExtractPlugin.loader
115
+ ? mini_css_extract_plugin_1.default.loader
90
116
  : resolveLoader("style-loader"),
91
- resolveLoader("css-loader"),
92
- resolveLoader("postcss-loader"),
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
+ },
93
131
  ],
94
132
  },
95
133
  {
@@ -99,9 +137,9 @@ export const createClientConfig = () => {
99
137
  ],
100
138
  },
101
139
  plugins: [
102
- new CleanWebpackPlugin(),
103
- new HtmlWebpackPlugin({
104
- template: path.resolve(__dirname, "../lib/server/default-index.html"),
140
+ new clean_webpack_plugin_1.CleanWebpackPlugin(),
141
+ new html_webpack_plugin_1.default({
142
+ template: path_1.default.resolve(__dirname, "../lib/server/default-index.html"),
105
143
  filename: "index.html",
106
144
  inject: "body",
107
145
  minify: isProduction
@@ -119,7 +157,7 @@ export const createClientConfig = () => {
119
157
  }
120
158
  : false,
121
159
  }),
122
- new MiniCssExtractPlugin({
160
+ new mini_css_extract_plugin_1.default({
123
161
  filename: isProduction ? "[name].[contenthash].css" : "[name].css",
124
162
  }),
125
163
  ],
@@ -138,37 +176,25 @@ export const createClientConfig = () => {
138
176
  devtool: isProduction ? "source-map" : "eval-source-map",
139
177
  };
140
178
  };
141
- export const createServerConfig = () => {
179
+ exports.createClientConfig = createClientConfig;
180
+ const createServerConfig = () => {
142
181
  const isProduction = process.env.NODE_ENV === "production";
143
182
  const serverEntry = findEntry([
144
183
  "src/server",
145
184
  "src/server/index",
146
185
  "src/server/main",
147
186
  ]);
148
- // View Injection Logic
149
- const viewsDir = path.resolve(cwd, "src/views");
150
- const hasViews = fs.existsSync(viewsDir);
151
- const viewsLoaderPath = path.resolve(__dirname, "../../node_modules/.cache/arcanajs/views-loader.js");
152
- // Ensure cache directory exists
153
- const cacheDir = path.dirname(viewsLoaderPath);
154
- if (!fs.existsSync(cacheDir)) {
155
- fs.mkdirSync(cacheDir, { recursive: true });
156
- }
157
- // Generate the loader file
158
- const loaderContent = hasViews
159
- ? `module.exports = require.context('${viewsDir}', true, /\\.(tsx|jsx)$/);`
160
- : `module.exports = null;`;
161
- fs.writeFileSync(viewsLoaderPath, loaderContent);
187
+ const viewsLoaderPath = getViewsLoaderPath();
162
188
  return {
163
189
  mode: isProduction ? "production" : "development",
164
190
  target: "node",
165
191
  entry: serverEntry,
166
192
  output: {
167
- path: path.resolve(cwd, "dist"),
193
+ path: path_1.default.resolve(cwd, "dist"),
168
194
  filename: "server.js",
169
195
  },
170
196
  externals: [
171
- nodeExternals({
197
+ (0, webpack_node_externals_1.default)({
172
198
  allowlist: [/^arcanajs/],
173
199
  }),
174
200
  ],
@@ -179,7 +205,7 @@ export const createServerConfig = () => {
179
205
  },
180
206
  },
181
207
  resolveLoader: {
182
- modules: ["node_modules", path.resolve(__dirname, "../../node_modules")],
208
+ modules: ["node_modules", path_1.default.resolve(__dirname, "../../node_modules")],
183
209
  },
184
210
  module: {
185
211
  rules: [
@@ -216,3 +242,4 @@ export const createServerConfig = () => {
216
242
  // devtool: isProduction ? "source-map" : "eval-source-map",
217
243
  };
218
244
  };
245
+ exports.createServerConfig = createServerConfig;
@@ -1,8 +1,16 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { hydrateRoot } from "react-dom/client";
3
- import { HeadContext } from "../shared/context/HeadContext";
4
- import { ArcanaJSApp } from "../shared/core/ArcanaJSApp";
5
- export const hydrateArcanaJS = (viewsOrContext, layout) => {
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.hydrateArcanaJS = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const client_1 = require("react-dom/client");
9
+ const HeadContext_1 = require("../shared/context/HeadContext");
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"));
13
+ const hydrateArcanaJS = (viewsOrContext, layout) => {
6
14
  let views = {};
7
15
  if (viewsOrContext.keys && typeof viewsOrContext.keys === "function") {
8
16
  viewsOrContext.keys().forEach((key) => {
@@ -13,6 +21,13 @@ export const hydrateArcanaJS = (viewsOrContext, layout) => {
13
21
  else {
14
22
  views = viewsOrContext;
15
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
+ }
16
31
  const container = document.getElementById("root");
17
32
  const dataScript = document.getElementById("__ArcanaJS_DATA__");
18
33
  // Client-side HeadManager (noop for push, as Head handles client updates via useEffect)
@@ -23,10 +38,11 @@ export const hydrateArcanaJS = (viewsOrContext, layout) => {
23
38
  if (container && dataScript) {
24
39
  try {
25
40
  const { page, data, params, csrfToken } = JSON.parse(dataScript.textContent || "{}");
26
- hydrateRoot(container, _jsx(HeadContext.Provider, { value: headManager, children: _jsx(ArcanaJSApp, { initialPage: page, initialData: data, initialParams: params, csrfToken: csrfToken, views: views, layout: layout }) }));
41
+ (0, client_1.hydrateRoot)(container, (0, jsx_runtime_1.jsx)(HeadContext_1.HeadContext.Provider, { value: headManager, children: (0, jsx_runtime_1.jsx)(ArcanaJSApp_1.ArcanaJSApp, { initialPage: page, initialData: data, initialParams: params, csrfToken: csrfToken, views: views, layout: layout }) }));
27
42
  }
28
43
  catch (e) {
29
44
  console.error("Failed to parse initial data", e);
30
45
  }
31
46
  }
32
47
  };
48
+ exports.hydrateArcanaJS = hydrateArcanaJS;
@@ -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";