@zenithbuild/core 0.6.1 → 0.6.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenithbuild/core",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Core library for the Zenith framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -59,6 +59,7 @@
59
59
  "typescript": "^5"
60
60
  },
61
61
  "dependencies": {
62
+ "@zenithbuild/router": "latest",
62
63
  "@types/acorn": "^6.0.4",
63
64
  "@types/marked": "^6.0.0",
64
65
  "@types/parse5": "^7.0.0",
@@ -68,4 +69,4 @@
68
69
  "parse5": "^8.0.0",
69
70
  "picocolors": "^1.1.1"
70
71
  }
71
- }
72
+ }
package/router/index.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Zenith Router
3
3
  *
4
- * File-based SPA router for Zenith framework.
5
- * Includes routing, navigation, and ZenLink components.
4
+ * This module re-exports from @zenithbuild/router package.
5
+ *
6
+ * The router has been extracted to its own package for:
7
+ * - Independent versioning
8
+ * - Better separation of concerns
9
+ * - Easier maintenance
6
10
  *
7
11
  * @example
8
12
  * ```ts
@@ -16,61 +20,9 @@
16
20
  * console.log('On blog section')
17
21
  * }
18
22
  * ```
23
+ *
24
+ * @deprecated Import directly from '@zenithbuild/router' for new projects
19
25
  */
20
26
 
21
- // Core router types and utilities
22
- export * from "./types"
23
- export * from "./manifest"
24
-
25
- // Router runtime (core router implementation)
26
- // These are the primary exports for router functionality
27
- export {
28
- initRouter,
29
- resolveRoute,
30
- navigate,
31
- getRoute,
32
- onRouteChange,
33
- beforeEach,
34
- afterEach,
35
- isActive,
36
- prefetch,
37
- isPrefetched
38
- } from "./runtime"
39
-
40
- // Navigation utilities (additional helpers and zen* prefixed exports)
41
- // Note: Some functions like navigate, isActive, prefetch are also in runtime
42
- // We export runtime's versions above, and navigation's unique functions here
43
- export {
44
- // Navigation API (zen* prefixed names)
45
- zenNavigate,
46
- zenBack,
47
- zenForward,
48
- zenGo,
49
- zenIsActive,
50
- zenPrefetch,
51
- zenIsPrefetched,
52
- zenGetRoute,
53
- zenGetParam,
54
- zenGetQuery,
55
- createZenLink,
56
- zenLink,
57
- // Additional navigation utilities (not in runtime)
58
- back,
59
- forward,
60
- go,
61
- getParam,
62
- getQuery,
63
- isExternalUrl,
64
- shouldUseSPANavigation,
65
- normalizePath,
66
- setGlobalTransition,
67
- getGlobalTransition,
68
- createTransitionContext
69
- } from "./navigation/index"
70
-
71
- // Navigation-specific types
72
- export type {
73
- ZenLinkProps,
74
- TransitionContext,
75
- TransitionHandler
76
- } from "./navigation/index"
27
+ // Re-export everything from @zenithbuild/router
28
+ export * from "@zenithbuild/router"
@@ -876,6 +876,320 @@ export function generateBundleJS(): string {
876
876
  global.processRawSections = processRawSections;
877
877
  global.slugify = slugify;
878
878
 
879
+ // ============================================
880
+ // SPA Router Runtime
881
+ // ============================================
882
+
883
+ // Router state
884
+ // Current route state
885
+ var currentRoute = {
886
+ path: '/',
887
+ params: {},
888
+ query: {}
889
+ };
890
+
891
+ // Route listeners
892
+ var routeListeners = new Set();
893
+
894
+ // Router outlet element
895
+ var routerOutlet = null;
896
+
897
+ // Page modules registry
898
+ var pageModules = {};
899
+
900
+ // Route manifest
901
+ var routeManifest = [];
902
+
903
+ /**
904
+ * Parse query string
905
+ */
906
+ function parseQueryString(search) {
907
+ var query = {};
908
+ if (!search || search === '?') return query;
909
+ var params = new URLSearchParams(search);
910
+ params.forEach(function(value, key) { query[key] = value; });
911
+ return query;
912
+ }
913
+
914
+ /**
915
+ * Resolve route from pathname
916
+ */
917
+ function resolveRoute(pathname) {
918
+ var normalizedPath = pathname === '' ? '/' : pathname;
919
+
920
+ for (var i = 0; i < routeManifest.length; i++) {
921
+ var route = routeManifest[i];
922
+ var match = route.regex.exec(normalizedPath);
923
+ if (match) {
924
+ var params = {};
925
+ for (var j = 0; j < route.paramNames.length; j++) {
926
+ var paramValue = match[j + 1];
927
+ if (paramValue !== undefined) {
928
+ params[route.paramNames[j]] = decodeURIComponent(paramValue);
929
+ }
930
+ }
931
+ return { record: route, params: params };
932
+ }
933
+ }
934
+ return null;
935
+ }
936
+
937
+ /**
938
+ * Clean up previous page
939
+ */
940
+ function cleanupPreviousPage() {
941
+ // Trigger unmount lifecycle hooks
942
+ if (global.__zenith && global.__zenith.triggerUnmount) {
943
+ global.__zenith.triggerUnmount();
944
+ }
945
+
946
+ // Remove previous page styles
947
+ var prevStyles = document.querySelectorAll('style[data-zen-page-style]');
948
+ prevStyles.forEach(function(s) { s.remove(); });
949
+
950
+ // Clean up window properties
951
+ if (global.__zenith_cleanup) {
952
+ global.__zenith_cleanup.forEach(function(key) {
953
+ try { delete global[key]; } catch(e) {}
954
+ });
955
+ }
956
+ global.__zenith_cleanup = [];
957
+ }
958
+
959
+ /**
960
+ * Inject styles
961
+ */
962
+ function injectStyles(styles) {
963
+ styles.forEach(function(content, i) {
964
+ var style = document.createElement('style');
965
+ style.setAttribute('data-zen-page-style', String(i));
966
+ style.textContent = content;
967
+ document.head.appendChild(style);
968
+ });
969
+ }
970
+
971
+ /**
972
+ * Execute scripts
973
+ */
974
+ function executeScripts(scripts) {
975
+ scripts.forEach(function(content) {
976
+ try {
977
+ var fn = new Function(content);
978
+ fn();
979
+ } catch (e) {
980
+ console.error('[Zenith Router] Script error:', e);
981
+ }
982
+ });
983
+ }
984
+
985
+ /**
986
+ * Render page
987
+ */
988
+ function renderPage(pageModule) {
989
+ if (!routerOutlet) {
990
+ console.warn('[Zenith Router] No router outlet');
991
+ return;
992
+ }
993
+
994
+ cleanupPreviousPage();
995
+ routerOutlet.innerHTML = pageModule.html;
996
+ injectStyles(pageModule.styles);
997
+ executeScripts(pageModule.scripts);
998
+
999
+ // Trigger mount lifecycle hooks after scripts are executed
1000
+ if (global.__zenith && global.__zenith.triggerMount) {
1001
+ global.__zenith.triggerMount();
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * Notify listeners
1007
+ */
1008
+ function notifyListeners(route, prevRoute) {
1009
+ routeListeners.forEach(function(listener) {
1010
+ try { listener(route, prevRoute); } catch(e) {}
1011
+ });
1012
+ }
1013
+
1014
+ /**
1015
+ * Resolve and render
1016
+ */
1017
+ function resolveAndRender(path, query, updateHistory, replace) {
1018
+ replace = replace || false;
1019
+ var prevRoute = Object.assign({}, currentRoute);
1020
+ var resolved = resolveRoute(path);
1021
+
1022
+ if (resolved) {
1023
+ currentRoute = {
1024
+ path: path,
1025
+ params: resolved.params,
1026
+ query: query,
1027
+ matched: resolved.record
1028
+ };
1029
+
1030
+ var pageModule = pageModules[resolved.record.path];
1031
+ if (pageModule) {
1032
+ renderPage(pageModule);
1033
+ }
1034
+ } else {
1035
+ currentRoute = { path: path, params: {}, query: query, matched: undefined };
1036
+ console.warn('[Zenith Router] No route matched:', path);
1037
+
1038
+ // Render 404 if available
1039
+ if (routerOutlet) {
1040
+ routerOutlet.innerHTML = '<div style="padding: 2rem; text-align: center;"><h1>404</h1><p>Page not found</p></div>';
1041
+ }
1042
+ }
1043
+
1044
+ if (updateHistory) {
1045
+ var url = path + (Object.keys(query).length ? '?' + new URLSearchParams(query) : '');
1046
+ if (replace) {
1047
+ history.replaceState(null, '', url);
1048
+ } else {
1049
+ history.pushState(null, '', url);
1050
+ }
1051
+ }
1052
+
1053
+ notifyListeners(currentRoute, prevRoute);
1054
+ global.__zenith_route = currentRoute;
1055
+ }
1056
+
1057
+ /**
1058
+ * Handle popstate
1059
+ */
1060
+ function handlePopState() {
1061
+ resolveAndRender(
1062
+ location.pathname,
1063
+ parseQueryString(location.search),
1064
+ false,
1065
+ false
1066
+ );
1067
+ }
1068
+
1069
+ /**
1070
+ * Navigate (public API)
1071
+ */
1072
+ function navigate(to, options) {
1073
+ options = options || {};
1074
+ var path, query = {};
1075
+
1076
+ if (to.includes('?')) {
1077
+ var parts = to.split('?');
1078
+ path = parts[0];
1079
+ query = parseQueryString('?' + parts[1]);
1080
+ } else {
1081
+ path = to;
1082
+ }
1083
+
1084
+ if (!path.startsWith('/')) {
1085
+ var currentDir = currentRoute.path.split('/').slice(0, -1).join('/');
1086
+ path = currentDir + '/' + path;
1087
+ }
1088
+
1089
+ var normalizedPath = path === '' ? '/' : path;
1090
+ var currentPath = currentRoute.path === '' ? '/' : currentRoute.path;
1091
+ var isSamePath = normalizedPath === currentPath;
1092
+
1093
+ if (isSamePath && JSON.stringify(query) === JSON.stringify(currentRoute.query)) {
1094
+ return;
1095
+ }
1096
+
1097
+ // Dev mode: If no route manifest is loaded, use browser navigation
1098
+ // This allows ZenLink to work in dev server where pages are served fresh
1099
+ if (routeManifest.length === 0) {
1100
+ var url = normalizedPath + (Object.keys(query).length ? '?' + new URLSearchParams(query) : '');
1101
+ if (options.replace) {
1102
+ location.replace(url);
1103
+ } else {
1104
+ location.href = url;
1105
+ }
1106
+ return;
1107
+ }
1108
+
1109
+ resolveAndRender(path, query, true, options.replace || false);
1110
+ }
1111
+
1112
+ /**
1113
+ * Get current route
1114
+ */
1115
+ function getRoute() {
1116
+ return Object.assign({}, currentRoute);
1117
+ }
1118
+
1119
+ /**
1120
+ * Subscribe to route changes
1121
+ */
1122
+ function onRouteChange(listener) {
1123
+ routeListeners.add(listener);
1124
+ return function() { routeListeners.delete(listener); };
1125
+ }
1126
+
1127
+ /**
1128
+ * Check if path is active
1129
+ */
1130
+ function isActive(path, exact) {
1131
+ if (exact) return currentRoute.path === path;
1132
+ return currentRoute.path.startsWith(path);
1133
+ }
1134
+
1135
+ /**
1136
+ * Prefetch a route
1137
+ */
1138
+ var prefetchedRoutes = new Set();
1139
+ function prefetch(path) {
1140
+ var normalizedPath = path === '' ? '/' : path;
1141
+
1142
+ if (prefetchedRoutes.has(normalizedPath)) {
1143
+ return Promise.resolve();
1144
+ }
1145
+ prefetchedRoutes.add(normalizedPath);
1146
+
1147
+ var resolved = resolveRoute(normalizedPath);
1148
+ if (!resolved) {
1149
+ return Promise.resolve();
1150
+ }
1151
+
1152
+ // In SPA build, all modules are already loaded
1153
+ return Promise.resolve();
1154
+ }
1155
+
1156
+ /**
1157
+ * Initialize router
1158
+ */
1159
+ function initRouter(manifest, modules, outlet) {
1160
+ routeManifest = manifest;
1161
+ Object.assign(pageModules, modules);
1162
+
1163
+ if (outlet) {
1164
+ routerOutlet = typeof outlet === 'string'
1165
+ ? document.querySelector(outlet)
1166
+ : outlet;
1167
+ }
1168
+
1169
+ window.addEventListener('popstate', handlePopState);
1170
+
1171
+ // Initial route resolution
1172
+ resolveAndRender(
1173
+ location.pathname,
1174
+ parseQueryString(location.search),
1175
+ false
1176
+ );
1177
+ }
1178
+
1179
+ // Expose router API globally
1180
+ global.__zenith_router = {
1181
+ navigate: navigate,
1182
+ getRoute: getRoute,
1183
+ onRouteChange: onRouteChange,
1184
+ isActive: isActive,
1185
+ prefetch: prefetch,
1186
+ initRouter: initRouter
1187
+ };
1188
+
1189
+ // Also expose navigate directly for convenience
1190
+ global.navigate = navigate;
1191
+
1192
+
879
1193
  // ============================================
880
1194
  // HMR Client (Development Only)
881
1195
  // ============================================