@flexireact/core 1.0.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.
@@ -0,0 +1,204 @@
1
+ /**
2
+ * FlexiReact Client Navigation
3
+ * Client-side navigation with prefetching
4
+ */
5
+
6
+ import React from 'react';
7
+
8
+ // Navigation state
9
+ const navigationState = {
10
+ listeners: new Set(),
11
+ prefetched: new Set()
12
+ };
13
+
14
+ /**
15
+ * Navigates to a new URL
16
+ */
17
+ export function navigate(url, options = {}) {
18
+ const { replace = false, scroll = true } = options;
19
+
20
+ if (replace) {
21
+ window.history.replaceState({}, '', url);
22
+ } else {
23
+ window.history.pushState({}, '', url);
24
+ }
25
+
26
+ // Dispatch navigation event
27
+ window.dispatchEvent(new CustomEvent('flexi:navigate', {
28
+ detail: { url, replace, scroll }
29
+ }));
30
+
31
+ // Scroll to top if needed
32
+ if (scroll) {
33
+ window.scrollTo(0, 0);
34
+ }
35
+
36
+ // Notify listeners
37
+ navigationState.listeners.forEach(listener => listener(url));
38
+
39
+ // Fetch and render new page
40
+ return fetchAndRender(url);
41
+ }
42
+
43
+ /**
44
+ * Prefetches a URL for faster navigation
45
+ */
46
+ export function prefetch(url) {
47
+ if (navigationState.prefetched.has(url)) {
48
+ return Promise.resolve();
49
+ }
50
+
51
+ navigationState.prefetched.add(url);
52
+
53
+ // Create a link element for prefetching
54
+ const link = document.createElement('link');
55
+ link.rel = 'prefetch';
56
+ link.href = url;
57
+ document.head.appendChild(link);
58
+
59
+ return Promise.resolve();
60
+ }
61
+
62
+ /**
63
+ * Fetches and renders a new page
64
+ */
65
+ async function fetchAndRender(url) {
66
+ try {
67
+ const response = await fetch(url, {
68
+ headers: {
69
+ 'X-Flexi-Navigation': 'true'
70
+ }
71
+ });
72
+
73
+ if (!response.ok) {
74
+ throw new Error(`Navigation failed: ${response.status}`);
75
+ }
76
+
77
+ const html = await response.text();
78
+
79
+ // Parse the HTML
80
+ const parser = new DOMParser();
81
+ const doc = parser.parseFromString(html, 'text/html');
82
+
83
+ // Update the page content
84
+ const newRoot = doc.getElementById('root');
85
+ const currentRoot = document.getElementById('root');
86
+
87
+ if (newRoot && currentRoot) {
88
+ currentRoot.innerHTML = newRoot.innerHTML;
89
+ }
90
+
91
+ // Update the title
92
+ document.title = doc.title;
93
+
94
+ // Update meta tags
95
+ updateMetaTags(doc);
96
+
97
+ // Re-hydrate islands
98
+ window.dispatchEvent(new CustomEvent('flexi:pageload'));
99
+
100
+ } catch (error) {
101
+ console.error('Navigation error:', error);
102
+ // Fallback to full page navigation
103
+ window.location.href = url;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Updates meta tags from new document
109
+ */
110
+ function updateMetaTags(doc) {
111
+ // Remove old meta tags
112
+ document.querySelectorAll('meta[data-flexi]').forEach(el => el.remove());
113
+
114
+ // Add new meta tags
115
+ doc.querySelectorAll('meta').forEach(meta => {
116
+ if (meta.name || meta.property) {
117
+ const newMeta = meta.cloneNode(true);
118
+ newMeta.setAttribute('data-flexi', 'true');
119
+ document.head.appendChild(newMeta);
120
+ }
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Link component for client-side navigation
126
+ */
127
+ export function Link({ href, children, prefetch: shouldPrefetch = true, replace = false, className, ...props }) {
128
+ const handleClick = (e) => {
129
+ // Allow normal navigation for external links or modified clicks
130
+ if (
131
+ e.ctrlKey ||
132
+ e.metaKey ||
133
+ e.shiftKey ||
134
+ e.button !== 0 ||
135
+ href.startsWith('http') ||
136
+ href.startsWith('//')
137
+ ) {
138
+ return;
139
+ }
140
+
141
+ e.preventDefault();
142
+ navigate(href, { replace });
143
+ };
144
+
145
+ const handleMouseEnter = () => {
146
+ if (shouldPrefetch) {
147
+ prefetch(href);
148
+ }
149
+ };
150
+
151
+ return React.createElement('a', {
152
+ href,
153
+ onClick: handleClick,
154
+ onMouseEnter: handleMouseEnter,
155
+ className,
156
+ ...props
157
+ }, children);
158
+ }
159
+
160
+ /**
161
+ * Hook to listen for navigation events
162
+ */
163
+ export function useNavigation() {
164
+ const [pathname, setPathname] = React.useState(
165
+ typeof window !== 'undefined' ? window.location.pathname : '/'
166
+ );
167
+
168
+ React.useEffect(() => {
169
+ const handleNavigation = (url) => {
170
+ setPathname(new URL(url, window.location.origin).pathname);
171
+ };
172
+
173
+ navigationState.listeners.add(handleNavigation);
174
+
175
+ const handlePopState = () => {
176
+ setPathname(window.location.pathname);
177
+ navigationState.listeners.forEach(listener => listener(window.location.pathname));
178
+ };
179
+
180
+ window.addEventListener('popstate', handlePopState);
181
+
182
+ return () => {
183
+ navigationState.listeners.delete(handleNavigation);
184
+ window.removeEventListener('popstate', handlePopState);
185
+ };
186
+ }, []);
187
+
188
+ return { pathname, navigate, prefetch };
189
+ }
190
+
191
+ // Setup popstate listener
192
+ if (typeof window !== 'undefined') {
193
+ window.addEventListener('popstate', () => {
194
+ const url = window.location.pathname + window.location.search;
195
+ navigationState.listeners.forEach(listener => listener(url));
196
+ });
197
+ }
198
+
199
+ export default {
200
+ navigate,
201
+ prefetch,
202
+ Link,
203
+ useNavigation
204
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * FlexiReact Client Runtime
3
+ * Main entry point for client-side JavaScript
4
+ */
5
+
6
+ import { hydrateAllIslands, setupProgressiveHydration } from './hydration.js';
7
+ import { navigate, prefetch } from './navigation.js';
8
+
9
+ // Expose to global scope
10
+ window.FlexiReact = {
11
+ navigate,
12
+ prefetch,
13
+ hydrateAllIslands,
14
+ setupProgressiveHydration
15
+ };
16
+
17
+ // Auto-setup on page load
18
+ document.addEventListener('DOMContentLoaded', () => {
19
+ // Setup progressive hydration for islands
20
+ if (window.__FLEXI_DATA__?.islands) {
21
+ setupProgressiveHydration(window.__FLEXI_DATA__.islands);
22
+ }
23
+
24
+ // Dispatch ready event
25
+ window.dispatchEvent(new CustomEvent('flexi:ready'));
26
+ });
27
+
28
+ // Handle page transitions
29
+ window.addEventListener('flexi:pageload', () => {
30
+ // Re-setup hydration after navigation
31
+ if (window.__FLEXI_DATA__?.islands) {
32
+ setupProgressiveHydration(window.__FLEXI_DATA__.islands);
33
+ }
34
+ });
35
+
36
+ console.log('⚡ FlexiReact v2 client runtime loaded');
package/core/config.js ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * FlexiReact Configuration System
3
+ * Handles loading and merging of configuration from flexireact.config.js
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { pathToFileURL } from 'url';
9
+
10
+ // Default configuration
11
+ export const defaultConfig = {
12
+ // Directories
13
+ pagesDir: 'pages',
14
+ layoutsDir: 'layouts',
15
+ publicDir: 'public',
16
+ outDir: '.flexi',
17
+
18
+ // Build options
19
+ build: {
20
+ target: 'es2022',
21
+ minify: true,
22
+ sourcemap: true,
23
+ splitting: true
24
+ },
25
+
26
+ // Server options
27
+ server: {
28
+ port: 3000,
29
+ host: 'localhost'
30
+ },
31
+
32
+ // SSG options
33
+ ssg: {
34
+ enabled: false,
35
+ paths: []
36
+ },
37
+
38
+ // Islands (partial hydration)
39
+ islands: {
40
+ enabled: true
41
+ },
42
+
43
+ // RSC options
44
+ rsc: {
45
+ enabled: true
46
+ },
47
+
48
+ // Plugins
49
+ plugins: [],
50
+
51
+ // Styles (CSS files to include)
52
+ styles: [],
53
+
54
+ // Scripts (JS files to include)
55
+ scripts: [],
56
+
57
+ // Favicon path
58
+ favicon: null
59
+ };
60
+
61
+ /**
62
+ * Loads configuration from the project root
63
+ * @param {string} projectRoot - Path to project root
64
+ * @returns {Object} Merged configuration
65
+ */
66
+ export async function loadConfig(projectRoot) {
67
+ const configPath = path.join(projectRoot, 'flexireact.config.js');
68
+
69
+ let userConfig = {};
70
+
71
+ if (fs.existsSync(configPath)) {
72
+ try {
73
+ const configUrl = pathToFileURL(configPath).href;
74
+ const module = await import(`${configUrl}?t=${Date.now()}`);
75
+ userConfig = module.default || module;
76
+ } catch (error) {
77
+ console.warn('Warning: Failed to load flexireact.config.js:', error.message);
78
+ }
79
+ }
80
+
81
+ // Deep merge configs
82
+ return deepMerge(defaultConfig, userConfig);
83
+ }
84
+
85
+ /**
86
+ * Deep merge two objects
87
+ */
88
+ function deepMerge(target, source) {
89
+ const result = { ...target };
90
+
91
+ for (const key in source) {
92
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
93
+ result[key] = deepMerge(target[key] || {}, source[key]);
94
+ } else {
95
+ result[key] = source[key];
96
+ }
97
+ }
98
+
99
+ return result;
100
+ }
101
+
102
+ /**
103
+ * Resolves all paths in config relative to project root
104
+ */
105
+ export function resolvePaths(config, projectRoot) {
106
+ return {
107
+ ...config,
108
+ pagesDir: path.resolve(projectRoot, config.pagesDir),
109
+ layoutsDir: path.resolve(projectRoot, config.layoutsDir),
110
+ publicDir: path.resolve(projectRoot, config.publicDir),
111
+ outDir: path.resolve(projectRoot, config.outDir)
112
+ };
113
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * FlexiReact Context System
3
+ * Provides request context and shared state for SSR/RSC
4
+ */
5
+
6
+ import React from 'react';
7
+
8
+ // Server-side request context
9
+ export const RequestContext = React.createContext(null);
10
+
11
+ // Route context for nested routes
12
+ export const RouteContext = React.createContext(null);
13
+
14
+ // Layout context
15
+ export const LayoutContext = React.createContext(null);
16
+
17
+ /**
18
+ * Creates a request context value
19
+ */
20
+ export function createRequestContext(req, res, params = {}, query = {}) {
21
+ return {
22
+ req,
23
+ res,
24
+ params,
25
+ query,
26
+ url: req.url,
27
+ method: req.method,
28
+ headers: req.headers,
29
+ cookies: parseCookies(req.headers.cookie || '')
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Parse cookies from header string
35
+ */
36
+ function parseCookies(cookieHeader) {
37
+ const cookies = {};
38
+ if (!cookieHeader) return cookies;
39
+
40
+ cookieHeader.split(';').forEach(cookie => {
41
+ const [name, ...rest] = cookie.split('=');
42
+ if (name) {
43
+ cookies[name.trim()] = rest.join('=').trim();
44
+ }
45
+ });
46
+
47
+ return cookies;
48
+ }
49
+
50
+ /**
51
+ * Hook to access request context (server-side only)
52
+ */
53
+ export function useRequest() {
54
+ const context = React.useContext(RequestContext);
55
+ if (!context) {
56
+ throw new Error('useRequest must be used within a RequestContext provider');
57
+ }
58
+ return context;
59
+ }
60
+
61
+ /**
62
+ * Hook to access route params
63
+ */
64
+ export function useParams() {
65
+ const context = React.useContext(RouteContext);
66
+ return context?.params || {};
67
+ }
68
+
69
+ /**
70
+ * Hook to access query parameters
71
+ */
72
+ export function useQuery() {
73
+ const context = React.useContext(RouteContext);
74
+ return context?.query || {};
75
+ }
76
+
77
+ /**
78
+ * Hook to access current pathname
79
+ */
80
+ export function usePathname() {
81
+ const context = React.useContext(RouteContext);
82
+ return context?.pathname || '/';
83
+ }
package/core/dev.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FlexiReact Dev Server Launcher
5
+ * This script starts the server with the JSX loader enabled
6
+ */
7
+
8
+ import { spawn } from 'child_process';
9
+ import { fileURLToPath, pathToFileURL } from 'url';
10
+ import path from 'path';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const serverPath = path.join(__dirname, 'server.js');
16
+ const loaderPath = path.join(__dirname, 'loader.js');
17
+
18
+ // Convert to file:// URLs for Windows compatibility
19
+ const loaderUrl = pathToFileURL(loaderPath).href;
20
+
21
+ // Start Node.js with the custom loader
22
+ const child = spawn(
23
+ process.execPath,
24
+ [
25
+ '--import',
26
+ `data:text/javascript,import { register } from 'node:module'; register('${loaderUrl.replace(/\\/g, '/')}', import.meta.url);`,
27
+ serverPath
28
+ ],
29
+ {
30
+ stdio: 'inherit',
31
+ cwd: process.cwd(),
32
+ env: process.env
33
+ }
34
+ );
35
+
36
+ child.on('error', (error) => {
37
+ console.error('Failed to start server:', error);
38
+ process.exit(1);
39
+ });
40
+
41
+ child.on('exit', (code) => {
42
+ process.exit(code || 0);
43
+ });
44
+
45
+ // Forward signals to child process
46
+ process.on('SIGINT', () => child.kill('SIGINT'));
47
+ process.on('SIGTERM', () => child.kill('SIGTERM'));
package/core/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * FlexiReact v2 - Main Entry Point
3
+ * A modern React framework with RSC, SSG, Islands, and more
4
+ */
5
+
6
+ // Core exports
7
+ export { loadConfig, defaultConfig, resolvePaths } from './config.js';
8
+ export { createRequestContext, useRequest, useParams, useQuery, usePathname } from './context.js';
9
+ export * from './utils.js';
10
+
11
+ // Router
12
+ export { buildRouteTree, matchRoute, findRouteLayouts, RouteType } from './router/index.js';
13
+
14
+ // Render
15
+ export { renderPage, renderError, renderLoading } from './render/index.js';
16
+
17
+ // Server
18
+ export { createServer } from './server/index.js';
19
+
20
+ // Build
21
+ export { build, buildDev, BuildMode } from './build/index.js';
22
+
23
+ // SSG
24
+ export { generateStaticSite, SSGResult, ISRManager } from './ssg/index.js';
25
+
26
+ // RSC
27
+ export {
28
+ processServerComponent,
29
+ createClientReference,
30
+ serializeRSCPayload,
31
+ createServerAction,
32
+ handleServerAction,
33
+ ServerBoundary,
34
+ ClientBoundary,
35
+ RSC_CONTENT_TYPE
36
+ } from './rsc/index.js';
37
+
38
+ // Islands
39
+ export {
40
+ Island,
41
+ createIsland,
42
+ createLazyIsland,
43
+ getRegisteredIslands,
44
+ generateHydrationScript,
45
+ generateAdvancedHydrationScript,
46
+ LoadStrategy
47
+ } from './islands/index.js';
48
+
49
+ // Middleware
50
+ export {
51
+ MiddlewareRequest,
52
+ MiddlewareResponse,
53
+ loadMiddleware,
54
+ runMiddleware,
55
+ composeMiddleware,
56
+ middlewares
57
+ } from './middleware/index.js';
58
+
59
+ // Plugins
60
+ export {
61
+ PluginManager,
62
+ PluginHooks,
63
+ pluginManager,
64
+ loadPlugins,
65
+ definePlugin,
66
+ builtinPlugins
67
+ } from './plugins/index.js';
68
+
69
+ // Version
70
+ export const VERSION = '2.0.0';
71
+
72
+ // Default export
73
+ export default {
74
+ VERSION,
75
+ createServer
76
+ };