Initial commit
This commit is contained in:
172
js/loader.js
Normal file
172
js/loader.js
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* On-demand page loader for Babel/JSX scripts.
|
||||
*
|
||||
* Instead of loading ALL pages as <script type="text/babel"> upfront
|
||||
* (which forces Babel to parse ~580KB of JSX at once), this loader
|
||||
* fetches individual page scripts only when the user navigates to them,
|
||||
* then transforms with Babel and evaluates the result.
|
||||
*
|
||||
* Must be loaded as a plain <script> BEFORE any page scripts.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.GDD = window.GDD || {};
|
||||
|
||||
var _cache = {}; // pageId -> true (loaded)
|
||||
var _loading = {}; // pageId -> Promise
|
||||
var _scriptCache = {}; // src -> transformed code
|
||||
|
||||
var PAGE_SCRIPTS = {
|
||||
'overview': 'js/pages/overview.js',
|
||||
'architecture': 'js/pages/architecture.js',
|
||||
'techstack': 'js/pages/techstack.js',
|
||||
'backend': 'js/pages/backend.js',
|
||||
'agents': 'js/pages/agents.js',
|
||||
'gameplay': 'js/pages/gameplay.js',
|
||||
'ships': 'js/pages/ships.js',
|
||||
'economy': 'js/pages/economy.js',
|
||||
'social': 'js/pages/social.js',
|
||||
'ship-ai': 'js/pages/ship-ai.js',
|
||||
'roadmap': 'js/pages/roadmap.js',
|
||||
'risks': 'js/pages/risks.js',
|
||||
'demo-gallery': 'js/pages/demo-gallery.js',
|
||||
'demo-starmap': 'js/demos/starmap.js',
|
||||
'demo-movement': 'js/demos/movement.js',
|
||||
'demo-combat': 'js/demos/combat.js',
|
||||
'demo-market': 'js/demos/market.js',
|
||||
'demo-fitting': 'js/demos/fitting.js',
|
||||
'demo-refining': 'js/demos/refining.js',
|
||||
'demo-progression':'js/demos/progression.js',
|
||||
'demo-bounty': 'js/demos/bounty.js',
|
||||
'demo-gamehud': 'js/demos/gamehud.js',
|
||||
'demo-chat': 'js/demos/chat.js',
|
||||
'demo-zora': 'js/demos/zora.js',
|
||||
'demo-galaxy': 'js/demos/galaxy.js',
|
||||
};
|
||||
|
||||
// Component name map — mirrors the one in app.js
|
||||
var PAGE_COMP_NAMES = {
|
||||
'overview': 'OverviewPage',
|
||||
'architecture': 'ArchitecturePage',
|
||||
'techstack': 'TechStackPage',
|
||||
'backend': 'BackendPage',
|
||||
'agents': 'AgentsPage',
|
||||
'gameplay': 'GameplayPage',
|
||||
'ships': 'ShipsPage',
|
||||
'economy': 'EconomyPage',
|
||||
'social': 'SocialPage',
|
||||
'ship-ai': 'ShipAIPage',
|
||||
'roadmap': 'RoadmapPage',
|
||||
'risks': 'RisksPage',
|
||||
'demo-gallery': 'DemoGalleryPage',
|
||||
'demo-starmap': 'StarMapDemo',
|
||||
'demo-movement': 'ShipMovementDemo',
|
||||
'demo-combat': 'CombatDemo',
|
||||
'demo-market': 'MarketDemo',
|
||||
'demo-fitting': 'FittingDemo',
|
||||
'demo-refining': 'RefiningDemo',
|
||||
'demo-progression':'ProgressionDemo',
|
||||
'demo-bounty': 'BountyDemo',
|
||||
'demo-gamehud': 'GameHudDemo',
|
||||
'demo-chat': 'ChatDemo',
|
||||
'demo-zora': 'ZoraDemo',
|
||||
'demo-galaxy': 'GalaxyDemo',
|
||||
};
|
||||
|
||||
var _isFileProtocol = (window.location.protocol === 'file:');
|
||||
|
||||
function fetchAndTransform(src) {
|
||||
// Check memory cache first
|
||||
if (_scriptCache[src]) {
|
||||
return Promise.resolve(_scriptCache[src]);
|
||||
}
|
||||
|
||||
// On file:// protocol, fetch() is blocked by the browser.
|
||||
// Try injecting a <script type="text/babel" src> tag instead —
|
||||
// Babel standalone's MutationObserver will pick it up.
|
||||
if (_isFileProtocol) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
// Check if component appeared (Babel may have processed it
|
||||
// from a previously injected tag)
|
||||
var compName = PAGE_COMP_NAMES[Object.keys(PAGE_SCRIPTS).filter(
|
||||
function(k) { return PAGE_SCRIPTS[k] === src; }
|
||||
)[0]];
|
||||
if (compName && window.GDD[compName]) {
|
||||
resolve('');
|
||||
return;
|
||||
}
|
||||
reject(new Error(
|
||||
'Cannot load page from file:// protocol. ' +
|
||||
'Serve this folder via HTTP for full navigation. ' +
|
||||
'Run: python3 -m http.server 8000 (then open http://localhost:8000)'
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
return fetch(src)
|
||||
.then(function(r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status + ' loading ' + src);
|
||||
return r.text();
|
||||
})
|
||||
.then(function(code) {
|
||||
// Transform JSX → JS using Babel standalone
|
||||
var transformed = Babel.transform(code, {
|
||||
presets: ['react'],
|
||||
plugins: [],
|
||||
}).code;
|
||||
|
||||
_scriptCache[src] = transformed;
|
||||
return transformed;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a page script on demand. Returns a Promise.
|
||||
*/
|
||||
window.GDD.loadPage = function loadPage(pageId) {
|
||||
if (_cache[pageId]) return Promise.resolve();
|
||||
if (_loading[pageId]) return _loading[pageId];
|
||||
|
||||
// Check if component was already registered by an inline <script type="text/babel">
|
||||
var compName = PAGE_COMP_NAMES[pageId];
|
||||
if (compName && window.GDD[compName]) {
|
||||
_cache[pageId] = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var src = PAGE_SCRIPTS[pageId];
|
||||
if (!src) return Promise.resolve();
|
||||
|
||||
var promise = fetchAndTransform(src)
|
||||
.then(function(code) {
|
||||
// Execute in global scope so window.GDD assignments work
|
||||
(0, eval)(code);
|
||||
_cache[pageId] = true;
|
||||
delete _loading[pageId];
|
||||
})
|
||||
.catch(function(err) {
|
||||
delete _loading[pageId];
|
||||
console.error('[loader] Failed to load page:', pageId, err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
_loading[pageId] = promise;
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload a page in the background (no-op if already loaded/loading).
|
||||
*/
|
||||
window.GDD.preloadPage = function preloadPage(pageId) {
|
||||
if (_cache[pageId] || _loading[pageId]) return;
|
||||
setTimeout(function() { window.GDD.loadPage(pageId); }, 200);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a page is loaded and its component is available.
|
||||
*/
|
||||
window.GDD.isPageLoaded = function isPageLoaded(pageId) {
|
||||
return !!_cache[pageId];
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user