@limlabs/rex 0.0.1-test.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -0
- package/src/document.js +40 -0
- package/src/index.js +4 -0
- package/src/link.js +42 -0
- package/src/router.js +114 -0
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@limlabs/rex",
|
|
3
|
+
"version": "0.0.1-test.1",
|
|
4
|
+
"description": "Rex - Next.js Pages Router reimplemented in Rust",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.js",
|
|
8
|
+
"./document": "./src/document.js",
|
|
9
|
+
"./link": "./src/link.js",
|
|
10
|
+
"./router": "./src/router.js"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"react": "^18.0.0",
|
|
14
|
+
"react-dom": "^18.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/document.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* rex/document exports
|
|
5
|
+
* These components are meaningful during SSR and provide structure for the HTML document.
|
|
6
|
+
* On the client side, they are essentially pass-throughs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function Html({ children, ...props }) {
|
|
10
|
+
return React.createElement('html', props, children);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Head({ children }) {
|
|
14
|
+
return React.createElement('head', null, children);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Main() {
|
|
18
|
+
// During SSR, this is replaced with the actual page content
|
|
19
|
+
return React.createElement('div', { id: '__rex' });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function NextScript() {
|
|
23
|
+
// During SSR, this is replaced with the actual script tags
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Default document component
|
|
28
|
+
export default function Document() {
|
|
29
|
+
return React.createElement(
|
|
30
|
+
Html,
|
|
31
|
+
null,
|
|
32
|
+
React.createElement(Head, null),
|
|
33
|
+
React.createElement(
|
|
34
|
+
'body',
|
|
35
|
+
null,
|
|
36
|
+
React.createElement(Main, null),
|
|
37
|
+
React.createElement(NextScript, null)
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
}
|
package/src/index.js
ADDED
package/src/link.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { navigateTo } from './router.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* rex/link - Client-side navigation link
|
|
6
|
+
* Renders an <a> tag that intercepts clicks for SPA navigation.
|
|
7
|
+
*/
|
|
8
|
+
export default function Link({ href, children, target, rel, className, style, ...rest }) {
|
|
9
|
+
function handleClick(e) {
|
|
10
|
+
// Don't intercept if:
|
|
11
|
+
// - modifier key is held (user wants new tab)
|
|
12
|
+
// - target is set (external link behavior)
|
|
13
|
+
// - href is external
|
|
14
|
+
if (
|
|
15
|
+
e.metaKey ||
|
|
16
|
+
e.ctrlKey ||
|
|
17
|
+
e.shiftKey ||
|
|
18
|
+
e.altKey ||
|
|
19
|
+
target === '_blank' ||
|
|
20
|
+
(href && (href.startsWith('http://') || href.startsWith('https://')))
|
|
21
|
+
) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
navigateTo(href);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return React.createElement(
|
|
30
|
+
'a',
|
|
31
|
+
{
|
|
32
|
+
href: href,
|
|
33
|
+
onClick: handleClick,
|
|
34
|
+
target: target,
|
|
35
|
+
rel: rel,
|
|
36
|
+
className: className,
|
|
37
|
+
style: style,
|
|
38
|
+
...rest,
|
|
39
|
+
},
|
|
40
|
+
children
|
|
41
|
+
);
|
|
42
|
+
}
|
package/src/router.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rex/router - Client-side router
|
|
3
|
+
* Handles SPA navigation by fetching page data and re-rendering.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
let _initialized = false;
|
|
7
|
+
let _currentPath = null;
|
|
8
|
+
let _buildId = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize the client-side router.
|
|
12
|
+
* Called automatically on page load.
|
|
13
|
+
*/
|
|
14
|
+
export function initializeRouter() {
|
|
15
|
+
if (_initialized) return;
|
|
16
|
+
_initialized = true;
|
|
17
|
+
|
|
18
|
+
_currentPath = window.location.pathname;
|
|
19
|
+
_buildId = window.__REX_BUILD_ID__ || '';
|
|
20
|
+
|
|
21
|
+
// Listen for browser back/forward
|
|
22
|
+
window.addEventListener('popstate', function(event) {
|
|
23
|
+
var path = window.location.pathname;
|
|
24
|
+
if (path !== _currentPath) {
|
|
25
|
+
_currentPath = path;
|
|
26
|
+
loadPage(path, false);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Navigate to a new path via client-side routing.
|
|
33
|
+
*/
|
|
34
|
+
export function navigateTo(path) {
|
|
35
|
+
if (path === _currentPath) return;
|
|
36
|
+
|
|
37
|
+
_currentPath = path;
|
|
38
|
+
window.history.pushState(null, '', path);
|
|
39
|
+
loadPage(path, true);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the current route information (React hook style, but simplified).
|
|
44
|
+
*/
|
|
45
|
+
export function useRouter() {
|
|
46
|
+
return {
|
|
47
|
+
pathname: window.location.pathname,
|
|
48
|
+
query: parseQuery(window.location.search),
|
|
49
|
+
push: navigateTo,
|
|
50
|
+
back: function() { window.history.back(); },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function loadPage(path, isNavigation) {
|
|
55
|
+
try {
|
|
56
|
+
// Fetch page data
|
|
57
|
+
var dataPath = path === '/' ? '/index' : path;
|
|
58
|
+
var dataUrl = '/_rex/data/' + _buildId + dataPath + '.json';
|
|
59
|
+
|
|
60
|
+
var response = await fetch(dataUrl);
|
|
61
|
+
|
|
62
|
+
if (response.status === 404) {
|
|
63
|
+
// Build ID mismatch or route not found - full reload
|
|
64
|
+
window.location.href = path;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error('Failed to fetch page data: ' + response.status);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var data = await response.json();
|
|
73
|
+
|
|
74
|
+
if (data.redirect) {
|
|
75
|
+
navigateTo(data.redirect.destination);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (data.notFound) {
|
|
80
|
+
// Show 404
|
|
81
|
+
window.location.href = path;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// For the prototype, we do a full page reload
|
|
86
|
+
// A full implementation would dynamically import the page module
|
|
87
|
+
// and re-render using window.__REX_ROOT__.render()
|
|
88
|
+
window.location.href = path;
|
|
89
|
+
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('[Rex Router] Navigation error:', err);
|
|
92
|
+
window.location.href = path;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseQuery(search) {
|
|
97
|
+
var query = {};
|
|
98
|
+
if (!search || search.length <= 1) return query;
|
|
99
|
+
var pairs = search.substring(1).split('&');
|
|
100
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
101
|
+
var pair = pairs[i].split('=');
|
|
102
|
+
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
|
|
103
|
+
}
|
|
104
|
+
return query;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Auto-initialize when loaded in browser
|
|
108
|
+
if (typeof window !== 'undefined') {
|
|
109
|
+
if (document.readyState === 'loading') {
|
|
110
|
+
document.addEventListener('DOMContentLoaded', initializeRouter);
|
|
111
|
+
} else {
|
|
112
|
+
initializeRouter();
|
|
113
|
+
}
|
|
114
|
+
}
|