mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-01-19 20:25:01 +00:00
add loading progress support and new indicator
This commit is contained in:
parent
8e6ae456f0
commit
2a730b8a44
38
app/page.tsx
38
app/page.tsx
|
|
@ -26,7 +26,19 @@ function MapInspector() {
|
|||
const [missionName, setMissionName] = useState(
|
||||
searchParams.get("mission") || "TWL2_WoodyMyrk",
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [loadingProgress, setLoadingProgress] = useState(0);
|
||||
const [showLoadingIndicator, setShowLoadingIndicator] = useState(true);
|
||||
const isLoading = loadingProgress < 1;
|
||||
|
||||
// Keep the loading indicator visible briefly after reaching 100%
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setShowLoadingIndicator(true);
|
||||
} else {
|
||||
const timer = setTimeout(() => setShowLoadingIndicator(false), 500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
// For automation, like the t2-maps app!
|
||||
|
|
@ -48,17 +60,31 @@ function MapInspector() {
|
|||
router.replace(`?${params.toString()}`, { scroll: false });
|
||||
}, [missionName, router]);
|
||||
|
||||
const handleLoadingChange = useCallback((loading: boolean) => {
|
||||
setIsLoading(loading);
|
||||
}, []);
|
||||
const handleLoadingChange = useCallback(
|
||||
(_loading: boolean, progress: number = 0) => {
|
||||
setLoadingProgress(progress);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<main>
|
||||
<SettingsProvider>
|
||||
<div id="canvasContainer">
|
||||
{isLoading && (
|
||||
<div id="loadingIndicator" className="LoadingSpinner" />
|
||||
{showLoadingIndicator && (
|
||||
<div id="loadingIndicator" data-complete={!isLoading}>
|
||||
<div className="LoadingSpinner" />
|
||||
<div className="LoadingProgress">
|
||||
<div
|
||||
className="LoadingProgress-bar"
|
||||
style={{ width: `${loadingProgress * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="LoadingProgress-text">
|
||||
{Math.round(loadingProgress * 100)}%
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Canvas shadows frameloop="always">
|
||||
<CamerasProvider>
|
||||
|
|
|
|||
|
|
@ -259,22 +259,64 @@ main {
|
|||
}
|
||||
|
||||
.LoadingSpinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.2);
|
||||
border-top-color: white;
|
||||
border-radius: 50%;
|
||||
animation: LoadingSpinner-spin 1s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes LoadingSpinner-spin {
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#loadingIndicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#loadingIndicator[data-complete="true"] {
|
||||
animation: loadingComplete 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes loadingComplete {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.LoadingProgress {
|
||||
width: 200px;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.LoadingProgress-bar {
|
||||
height: 100%;
|
||||
background: white;
|
||||
border-radius: 2px;
|
||||
transition: width 0.1s ease-out;
|
||||
}
|
||||
|
||||
.LoadingProgress-text {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
html{box-sizing:border-box;background:#000;margin:0;padding:0}*,:before,:after{box-sizing:inherit}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Oxygen,Open Sans,sans-serif;font-size:100%}body{margin:0;padding:0}main{width:100vw;height:100vh}#canvasContainer{z-index:0;position:absolute;top:0;bottom:0;left:0;right:0}#controls{color:#fff;z-index:1;background:rgba(0,0,0,.5);border-radius:0 0 4px;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}.CheckboxField,.Field{align-items:center;gap:6px;display:flex}#fovInput,#speedInput{max-width:80px}.StaticShapeLabel{color:#fff;white-space:nowrap;background:rgba(0,0,0,.5);border-radius:1px;padding:1px 3px;font-size:11px}.StatsPanel{right:0;left:auto!important}.AxisLabel{pointer-events:none;font-size:12px}.AxisLabel[data-axis=x]{color:#f90}.AxisLabel[data-axis=y]{color:#9f0}.AxisLabel[data-axis=z]{color:#09f}.MissionSelect-inputWrapper{align-items:center;display:flex;position:relative}.MissionSelect-shortcut{color:rgba(255,255,255,.6);pointer-events:none;background:rgba(255,255,255,.15);border-radius:3px;padding:1px 4px;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:11px;position:absolute;right:7px}.MissionSelect-input[aria-expanded=true]~.MissionSelect-shortcut{display:none}.MissionSelect-input{color:#fff;background:rgba(0,0,0,.6);border:1px solid rgba(255,255,255,.3);border-radius:3px;outline:none;width:240px;padding:6px 36px 6px 8px;font-size:14px}.MissionSelect-input[aria-expanded=true]{padding-right:8px}.MissionSelect-input:focus{border-color:rgba(255,255,255,.6)}.MissionSelect-input::placeholder{color:#fff;font-weight:600}.MissionSelect-popover{z-index:100;min-width:320px;max-height:var(--popover-available-height,90vh);overscroll-behavior:contain;background:rgba(20,20,20,.95);border:1px solid rgba(255,255,255,.5);border-radius:3px;overflow-y:auto;box-shadow:0 8px 24px rgba(0,0,0,.6)}.MissionSelect-list{padding:4px 0}.MissionSelect-list:has(>.MissionSelect-group:first-child){padding-top:0}.MissionSelect-group{padding-bottom:4px}.MissionSelect-groupLabel{color:#c6caca;z-index:1;background:rgba(58,69,72,.95);border-bottom:1px solid rgba(255,255,255,.3);padding:6px 8px 6px 12px;font-size:13px;font-weight:600;position:-webkit-sticky;position:sticky;top:0}.MissionSelect-group:not(:last-child){border-bottom:1px solid rgba(255,255,255,.3)}.MissionSelect-item{cursor:pointer;border-radius:4px;outline:none;flex-direction:column;gap:1px;margin:4px 4px 0;padding:6px 8px;scroll-margin-top:32px;display:flex}.MissionSelect-list>.MissionSelect-item:first-child{margin-top:0}.MissionSelect-item[data-active-item]{background:rgba(255,255,255,.15)}.MissionSelect-item[aria-selected=true]{background:rgba(100,150,255,.3)}.MissionSelect-itemHeader{align-items:center;gap:6px;display:flex}.MissionSelect-itemName{color:#fff;font-size:14px;font-weight:600}.MissionSelect-itemTypes{gap:3px;display:flex}.MissionSelect-itemType{color:#fff;background:rgba(255,157,0,.4);border-radius:3px;padding:2px 5px;font-size:10px;font-weight:600}.MissionSelect-itemMissionName{color:rgba(255,255,255,.5);font-size:12px}.MissionSelect-noResults{color:rgba(255,255,255,.5);text-align:center;padding:12px 8px;font-size:13px}.LoadingSpinner{pointer-events:none;z-index:1;border:4px solid rgba(255,255,255,.2);border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite LoadingSpinner-spin;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}@keyframes LoadingSpinner-spin{to{transform:translate(-50%,-50%)rotate(360deg)}}
|
||||
html{box-sizing:border-box;background:#000;margin:0;padding:0}*,:before,:after{box-sizing:inherit}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,Oxygen,Open Sans,sans-serif;font-size:100%}body{margin:0;padding:0}main{width:100vw;height:100vh}#canvasContainer{z-index:0;position:absolute;top:0;bottom:0;left:0;right:0}#controls{color:#fff;z-index:1;background:rgba(0,0,0,.5);border-radius:0 0 4px;align-items:center;gap:20px;padding:8px 12px 8px 8px;font-size:13px;display:flex;position:fixed;top:0;left:0}.CheckboxField,.Field{align-items:center;gap:6px;display:flex}#fovInput,#speedInput{max-width:80px}.StaticShapeLabel{color:#fff;white-space:nowrap;background:rgba(0,0,0,.5);border-radius:1px;padding:1px 3px;font-size:11px}.StatsPanel{right:0;left:auto!important}.AxisLabel{pointer-events:none;font-size:12px}.AxisLabel[data-axis=x]{color:#f90}.AxisLabel[data-axis=y]{color:#9f0}.AxisLabel[data-axis=z]{color:#09f}.MissionSelect-inputWrapper{align-items:center;display:flex;position:relative}.MissionSelect-shortcut{color:rgba(255,255,255,.6);pointer-events:none;background:rgba(255,255,255,.15);border-radius:3px;padding:1px 4px;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:11px;position:absolute;right:7px}.MissionSelect-input[aria-expanded=true]~.MissionSelect-shortcut{display:none}.MissionSelect-input{color:#fff;background:rgba(0,0,0,.6);border:1px solid rgba(255,255,255,.3);border-radius:3px;outline:none;width:240px;padding:6px 36px 6px 8px;font-size:14px}.MissionSelect-input[aria-expanded=true]{padding-right:8px}.MissionSelect-input:focus{border-color:rgba(255,255,255,.6)}.MissionSelect-input::placeholder{color:#fff;font-weight:600}.MissionSelect-popover{z-index:100;min-width:320px;max-height:var(--popover-available-height,90vh);overscroll-behavior:contain;background:rgba(20,20,20,.95);border:1px solid rgba(255,255,255,.5);border-radius:3px;overflow-y:auto;box-shadow:0 8px 24px rgba(0,0,0,.6)}.MissionSelect-list{padding:4px 0}.MissionSelect-list:has(>.MissionSelect-group:first-child){padding-top:0}.MissionSelect-group{padding-bottom:4px}.MissionSelect-groupLabel{color:#c6caca;z-index:1;background:rgba(58,69,72,.95);border-bottom:1px solid rgba(255,255,255,.3);padding:6px 8px 6px 12px;font-size:13px;font-weight:600;position:-webkit-sticky;position:sticky;top:0}.MissionSelect-group:not(:last-child){border-bottom:1px solid rgba(255,255,255,.3)}.MissionSelect-item{cursor:pointer;border-radius:4px;outline:none;flex-direction:column;gap:1px;margin:4px 4px 0;padding:6px 8px;scroll-margin-top:32px;display:flex}.MissionSelect-list>.MissionSelect-item:first-child{margin-top:0}.MissionSelect-item[data-active-item]{background:rgba(255,255,255,.15)}.MissionSelect-item[aria-selected=true]{background:rgba(100,150,255,.3)}.MissionSelect-itemHeader{align-items:center;gap:6px;display:flex}.MissionSelect-itemName{color:#fff;font-size:14px;font-weight:600}.MissionSelect-itemTypes{gap:3px;display:flex}.MissionSelect-itemType{color:#fff;background:rgba(255,157,0,.4);border-radius:3px;padding:2px 5px;font-size:10px;font-weight:600}.MissionSelect-itemMissionName{color:rgba(255,255,255,.5);font-size:12px}.MissionSelect-noResults{color:rgba(255,255,255,.5);text-align:center;padding:12px 8px;font-size:13px}.LoadingSpinner{border:4px solid rgba(255,255,255,.2);border-top-color:#fff;border-radius:50%;width:48px;height:48px;animation:1s linear infinite LoadingSpinner-spin}@keyframes LoadingSpinner-spin{to{transform:rotate(360deg)}}#loadingIndicator{pointer-events:none;z-index:1;opacity:.8;flex-direction:column;align-items:center;gap:16px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}#loadingIndicator[data-complete=true]{animation:.3s ease-out forwards loadingComplete}@keyframes loadingComplete{0%{opacity:1}to{opacity:0}}.LoadingProgress{background:rgba(255,255,255,.2);border-radius:2px;width:200px;height:4px;overflow:hidden}.LoadingProgress-bar{background:#fff;border-radius:2px;height:100%;transition:width .1s ease-out}.LoadingProgress-text{color:rgba(255,255,255,.7);font-variant-numeric:tabular-nums;font-size:14px}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,23 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[next]/entry/page-loader.ts { PAGE => \"[project]/node_modules/next/error.js [client] (ecmascript)\" } [client] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
||||
|
||||
const PAGE_PATH = "/_error";
|
||||
(window.__NEXT_P = window.__NEXT_P || []).push([
|
||||
PAGE_PATH,
|
||||
()=>{
|
||||
return __turbopack_context__.r("[project]/node_modules/next/error.js [client] (ecmascript)");
|
||||
}
|
||||
]);
|
||||
// @ts-expect-error module.hot exists
|
||||
if (module.hot) {
|
||||
// @ts-expect-error module.hot exists
|
||||
module.hot.dispose(function() {
|
||||
window.__NEXT_P.push([
|
||||
PAGE_PATH
|
||||
]);
|
||||
});
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=%5Bnext%5D_entry_page-loader_ts_43b523b5._.js.map
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sections": [
|
||||
{"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["turbopack:///[next]/entry/page-loader.ts"],"sourcesContent":["const PAGE_PATH = \"/_error\";\n\n/// <reference types=\"next/client\" />\n\n// inserted by rust code\ndeclare const PAGE_PATH: string\n\n // Adapted from https://github.com/vercel/next.js/blob/b7f9f1f98fc8ab602e84825105b5727272b72e7d/packages/next/src/build/webpack/loaders/next-client-pages-loader.ts\n;(window.__NEXT_P = window.__NEXT_P || []).push([\n PAGE_PATH,\n () => {\n return require('PAGE')\n },\n])\n// @ts-expect-error module.hot exists\nif (module.hot) {\n // @ts-expect-error module.hot exists\n module.hot.dispose(function () {\n window.__NEXT_P.push([PAGE_PATH])\n })\n}\n"],"names":[],"mappings":"AAAA,MAAM,YAAY;AAQjB,CAAC,OAAO,QAAQ,GAAG,OAAO,QAAQ,IAAI,EAAE,EAAE,IAAI,CAAC;IAC9C;IACA;QACE;IACF;CACD;AACD,qCAAqC;AACrC,IAAI,OAAO,GAAG,EAAE;IACd,qCAAqC;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC;QACjB,OAAO,QAAQ,CAAC,IAAI,CAAC;YAAC;SAAU;IAClC;AACF","ignoreList":[0],"debugId":null}}]
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[next]/entry/page-loader.ts { PAGE => \"[project]/node_modules/next/app.js [client] (ecmascript)\" } [client] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
||||
|
||||
const PAGE_PATH = "/_app";
|
||||
(window.__NEXT_P = window.__NEXT_P || []).push([
|
||||
PAGE_PATH,
|
||||
()=>{
|
||||
return __turbopack_context__.r("[project]/node_modules/next/app.js [client] (ecmascript)");
|
||||
}
|
||||
]);
|
||||
// @ts-expect-error module.hot exists
|
||||
if (module.hot) {
|
||||
// @ts-expect-error module.hot exists
|
||||
module.hot.dispose(function() {
|
||||
window.__NEXT_P.push([
|
||||
PAGE_PATH
|
||||
]);
|
||||
});
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=%5Bnext%5D_entry_page-loader_ts_742e4b53._.js.map
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sections": [
|
||||
{"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["turbopack:///[next]/entry/page-loader.ts"],"sourcesContent":["const PAGE_PATH = \"/_app\";\n\n/// <reference types=\"next/client\" />\n\n// inserted by rust code\ndeclare const PAGE_PATH: string\n\n // Adapted from https://github.com/vercel/next.js/blob/b7f9f1f98fc8ab602e84825105b5727272b72e7d/packages/next/src/build/webpack/loaders/next-client-pages-loader.ts\n;(window.__NEXT_P = window.__NEXT_P || []).push([\n PAGE_PATH,\n () => {\n return require('PAGE')\n },\n])\n// @ts-expect-error module.hot exists\nif (module.hot) {\n // @ts-expect-error module.hot exists\n module.hot.dispose(function () {\n window.__NEXT_P.push([PAGE_PATH])\n })\n}\n"],"names":[],"mappings":"AAAA,MAAM,YAAY;AAQjB,CAAC,OAAO,QAAQ,GAAG,OAAO,QAAQ,IAAI,EAAE,EAAE,IAAI,CAAC;IAC9C;IACA;QACE;IACF;CACD;AACD,qCAAqC;AACrC,IAAI,OAAO,GAAG,EAAE;IACd,qCAAqC;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC;QACjB,OAAO,QAAQ,CAAC,IAAI,CAAC;YAAC;SAAU;IAClC;AACF","ignoreList":[0],"debugId":null}}]
|
||||
}
|
||||
|
|
@ -1,481 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[turbopack]/browser/dev/hmr-client/hmr-client.ts [client] (ecmascript)", ((__turbopack_context__) => {
|
||||
"use strict";
|
||||
|
||||
/// <reference path="../../../shared/runtime-types.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-globals.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-protocol.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-extensions.ts" />
|
||||
__turbopack_context__.s([
|
||||
"connect",
|
||||
()=>connect,
|
||||
"setHooks",
|
||||
()=>setHooks,
|
||||
"subscribeToUpdate",
|
||||
()=>subscribeToUpdate
|
||||
]);
|
||||
function connect(param) {
|
||||
let { addMessageListener, sendMessage, onUpdateError = console.error } = param;
|
||||
addMessageListener((msg)=>{
|
||||
switch(msg.type){
|
||||
case 'turbopack-connected':
|
||||
handleSocketConnected(sendMessage);
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
if (Array.isArray(msg.data)) {
|
||||
for(let i = 0; i < msg.data.length; i++){
|
||||
handleSocketMessage(msg.data[i]);
|
||||
}
|
||||
} else {
|
||||
handleSocketMessage(msg.data);
|
||||
}
|
||||
applyAggregatedUpdates();
|
||||
} catch (e) {
|
||||
console.warn('[Fast Refresh] performing full reload\n\n' + "Fast Refresh will perform a full reload when you edit a file that's imported by modules outside of the React rendering tree.\n" + 'You might have a file which exports a React component but also exports a value that is imported by a non-React component file.\n' + 'Consider migrating the non-React component export to a separate file and importing it into both files.\n\n' + 'It is also possible the parent component of the component you edited is a class component, which disables Fast Refresh.\n' + 'Fast Refresh requires at least one parent function component in your React tree.');
|
||||
onUpdateError(e);
|
||||
location.reload();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
const queued = globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS;
|
||||
if (queued != null && !Array.isArray(queued)) {
|
||||
throw new Error('A separate HMR handler was already registered');
|
||||
}
|
||||
globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS = {
|
||||
push: (param)=>{
|
||||
let [chunkPath, callback] = param;
|
||||
subscribeToChunkUpdate(chunkPath, sendMessage, callback);
|
||||
}
|
||||
};
|
||||
if (Array.isArray(queued)) {
|
||||
for (const [chunkPath, callback] of queued){
|
||||
subscribeToChunkUpdate(chunkPath, sendMessage, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
const updateCallbackSets = new Map();
|
||||
function sendJSON(sendMessage, message) {
|
||||
sendMessage(JSON.stringify(message));
|
||||
}
|
||||
function resourceKey(resource) {
|
||||
return JSON.stringify({
|
||||
path: resource.path,
|
||||
headers: resource.headers || null
|
||||
});
|
||||
}
|
||||
function subscribeToUpdates(sendMessage, resource) {
|
||||
sendJSON(sendMessage, {
|
||||
type: 'turbopack-subscribe',
|
||||
...resource
|
||||
});
|
||||
return ()=>{
|
||||
sendJSON(sendMessage, {
|
||||
type: 'turbopack-unsubscribe',
|
||||
...resource
|
||||
});
|
||||
};
|
||||
}
|
||||
function handleSocketConnected(sendMessage) {
|
||||
for (const key of updateCallbackSets.keys()){
|
||||
subscribeToUpdates(sendMessage, JSON.parse(key));
|
||||
}
|
||||
}
|
||||
// we aggregate all pending updates until the issues are resolved
|
||||
const chunkListsWithPendingUpdates = new Map();
|
||||
function aggregateUpdates(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
let aggregated = chunkListsWithPendingUpdates.get(key);
|
||||
if (aggregated) {
|
||||
aggregated.instruction = mergeChunkListUpdates(aggregated.instruction, msg.instruction);
|
||||
} else {
|
||||
chunkListsWithPendingUpdates.set(key, msg);
|
||||
}
|
||||
}
|
||||
function applyAggregatedUpdates() {
|
||||
if (chunkListsWithPendingUpdates.size === 0) return;
|
||||
hooks.beforeRefresh();
|
||||
for (const msg of chunkListsWithPendingUpdates.values()){
|
||||
triggerUpdate(msg);
|
||||
}
|
||||
chunkListsWithPendingUpdates.clear();
|
||||
finalizeUpdate();
|
||||
}
|
||||
function mergeChunkListUpdates(updateA, updateB) {
|
||||
let chunks;
|
||||
if (updateA.chunks != null) {
|
||||
if (updateB.chunks == null) {
|
||||
chunks = updateA.chunks;
|
||||
} else {
|
||||
chunks = mergeChunkListChunks(updateA.chunks, updateB.chunks);
|
||||
}
|
||||
} else if (updateB.chunks != null) {
|
||||
chunks = updateB.chunks;
|
||||
}
|
||||
let merged;
|
||||
if (updateA.merged != null) {
|
||||
if (updateB.merged == null) {
|
||||
merged = updateA.merged;
|
||||
} else {
|
||||
// Since `merged` is an array of updates, we need to merge them all into
|
||||
// one, consistent update.
|
||||
// Since there can only be `EcmascriptMergeUpdates` in the array, there is
|
||||
// no need to key on the `type` field.
|
||||
let update = updateA.merged[0];
|
||||
for(let i = 1; i < updateA.merged.length; i++){
|
||||
update = mergeChunkListEcmascriptMergedUpdates(update, updateA.merged[i]);
|
||||
}
|
||||
for(let i = 0; i < updateB.merged.length; i++){
|
||||
update = mergeChunkListEcmascriptMergedUpdates(update, updateB.merged[i]);
|
||||
}
|
||||
merged = [
|
||||
update
|
||||
];
|
||||
}
|
||||
} else if (updateB.merged != null) {
|
||||
merged = updateB.merged;
|
||||
}
|
||||
return {
|
||||
type: 'ChunkListUpdate',
|
||||
chunks,
|
||||
merged
|
||||
};
|
||||
}
|
||||
function mergeChunkListChunks(chunksA, chunksB) {
|
||||
const chunks = {};
|
||||
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)){
|
||||
const chunkUpdateB = chunksB[chunkPath];
|
||||
if (chunkUpdateB != null) {
|
||||
const mergedUpdate = mergeChunkUpdates(chunkUpdateA, chunkUpdateB);
|
||||
if (mergedUpdate != null) {
|
||||
chunks[chunkPath] = mergedUpdate;
|
||||
}
|
||||
} else {
|
||||
chunks[chunkPath] = chunkUpdateA;
|
||||
}
|
||||
}
|
||||
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)){
|
||||
if (chunks[chunkPath] == null) {
|
||||
chunks[chunkPath] = chunkUpdateB;
|
||||
}
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
function mergeChunkUpdates(updateA, updateB) {
|
||||
if (updateA.type === 'added' && updateB.type === 'deleted' || updateA.type === 'deleted' && updateB.type === 'added') {
|
||||
return undefined;
|
||||
}
|
||||
if (updateA.type === 'partial') {
|
||||
invariant(updateA.instruction, 'Partial updates are unsupported');
|
||||
}
|
||||
if (updateB.type === 'partial') {
|
||||
invariant(updateB.instruction, 'Partial updates are unsupported');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function mergeChunkListEcmascriptMergedUpdates(mergedA, mergedB) {
|
||||
const entries = mergeEcmascriptChunkEntries(mergedA.entries, mergedB.entries);
|
||||
const chunks = mergeEcmascriptChunksUpdates(mergedA.chunks, mergedB.chunks);
|
||||
return {
|
||||
type: 'EcmascriptMergedUpdate',
|
||||
entries,
|
||||
chunks
|
||||
};
|
||||
}
|
||||
function mergeEcmascriptChunkEntries(entriesA, entriesB) {
|
||||
return {
|
||||
...entriesA,
|
||||
...entriesB
|
||||
};
|
||||
}
|
||||
function mergeEcmascriptChunksUpdates(chunksA, chunksB) {
|
||||
if (chunksA == null) {
|
||||
return chunksB;
|
||||
}
|
||||
if (chunksB == null) {
|
||||
return chunksA;
|
||||
}
|
||||
const chunks = {};
|
||||
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)){
|
||||
const chunkUpdateB = chunksB[chunkPath];
|
||||
if (chunkUpdateB != null) {
|
||||
const mergedUpdate = mergeEcmascriptChunkUpdates(chunkUpdateA, chunkUpdateB);
|
||||
if (mergedUpdate != null) {
|
||||
chunks[chunkPath] = mergedUpdate;
|
||||
}
|
||||
} else {
|
||||
chunks[chunkPath] = chunkUpdateA;
|
||||
}
|
||||
}
|
||||
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)){
|
||||
if (chunks[chunkPath] == null) {
|
||||
chunks[chunkPath] = chunkUpdateB;
|
||||
}
|
||||
}
|
||||
if (Object.keys(chunks).length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
function mergeEcmascriptChunkUpdates(updateA, updateB) {
|
||||
if (updateA.type === 'added' && updateB.type === 'deleted') {
|
||||
// These two completely cancel each other out.
|
||||
return undefined;
|
||||
}
|
||||
if (updateA.type === 'deleted' && updateB.type === 'added') {
|
||||
const added = [];
|
||||
const deleted = [];
|
||||
var _updateA_modules;
|
||||
const deletedModules = new Set((_updateA_modules = updateA.modules) !== null && _updateA_modules !== void 0 ? _updateA_modules : []);
|
||||
var _updateB_modules;
|
||||
const addedModules = new Set((_updateB_modules = updateB.modules) !== null && _updateB_modules !== void 0 ? _updateB_modules : []);
|
||||
for (const moduleId of addedModules){
|
||||
if (!deletedModules.has(moduleId)) {
|
||||
added.push(moduleId);
|
||||
}
|
||||
}
|
||||
for (const moduleId of deletedModules){
|
||||
if (!addedModules.has(moduleId)) {
|
||||
deleted.push(moduleId);
|
||||
}
|
||||
}
|
||||
if (added.length === 0 && deleted.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
type: 'partial',
|
||||
added,
|
||||
deleted
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'partial' && updateB.type === 'partial') {
|
||||
var _updateA_added, _updateB_added;
|
||||
const added = new Set([
|
||||
...(_updateA_added = updateA.added) !== null && _updateA_added !== void 0 ? _updateA_added : [],
|
||||
...(_updateB_added = updateB.added) !== null && _updateB_added !== void 0 ? _updateB_added : []
|
||||
]);
|
||||
var _updateA_deleted, _updateB_deleted;
|
||||
const deleted = new Set([
|
||||
...(_updateA_deleted = updateA.deleted) !== null && _updateA_deleted !== void 0 ? _updateA_deleted : [],
|
||||
...(_updateB_deleted = updateB.deleted) !== null && _updateB_deleted !== void 0 ? _updateB_deleted : []
|
||||
]);
|
||||
if (updateB.added != null) {
|
||||
for (const moduleId of updateB.added){
|
||||
deleted.delete(moduleId);
|
||||
}
|
||||
}
|
||||
if (updateB.deleted != null) {
|
||||
for (const moduleId of updateB.deleted){
|
||||
added.delete(moduleId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'partial',
|
||||
added: [
|
||||
...added
|
||||
],
|
||||
deleted: [
|
||||
...deleted
|
||||
]
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'added' && updateB.type === 'partial') {
|
||||
var _updateA_modules1, _updateB_added1;
|
||||
const modules = new Set([
|
||||
...(_updateA_modules1 = updateA.modules) !== null && _updateA_modules1 !== void 0 ? _updateA_modules1 : [],
|
||||
...(_updateB_added1 = updateB.added) !== null && _updateB_added1 !== void 0 ? _updateB_added1 : []
|
||||
]);
|
||||
var _updateB_deleted1;
|
||||
for (const moduleId of (_updateB_deleted1 = updateB.deleted) !== null && _updateB_deleted1 !== void 0 ? _updateB_deleted1 : []){
|
||||
modules.delete(moduleId);
|
||||
}
|
||||
return {
|
||||
type: 'added',
|
||||
modules: [
|
||||
...modules
|
||||
]
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'partial' && updateB.type === 'deleted') {
|
||||
var _updateB_modules1;
|
||||
// We could eagerly return `updateB` here, but this would potentially be
|
||||
// incorrect if `updateA` has added modules.
|
||||
const modules = new Set((_updateB_modules1 = updateB.modules) !== null && _updateB_modules1 !== void 0 ? _updateB_modules1 : []);
|
||||
if (updateA.added != null) {
|
||||
for (const moduleId of updateA.added){
|
||||
modules.delete(moduleId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'deleted',
|
||||
modules: [
|
||||
...modules
|
||||
]
|
||||
};
|
||||
}
|
||||
// Any other update combination is invalid.
|
||||
return undefined;
|
||||
}
|
||||
function invariant(_, message) {
|
||||
throw new Error("Invariant: ".concat(message));
|
||||
}
|
||||
const CRITICAL = [
|
||||
'bug',
|
||||
'error',
|
||||
'fatal'
|
||||
];
|
||||
function compareByList(list, a, b) {
|
||||
const aI = list.indexOf(a) + 1 || list.length;
|
||||
const bI = list.indexOf(b) + 1 || list.length;
|
||||
return aI - bI;
|
||||
}
|
||||
const chunksWithIssues = new Map();
|
||||
function emitIssues() {
|
||||
const issues = [];
|
||||
const deduplicationSet = new Set();
|
||||
for (const [_, chunkIssues] of chunksWithIssues){
|
||||
for (const chunkIssue of chunkIssues){
|
||||
if (deduplicationSet.has(chunkIssue.formatted)) continue;
|
||||
issues.push(chunkIssue);
|
||||
deduplicationSet.add(chunkIssue.formatted);
|
||||
}
|
||||
}
|
||||
sortIssues(issues);
|
||||
hooks.issues(issues);
|
||||
}
|
||||
function handleIssues(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
let hasCriticalIssues = false;
|
||||
for (const issue of msg.issues){
|
||||
if (CRITICAL.includes(issue.severity)) {
|
||||
hasCriticalIssues = true;
|
||||
}
|
||||
}
|
||||
if (msg.issues.length > 0) {
|
||||
chunksWithIssues.set(key, msg.issues);
|
||||
} else if (chunksWithIssues.has(key)) {
|
||||
chunksWithIssues.delete(key);
|
||||
}
|
||||
emitIssues();
|
||||
return hasCriticalIssues;
|
||||
}
|
||||
const SEVERITY_ORDER = [
|
||||
'bug',
|
||||
'fatal',
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'log'
|
||||
];
|
||||
const CATEGORY_ORDER = [
|
||||
'parse',
|
||||
'resolve',
|
||||
'code generation',
|
||||
'rendering',
|
||||
'typescript',
|
||||
'other'
|
||||
];
|
||||
function sortIssues(issues) {
|
||||
issues.sort((a, b)=>{
|
||||
const first = compareByList(SEVERITY_ORDER, a.severity, b.severity);
|
||||
if (first !== 0) return first;
|
||||
return compareByList(CATEGORY_ORDER, a.category, b.category);
|
||||
});
|
||||
}
|
||||
const hooks = {
|
||||
beforeRefresh: ()=>{},
|
||||
refresh: ()=>{},
|
||||
buildOk: ()=>{},
|
||||
issues: (_issues)=>{}
|
||||
};
|
||||
function setHooks(newHooks) {
|
||||
Object.assign(hooks, newHooks);
|
||||
}
|
||||
function handleSocketMessage(msg) {
|
||||
sortIssues(msg.issues);
|
||||
handleIssues(msg);
|
||||
switch(msg.type){
|
||||
case 'issues':
|
||||
break;
|
||||
case 'partial':
|
||||
// aggregate updates
|
||||
aggregateUpdates(msg);
|
||||
break;
|
||||
default:
|
||||
// run single update
|
||||
const runHooks = chunkListsWithPendingUpdates.size === 0;
|
||||
if (runHooks) hooks.beforeRefresh();
|
||||
triggerUpdate(msg);
|
||||
if (runHooks) finalizeUpdate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
function finalizeUpdate() {
|
||||
hooks.refresh();
|
||||
hooks.buildOk();
|
||||
// This is used by the Next.js integration test suite to notify it when HMR
|
||||
// updates have been completed.
|
||||
// TODO: Only run this in test environments (gate by `process.env.__NEXT_TEST_MODE`)
|
||||
if (globalThis.__NEXT_HMR_CB) {
|
||||
globalThis.__NEXT_HMR_CB();
|
||||
globalThis.__NEXT_HMR_CB = null;
|
||||
}
|
||||
}
|
||||
function subscribeToChunkUpdate(chunkListPath, sendMessage, callback) {
|
||||
return subscribeToUpdate({
|
||||
path: chunkListPath
|
||||
}, sendMessage, callback);
|
||||
}
|
||||
function subscribeToUpdate(resource, sendMessage, callback) {
|
||||
const key = resourceKey(resource);
|
||||
let callbackSet;
|
||||
const existingCallbackSet = updateCallbackSets.get(key);
|
||||
if (!existingCallbackSet) {
|
||||
callbackSet = {
|
||||
callbacks: new Set([
|
||||
callback
|
||||
]),
|
||||
unsubscribe: subscribeToUpdates(sendMessage, resource)
|
||||
};
|
||||
updateCallbackSets.set(key, callbackSet);
|
||||
} else {
|
||||
existingCallbackSet.callbacks.add(callback);
|
||||
callbackSet = existingCallbackSet;
|
||||
}
|
||||
return ()=>{
|
||||
callbackSet.callbacks.delete(callback);
|
||||
if (callbackSet.callbacks.size === 0) {
|
||||
callbackSet.unsubscribe();
|
||||
updateCallbackSets.delete(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
function triggerUpdate(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
const callbackSet = updateCallbackSets.get(key);
|
||||
if (!callbackSet) {
|
||||
return;
|
||||
}
|
||||
for (const callback of callbackSet.callbacks){
|
||||
callback(msg);
|
||||
}
|
||||
if (msg.type === 'notFound') {
|
||||
// This indicates that the resource which we subscribed to either does not exist or
|
||||
// has been deleted. In either case, we should clear all update callbacks, so if a
|
||||
// new subscription is created for the same resource, it will send a new "subscribe"
|
||||
// message to the server.
|
||||
// No need to send an "unsubscribe" message to the server, it will have already
|
||||
// dropped the update stream before sending the "notFound" message.
|
||||
updateCallbackSets.delete(key);
|
||||
}
|
||||
}
|
||||
}),
|
||||
"[hmr-entry]/hmr-entry.js { ENTRY => \"[project]/pages/_error\" }", ((__turbopack_context__) => {
|
||||
"use strict";
|
||||
|
||||
__turbopack_context__.r("[next]/entry/page-loader.ts { PAGE => \"[project]/node_modules/next/error.js [client] (ecmascript)\" } [client] (ecmascript)");
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=%5Broot-of-the-server%5D__092393de._.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,481 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[turbopack]/browser/dev/hmr-client/hmr-client.ts [client] (ecmascript)", ((__turbopack_context__) => {
|
||||
"use strict";
|
||||
|
||||
/// <reference path="../../../shared/runtime-types.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-globals.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-protocol.d.ts" />
|
||||
/// <reference path="../../runtime/base/dev-extensions.ts" />
|
||||
__turbopack_context__.s([
|
||||
"connect",
|
||||
()=>connect,
|
||||
"setHooks",
|
||||
()=>setHooks,
|
||||
"subscribeToUpdate",
|
||||
()=>subscribeToUpdate
|
||||
]);
|
||||
function connect(param) {
|
||||
let { addMessageListener, sendMessage, onUpdateError = console.error } = param;
|
||||
addMessageListener((msg)=>{
|
||||
switch(msg.type){
|
||||
case 'turbopack-connected':
|
||||
handleSocketConnected(sendMessage);
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
if (Array.isArray(msg.data)) {
|
||||
for(let i = 0; i < msg.data.length; i++){
|
||||
handleSocketMessage(msg.data[i]);
|
||||
}
|
||||
} else {
|
||||
handleSocketMessage(msg.data);
|
||||
}
|
||||
applyAggregatedUpdates();
|
||||
} catch (e) {
|
||||
console.warn('[Fast Refresh] performing full reload\n\n' + "Fast Refresh will perform a full reload when you edit a file that's imported by modules outside of the React rendering tree.\n" + 'You might have a file which exports a React component but also exports a value that is imported by a non-React component file.\n' + 'Consider migrating the non-React component export to a separate file and importing it into both files.\n\n' + 'It is also possible the parent component of the component you edited is a class component, which disables Fast Refresh.\n' + 'Fast Refresh requires at least one parent function component in your React tree.');
|
||||
onUpdateError(e);
|
||||
location.reload();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
const queued = globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS;
|
||||
if (queued != null && !Array.isArray(queued)) {
|
||||
throw new Error('A separate HMR handler was already registered');
|
||||
}
|
||||
globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS = {
|
||||
push: (param)=>{
|
||||
let [chunkPath, callback] = param;
|
||||
subscribeToChunkUpdate(chunkPath, sendMessage, callback);
|
||||
}
|
||||
};
|
||||
if (Array.isArray(queued)) {
|
||||
for (const [chunkPath, callback] of queued){
|
||||
subscribeToChunkUpdate(chunkPath, sendMessage, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
const updateCallbackSets = new Map();
|
||||
function sendJSON(sendMessage, message) {
|
||||
sendMessage(JSON.stringify(message));
|
||||
}
|
||||
function resourceKey(resource) {
|
||||
return JSON.stringify({
|
||||
path: resource.path,
|
||||
headers: resource.headers || null
|
||||
});
|
||||
}
|
||||
function subscribeToUpdates(sendMessage, resource) {
|
||||
sendJSON(sendMessage, {
|
||||
type: 'turbopack-subscribe',
|
||||
...resource
|
||||
});
|
||||
return ()=>{
|
||||
sendJSON(sendMessage, {
|
||||
type: 'turbopack-unsubscribe',
|
||||
...resource
|
||||
});
|
||||
};
|
||||
}
|
||||
function handleSocketConnected(sendMessage) {
|
||||
for (const key of updateCallbackSets.keys()){
|
||||
subscribeToUpdates(sendMessage, JSON.parse(key));
|
||||
}
|
||||
}
|
||||
// we aggregate all pending updates until the issues are resolved
|
||||
const chunkListsWithPendingUpdates = new Map();
|
||||
function aggregateUpdates(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
let aggregated = chunkListsWithPendingUpdates.get(key);
|
||||
if (aggregated) {
|
||||
aggregated.instruction = mergeChunkListUpdates(aggregated.instruction, msg.instruction);
|
||||
} else {
|
||||
chunkListsWithPendingUpdates.set(key, msg);
|
||||
}
|
||||
}
|
||||
function applyAggregatedUpdates() {
|
||||
if (chunkListsWithPendingUpdates.size === 0) return;
|
||||
hooks.beforeRefresh();
|
||||
for (const msg of chunkListsWithPendingUpdates.values()){
|
||||
triggerUpdate(msg);
|
||||
}
|
||||
chunkListsWithPendingUpdates.clear();
|
||||
finalizeUpdate();
|
||||
}
|
||||
function mergeChunkListUpdates(updateA, updateB) {
|
||||
let chunks;
|
||||
if (updateA.chunks != null) {
|
||||
if (updateB.chunks == null) {
|
||||
chunks = updateA.chunks;
|
||||
} else {
|
||||
chunks = mergeChunkListChunks(updateA.chunks, updateB.chunks);
|
||||
}
|
||||
} else if (updateB.chunks != null) {
|
||||
chunks = updateB.chunks;
|
||||
}
|
||||
let merged;
|
||||
if (updateA.merged != null) {
|
||||
if (updateB.merged == null) {
|
||||
merged = updateA.merged;
|
||||
} else {
|
||||
// Since `merged` is an array of updates, we need to merge them all into
|
||||
// one, consistent update.
|
||||
// Since there can only be `EcmascriptMergeUpdates` in the array, there is
|
||||
// no need to key on the `type` field.
|
||||
let update = updateA.merged[0];
|
||||
for(let i = 1; i < updateA.merged.length; i++){
|
||||
update = mergeChunkListEcmascriptMergedUpdates(update, updateA.merged[i]);
|
||||
}
|
||||
for(let i = 0; i < updateB.merged.length; i++){
|
||||
update = mergeChunkListEcmascriptMergedUpdates(update, updateB.merged[i]);
|
||||
}
|
||||
merged = [
|
||||
update
|
||||
];
|
||||
}
|
||||
} else if (updateB.merged != null) {
|
||||
merged = updateB.merged;
|
||||
}
|
||||
return {
|
||||
type: 'ChunkListUpdate',
|
||||
chunks,
|
||||
merged
|
||||
};
|
||||
}
|
||||
function mergeChunkListChunks(chunksA, chunksB) {
|
||||
const chunks = {};
|
||||
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)){
|
||||
const chunkUpdateB = chunksB[chunkPath];
|
||||
if (chunkUpdateB != null) {
|
||||
const mergedUpdate = mergeChunkUpdates(chunkUpdateA, chunkUpdateB);
|
||||
if (mergedUpdate != null) {
|
||||
chunks[chunkPath] = mergedUpdate;
|
||||
}
|
||||
} else {
|
||||
chunks[chunkPath] = chunkUpdateA;
|
||||
}
|
||||
}
|
||||
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)){
|
||||
if (chunks[chunkPath] == null) {
|
||||
chunks[chunkPath] = chunkUpdateB;
|
||||
}
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
function mergeChunkUpdates(updateA, updateB) {
|
||||
if (updateA.type === 'added' && updateB.type === 'deleted' || updateA.type === 'deleted' && updateB.type === 'added') {
|
||||
return undefined;
|
||||
}
|
||||
if (updateA.type === 'partial') {
|
||||
invariant(updateA.instruction, 'Partial updates are unsupported');
|
||||
}
|
||||
if (updateB.type === 'partial') {
|
||||
invariant(updateB.instruction, 'Partial updates are unsupported');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function mergeChunkListEcmascriptMergedUpdates(mergedA, mergedB) {
|
||||
const entries = mergeEcmascriptChunkEntries(mergedA.entries, mergedB.entries);
|
||||
const chunks = mergeEcmascriptChunksUpdates(mergedA.chunks, mergedB.chunks);
|
||||
return {
|
||||
type: 'EcmascriptMergedUpdate',
|
||||
entries,
|
||||
chunks
|
||||
};
|
||||
}
|
||||
function mergeEcmascriptChunkEntries(entriesA, entriesB) {
|
||||
return {
|
||||
...entriesA,
|
||||
...entriesB
|
||||
};
|
||||
}
|
||||
function mergeEcmascriptChunksUpdates(chunksA, chunksB) {
|
||||
if (chunksA == null) {
|
||||
return chunksB;
|
||||
}
|
||||
if (chunksB == null) {
|
||||
return chunksA;
|
||||
}
|
||||
const chunks = {};
|
||||
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)){
|
||||
const chunkUpdateB = chunksB[chunkPath];
|
||||
if (chunkUpdateB != null) {
|
||||
const mergedUpdate = mergeEcmascriptChunkUpdates(chunkUpdateA, chunkUpdateB);
|
||||
if (mergedUpdate != null) {
|
||||
chunks[chunkPath] = mergedUpdate;
|
||||
}
|
||||
} else {
|
||||
chunks[chunkPath] = chunkUpdateA;
|
||||
}
|
||||
}
|
||||
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)){
|
||||
if (chunks[chunkPath] == null) {
|
||||
chunks[chunkPath] = chunkUpdateB;
|
||||
}
|
||||
}
|
||||
if (Object.keys(chunks).length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
function mergeEcmascriptChunkUpdates(updateA, updateB) {
|
||||
if (updateA.type === 'added' && updateB.type === 'deleted') {
|
||||
// These two completely cancel each other out.
|
||||
return undefined;
|
||||
}
|
||||
if (updateA.type === 'deleted' && updateB.type === 'added') {
|
||||
const added = [];
|
||||
const deleted = [];
|
||||
var _updateA_modules;
|
||||
const deletedModules = new Set((_updateA_modules = updateA.modules) !== null && _updateA_modules !== void 0 ? _updateA_modules : []);
|
||||
var _updateB_modules;
|
||||
const addedModules = new Set((_updateB_modules = updateB.modules) !== null && _updateB_modules !== void 0 ? _updateB_modules : []);
|
||||
for (const moduleId of addedModules){
|
||||
if (!deletedModules.has(moduleId)) {
|
||||
added.push(moduleId);
|
||||
}
|
||||
}
|
||||
for (const moduleId of deletedModules){
|
||||
if (!addedModules.has(moduleId)) {
|
||||
deleted.push(moduleId);
|
||||
}
|
||||
}
|
||||
if (added.length === 0 && deleted.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
type: 'partial',
|
||||
added,
|
||||
deleted
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'partial' && updateB.type === 'partial') {
|
||||
var _updateA_added, _updateB_added;
|
||||
const added = new Set([
|
||||
...(_updateA_added = updateA.added) !== null && _updateA_added !== void 0 ? _updateA_added : [],
|
||||
...(_updateB_added = updateB.added) !== null && _updateB_added !== void 0 ? _updateB_added : []
|
||||
]);
|
||||
var _updateA_deleted, _updateB_deleted;
|
||||
const deleted = new Set([
|
||||
...(_updateA_deleted = updateA.deleted) !== null && _updateA_deleted !== void 0 ? _updateA_deleted : [],
|
||||
...(_updateB_deleted = updateB.deleted) !== null && _updateB_deleted !== void 0 ? _updateB_deleted : []
|
||||
]);
|
||||
if (updateB.added != null) {
|
||||
for (const moduleId of updateB.added){
|
||||
deleted.delete(moduleId);
|
||||
}
|
||||
}
|
||||
if (updateB.deleted != null) {
|
||||
for (const moduleId of updateB.deleted){
|
||||
added.delete(moduleId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'partial',
|
||||
added: [
|
||||
...added
|
||||
],
|
||||
deleted: [
|
||||
...deleted
|
||||
]
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'added' && updateB.type === 'partial') {
|
||||
var _updateA_modules1, _updateB_added1;
|
||||
const modules = new Set([
|
||||
...(_updateA_modules1 = updateA.modules) !== null && _updateA_modules1 !== void 0 ? _updateA_modules1 : [],
|
||||
...(_updateB_added1 = updateB.added) !== null && _updateB_added1 !== void 0 ? _updateB_added1 : []
|
||||
]);
|
||||
var _updateB_deleted1;
|
||||
for (const moduleId of (_updateB_deleted1 = updateB.deleted) !== null && _updateB_deleted1 !== void 0 ? _updateB_deleted1 : []){
|
||||
modules.delete(moduleId);
|
||||
}
|
||||
return {
|
||||
type: 'added',
|
||||
modules: [
|
||||
...modules
|
||||
]
|
||||
};
|
||||
}
|
||||
if (updateA.type === 'partial' && updateB.type === 'deleted') {
|
||||
var _updateB_modules1;
|
||||
// We could eagerly return `updateB` here, but this would potentially be
|
||||
// incorrect if `updateA` has added modules.
|
||||
const modules = new Set((_updateB_modules1 = updateB.modules) !== null && _updateB_modules1 !== void 0 ? _updateB_modules1 : []);
|
||||
if (updateA.added != null) {
|
||||
for (const moduleId of updateA.added){
|
||||
modules.delete(moduleId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'deleted',
|
||||
modules: [
|
||||
...modules
|
||||
]
|
||||
};
|
||||
}
|
||||
// Any other update combination is invalid.
|
||||
return undefined;
|
||||
}
|
||||
function invariant(_, message) {
|
||||
throw new Error("Invariant: ".concat(message));
|
||||
}
|
||||
const CRITICAL = [
|
||||
'bug',
|
||||
'error',
|
||||
'fatal'
|
||||
];
|
||||
function compareByList(list, a, b) {
|
||||
const aI = list.indexOf(a) + 1 || list.length;
|
||||
const bI = list.indexOf(b) + 1 || list.length;
|
||||
return aI - bI;
|
||||
}
|
||||
const chunksWithIssues = new Map();
|
||||
function emitIssues() {
|
||||
const issues = [];
|
||||
const deduplicationSet = new Set();
|
||||
for (const [_, chunkIssues] of chunksWithIssues){
|
||||
for (const chunkIssue of chunkIssues){
|
||||
if (deduplicationSet.has(chunkIssue.formatted)) continue;
|
||||
issues.push(chunkIssue);
|
||||
deduplicationSet.add(chunkIssue.formatted);
|
||||
}
|
||||
}
|
||||
sortIssues(issues);
|
||||
hooks.issues(issues);
|
||||
}
|
||||
function handleIssues(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
let hasCriticalIssues = false;
|
||||
for (const issue of msg.issues){
|
||||
if (CRITICAL.includes(issue.severity)) {
|
||||
hasCriticalIssues = true;
|
||||
}
|
||||
}
|
||||
if (msg.issues.length > 0) {
|
||||
chunksWithIssues.set(key, msg.issues);
|
||||
} else if (chunksWithIssues.has(key)) {
|
||||
chunksWithIssues.delete(key);
|
||||
}
|
||||
emitIssues();
|
||||
return hasCriticalIssues;
|
||||
}
|
||||
const SEVERITY_ORDER = [
|
||||
'bug',
|
||||
'fatal',
|
||||
'error',
|
||||
'warning',
|
||||
'info',
|
||||
'log'
|
||||
];
|
||||
const CATEGORY_ORDER = [
|
||||
'parse',
|
||||
'resolve',
|
||||
'code generation',
|
||||
'rendering',
|
||||
'typescript',
|
||||
'other'
|
||||
];
|
||||
function sortIssues(issues) {
|
||||
issues.sort((a, b)=>{
|
||||
const first = compareByList(SEVERITY_ORDER, a.severity, b.severity);
|
||||
if (first !== 0) return first;
|
||||
return compareByList(CATEGORY_ORDER, a.category, b.category);
|
||||
});
|
||||
}
|
||||
const hooks = {
|
||||
beforeRefresh: ()=>{},
|
||||
refresh: ()=>{},
|
||||
buildOk: ()=>{},
|
||||
issues: (_issues)=>{}
|
||||
};
|
||||
function setHooks(newHooks) {
|
||||
Object.assign(hooks, newHooks);
|
||||
}
|
||||
function handleSocketMessage(msg) {
|
||||
sortIssues(msg.issues);
|
||||
handleIssues(msg);
|
||||
switch(msg.type){
|
||||
case 'issues':
|
||||
break;
|
||||
case 'partial':
|
||||
// aggregate updates
|
||||
aggregateUpdates(msg);
|
||||
break;
|
||||
default:
|
||||
// run single update
|
||||
const runHooks = chunkListsWithPendingUpdates.size === 0;
|
||||
if (runHooks) hooks.beforeRefresh();
|
||||
triggerUpdate(msg);
|
||||
if (runHooks) finalizeUpdate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
function finalizeUpdate() {
|
||||
hooks.refresh();
|
||||
hooks.buildOk();
|
||||
// This is used by the Next.js integration test suite to notify it when HMR
|
||||
// updates have been completed.
|
||||
// TODO: Only run this in test environments (gate by `process.env.__NEXT_TEST_MODE`)
|
||||
if (globalThis.__NEXT_HMR_CB) {
|
||||
globalThis.__NEXT_HMR_CB();
|
||||
globalThis.__NEXT_HMR_CB = null;
|
||||
}
|
||||
}
|
||||
function subscribeToChunkUpdate(chunkListPath, sendMessage, callback) {
|
||||
return subscribeToUpdate({
|
||||
path: chunkListPath
|
||||
}, sendMessage, callback);
|
||||
}
|
||||
function subscribeToUpdate(resource, sendMessage, callback) {
|
||||
const key = resourceKey(resource);
|
||||
let callbackSet;
|
||||
const existingCallbackSet = updateCallbackSets.get(key);
|
||||
if (!existingCallbackSet) {
|
||||
callbackSet = {
|
||||
callbacks: new Set([
|
||||
callback
|
||||
]),
|
||||
unsubscribe: subscribeToUpdates(sendMessage, resource)
|
||||
};
|
||||
updateCallbackSets.set(key, callbackSet);
|
||||
} else {
|
||||
existingCallbackSet.callbacks.add(callback);
|
||||
callbackSet = existingCallbackSet;
|
||||
}
|
||||
return ()=>{
|
||||
callbackSet.callbacks.delete(callback);
|
||||
if (callbackSet.callbacks.size === 0) {
|
||||
callbackSet.unsubscribe();
|
||||
updateCallbackSets.delete(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
function triggerUpdate(msg) {
|
||||
const key = resourceKey(msg.resource);
|
||||
const callbackSet = updateCallbackSets.get(key);
|
||||
if (!callbackSet) {
|
||||
return;
|
||||
}
|
||||
for (const callback of callbackSet.callbacks){
|
||||
callback(msg);
|
||||
}
|
||||
if (msg.type === 'notFound') {
|
||||
// This indicates that the resource which we subscribed to either does not exist or
|
||||
// has been deleted. In either case, we should clear all update callbacks, so if a
|
||||
// new subscription is created for the same resource, it will send a new "subscribe"
|
||||
// message to the server.
|
||||
// No need to send an "unsubscribe" message to the server, it will have already
|
||||
// dropped the update stream before sending the "notFound" message.
|
||||
updateCallbackSets.delete(key);
|
||||
}
|
||||
}
|
||||
}),
|
||||
"[hmr-entry]/hmr-entry.js { ENTRY => \"[project]/pages/_app\" }", ((__turbopack_context__) => {
|
||||
"use strict";
|
||||
|
||||
__turbopack_context__.r("[next]/entry/page-loader.ts { PAGE => \"[project]/node_modules/next/app.js [client] (ecmascript)\" } [client] (ecmascript)");
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=%5Broot-of-the-server%5D__45f039c3._.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,8 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[project]/node_modules/next/app.js [client] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
||||
|
||||
module.exports = __turbopack_context__.r("[project]/node_modules/next/dist/pages/_app.js [client] (ecmascript)");
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=node_modules_next_app_72f3d36f.js.map
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sections": [
|
||||
{"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["file:///Users/exogen/Projects/t2-mapper/node_modules/next/app.js"],"sourcesContent":["module.exports = require('./dist/pages/_app')\n"],"names":[],"mappings":"AAAA,OAAO,OAAO","ignoreList":[0],"debugId":null}}]
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,8 +0,0 @@
|
|||
(globalThis.TURBOPACK || (globalThis.TURBOPACK = [])).push([typeof document === "object" ? document.currentScript : undefined,
|
||||
"[project]/node_modules/next/error.js [client] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
||||
|
||||
module.exports = __turbopack_context__.r("[project]/node_modules/next/dist/pages/_error.js [client] (ecmascript)");
|
||||
}),
|
||||
]);
|
||||
|
||||
//# sourceMappingURL=node_modules_next_error_1cfbb379.js.map
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sections": [
|
||||
{"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["file:///Users/exogen/Projects/t2-mapper/node_modules/next/error.js"],"sourcesContent":["module.exports = require('./dist/pages/_error')\n"],"names":[],"mappings":"AAAA,OAAO,OAAO","ignoreList":[0],"debugId":null}}]
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,14 +0,0 @@
|
|||
__turbopack_load_page_chunks__("/_app", [
|
||||
"static/chunks/node_modules_next_dist_compiled_next-devtools_index_5277ebc8.js",
|
||||
"static/chunks/node_modules_next_dist_compiled_e019967a._.js",
|
||||
"static/chunks/node_modules_next_dist_shared_lib_ffb1a49d._.js",
|
||||
"static/chunks/node_modules_next_dist_client_d0aa886c._.js",
|
||||
"static/chunks/node_modules_next_dist_3b64f844._.js",
|
||||
"static/chunks/node_modules_next_app_72f3d36f.js",
|
||||
"static/chunks/[next]_entry_page-loader_ts_742e4b53._.js",
|
||||
"static/chunks/node_modules_react-dom_4411d9bd._.js",
|
||||
"static/chunks/node_modules_db4bb196._.js",
|
||||
"static/chunks/[root-of-the-server]__45f039c3._.js",
|
||||
"static/chunks/pages__app_2da965e7._.js",
|
||||
"static/chunks/turbopack-pages__app_d2875d60._.js"
|
||||
])
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
__turbopack_load_page_chunks__("/_error", [
|
||||
"static/chunks/node_modules_next_dist_compiled_next-devtools_index_5277ebc8.js",
|
||||
"static/chunks/node_modules_next_dist_compiled_e019967a._.js",
|
||||
"static/chunks/node_modules_next_dist_shared_lib_80dd6ac5._.js",
|
||||
"static/chunks/node_modules_next_dist_client_d0aa886c._.js",
|
||||
"static/chunks/node_modules_next_dist_f7abec85._.js",
|
||||
"static/chunks/node_modules_next_error_1cfbb379.js",
|
||||
"static/chunks/[next]_entry_page-loader_ts_43b523b5._.js",
|
||||
"static/chunks/node_modules_react-dom_4411d9bd._.js",
|
||||
"static/chunks/node_modules_db4bb196._.js",
|
||||
"static/chunks/[root-of-the-server]__092393de._.js",
|
||||
"static/chunks/pages__error_2da965e7._.js",
|
||||
"static/chunks/turbopack-pages__error_6b118ac5._.js"
|
||||
])
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
(globalThis.TURBOPACK_CHUNK_LISTS || (globalThis.TURBOPACK_CHUNK_LISTS = [])).push({
|
||||
script: typeof document === "object" ? document.currentScript : undefined,
|
||||
chunks: [
|
||||
"static/chunks/node_modules_next_dist_compiled_next-devtools_index_5277ebc8.js",
|
||||
"static/chunks/node_modules_next_dist_compiled_e019967a._.js",
|
||||
"static/chunks/node_modules_next_dist_shared_lib_ffb1a49d._.js",
|
||||
"static/chunks/node_modules_next_dist_client_d0aa886c._.js",
|
||||
"static/chunks/node_modules_next_dist_3b64f844._.js",
|
||||
"static/chunks/node_modules_next_app_72f3d36f.js",
|
||||
"static/chunks/[next]_entry_page-loader_ts_742e4b53._.js",
|
||||
"static/chunks/node_modules_react-dom_4411d9bd._.js",
|
||||
"static/chunks/node_modules_db4bb196._.js",
|
||||
"static/chunks/[root-of-the-server]__45f039c3._.js"
|
||||
],
|
||||
source: "entry"
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,16 +0,0 @@
|
|||
(globalThis.TURBOPACK_CHUNK_LISTS || (globalThis.TURBOPACK_CHUNK_LISTS = [])).push({
|
||||
script: typeof document === "object" ? document.currentScript : undefined,
|
||||
chunks: [
|
||||
"static/chunks/node_modules_next_dist_compiled_next-devtools_index_5277ebc8.js",
|
||||
"static/chunks/node_modules_next_dist_compiled_e019967a._.js",
|
||||
"static/chunks/node_modules_next_dist_shared_lib_80dd6ac5._.js",
|
||||
"static/chunks/node_modules_next_dist_client_d0aa886c._.js",
|
||||
"static/chunks/node_modules_next_dist_f7abec85._.js",
|
||||
"static/chunks/node_modules_next_error_1cfbb379.js",
|
||||
"static/chunks/[next]_entry_page-loader_ts_43b523b5._.js",
|
||||
"static/chunks/node_modules_react-dom_4411d9bd._.js",
|
||||
"static/chunks/node_modules_db4bb196._.js",
|
||||
"static/chunks/[root-of-the-server]__092393de._.js"
|
||||
],
|
||||
source: "entry"
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -2,15 +2,15 @@
|
|||
2:I[39756,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"default"]
|
||||
3:I[37457,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"default"]
|
||||
4:I[47257,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"ClientPageRoot"]
|
||||
5:I[31713,["/t2-mapper/_next/static/chunks/f620a0b974993323.js","/t2-mapper/_next/static/chunks/32ef0c8650712240.js","/t2-mapper/_next/static/chunks/86f923aa0f996fe4.js","/t2-mapper/_next/static/chunks/741e1acf393d55a4.js"],"default"]
|
||||
5:I[31713,["/t2-mapper/_next/static/chunks/f620a0b974993323.js","/t2-mapper/_next/static/chunks/32ef0c8650712240.js","/t2-mapper/_next/static/chunks/e48d3b25285f3054.js","/t2-mapper/_next/static/chunks/1e4f7733a4dd09be.js"],"default"]
|
||||
8:I[97367,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"OutletBoundary"]
|
||||
a:I[11533,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"AsyncMetadataOutlet"]
|
||||
c:I[97367,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"ViewportBoundary"]
|
||||
e:I[97367,["/t2-mapper/_next/static/chunks/060f9a97930f3d04.js"],"MetadataBoundary"]
|
||||
f:"$Sreact.suspense"
|
||||
11:I[68027,[],"default"]
|
||||
:HL["/t2-mapper/_next/static/chunks/be0d253f8c18ec3b.css","style"]
|
||||
0:{"P":null,"b":"PYi7xunkxIU2JehSYV4Sg","p":"/t2-mapper","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/be0d253f8c18ec3b.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/f620a0b974993323.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/32ef0c8650712240.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/86f923aa0f996fe4.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/741e1acf393d55a4.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/be0d253f8c18ec3b.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"s":false,"S":true}
|
||||
:HL["/t2-mapper/_next/static/chunks/15b04c9d2ba2c4cf.css","style"]
|
||||
0:{"P":null,"b":"XHGJaNs99HGJmX9H1-TS5","p":"/t2-mapper","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/15b04c9d2ba2c4cf.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],[["$","script","script-0",{"src":"/t2-mapper/_next/static/chunks/f620a0b974993323.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/t2-mapper/_next/static/chunks/32ef0c8650712240.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/t2-mapper/_next/static/chunks/e48d3b25285f3054.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/t2-mapper/_next/static/chunks/1e4f7733a4dd09be.js","async":true,"nonce":"$undefined"}]],["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/chunks/15b04c9d2ba2c4cf.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"s":false,"S":true}
|
||||
6:{}
|
||||
7:"$0:f:0:1:2:children:1:props:children:0:props:params"
|
||||
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { renderObject } from "./renderObject";
|
|||
import { memo, useEffect, useState } from "react";
|
||||
import { RuntimeProvider } from "./RuntimeProvider";
|
||||
import {
|
||||
createProgressTracker,
|
||||
createScriptCache,
|
||||
FileSystemHandler,
|
||||
runServer,
|
||||
|
|
@ -50,6 +51,7 @@ function useParsedMission(name: string) {
|
|||
interface ExecutedMissionState {
|
||||
missionGroup: TorqueObject | undefined;
|
||||
runtime: TorqueRuntime | undefined;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
function useExecutedMission(
|
||||
|
|
@ -59,6 +61,7 @@ function useExecutedMission(
|
|||
const [state, setState] = useState<ExecutedMissionState>({
|
||||
missionGroup: undefined,
|
||||
runtime: undefined,
|
||||
progress: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -70,6 +73,13 @@ function useExecutedMission(
|
|||
// FIXME: Always just runs as the first game type for now...
|
||||
const missionType = parsedMission.missionTypes[0];
|
||||
|
||||
// Create progress tracker and update state on changes
|
||||
const progressTracker = createProgressTracker();
|
||||
const handleProgress = () => {
|
||||
setState((prev) => ({ ...prev, progress: progressTracker.progress }));
|
||||
};
|
||||
progressTracker.on("update", handleProgress);
|
||||
|
||||
const { runtime } = runServer({
|
||||
missionName,
|
||||
missionType,
|
||||
|
|
@ -78,10 +88,12 @@ function useExecutedMission(
|
|||
fileSystem,
|
||||
cache: scriptCache,
|
||||
signal: controller.signal,
|
||||
progress: progressTracker,
|
||||
ignoreScripts: [
|
||||
"scripts/admin.cs",
|
||||
"scripts/ai.cs",
|
||||
"scripts/aiCTF.cs",
|
||||
"scripts/aiTDM.cs",
|
||||
"scripts/aiHunters.cs",
|
||||
"scripts/deathMessages.cs",
|
||||
"scripts/graphBuild.cs",
|
||||
|
|
@ -92,11 +104,12 @@ function useExecutedMission(
|
|||
},
|
||||
onMissionLoadDone: () => {
|
||||
const missionGroup = runtime.getObjectByName("MissionGroup");
|
||||
setState({ missionGroup, runtime });
|
||||
setState({ missionGroup, runtime, progress: 1 });
|
||||
},
|
||||
});
|
||||
|
||||
return () => {
|
||||
progressTracker.off("update", handleProgress);
|
||||
controller.abort();
|
||||
runtime.destroy();
|
||||
};
|
||||
|
|
@ -107,7 +120,7 @@ function useExecutedMission(
|
|||
|
||||
interface MissionProps {
|
||||
name: string;
|
||||
onLoadingChange?: (isLoading: boolean) => void;
|
||||
onLoadingChange?: (isLoading: boolean, progress?: number) => void;
|
||||
}
|
||||
|
||||
export const Mission = memo(function Mission({
|
||||
|
|
@ -115,12 +128,15 @@ export const Mission = memo(function Mission({
|
|||
onLoadingChange,
|
||||
}: MissionProps) {
|
||||
const { data: parsedMission } = useParsedMission(name);
|
||||
const { missionGroup, runtime } = useExecutedMission(name, parsedMission);
|
||||
const { missionGroup, runtime, progress } = useExecutedMission(
|
||||
name,
|
||||
parsedMission,
|
||||
);
|
||||
const isLoading = !missionGroup || !runtime;
|
||||
|
||||
useEffect(() => {
|
||||
onLoadingChange?.(isLoading);
|
||||
}, [isLoading, onLoadingChange]);
|
||||
onLoadingChange?.(isLoading, progress);
|
||||
}, [isLoading, progress, onLoadingChange]);
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { TorqueObject, TorqueRuntime, TorqueRuntimeOptions } from "./types";
|
|||
export { generate, type GeneratorOptions } from "./codegen";
|
||||
export type { Program } from "./ast";
|
||||
export { createBuiltins } from "./builtins";
|
||||
export { createProgressTracker, type ProgressTracker } from "./progress";
|
||||
export { createRuntime, createScriptCache } from "./runtime";
|
||||
export { normalizePath } from "./utils";
|
||||
export type {
|
||||
|
|
|
|||
75
src/torqueScript/progress.ts
Normal file
75
src/torqueScript/progress.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
export type ProgressEventType = "update";
|
||||
export type ProgressListener = () => void;
|
||||
|
||||
export interface ProgressTracker {
|
||||
/** Total items discovered so far (increases as dependencies are found) */
|
||||
readonly total: number;
|
||||
/** Items completed */
|
||||
readonly loaded: number;
|
||||
/** Currently loading item path, if any */
|
||||
readonly current: string | null;
|
||||
/** Progress as a ratio from 0 to 1 (or 0 if total is 0) */
|
||||
readonly progress: number;
|
||||
/** Subscribe to progress updates */
|
||||
on(event: ProgressEventType, listener: ProgressListener): void;
|
||||
/** Unsubscribe from progress updates */
|
||||
off(event: ProgressEventType, listener: ProgressListener): void;
|
||||
}
|
||||
|
||||
export interface ProgressTrackerInternal extends ProgressTracker {
|
||||
/** Increment total count when a new item is discovered */
|
||||
addItem(path: string): void;
|
||||
/** Mark an item as completed */
|
||||
completeItem(): void;
|
||||
/** Set the currently loading item */
|
||||
setCurrent(path: string | null): void;
|
||||
}
|
||||
|
||||
export function createProgressTracker(): ProgressTrackerInternal {
|
||||
const listeners = new Set<ProgressListener>();
|
||||
|
||||
let total = 0;
|
||||
let loaded = 0;
|
||||
let current: string | null = null;
|
||||
|
||||
function notify(): void {
|
||||
for (const listener of listeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
get total() {
|
||||
return total;
|
||||
},
|
||||
get loaded() {
|
||||
return loaded;
|
||||
},
|
||||
get current() {
|
||||
return current;
|
||||
},
|
||||
get progress() {
|
||||
return total === 0 ? 0 : loaded / total;
|
||||
},
|
||||
on(_event: ProgressEventType, listener: ProgressListener) {
|
||||
listeners.add(listener);
|
||||
},
|
||||
off(_event: ProgressEventType, listener: ProgressListener) {
|
||||
listeners.delete(listener);
|
||||
},
|
||||
addItem(path: string) {
|
||||
total++;
|
||||
current = path;
|
||||
notify();
|
||||
},
|
||||
completeItem() {
|
||||
loaded++;
|
||||
current = null;
|
||||
notify();
|
||||
},
|
||||
setCurrent(path: string | null) {
|
||||
current = path;
|
||||
notify();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1121,12 +1121,16 @@ export function createRuntime(
|
|||
return;
|
||||
}
|
||||
|
||||
// Track this script in progress
|
||||
options.progress?.addItem(ref);
|
||||
|
||||
const loadPromise = (async () => {
|
||||
// Pass original path to loader - it handles its own normalization
|
||||
const source = await loader(ref);
|
||||
if (source == null) {
|
||||
console.warn(`Script not found: ${ref}`);
|
||||
state.failedScripts.add(normalized);
|
||||
options.progress?.completeItem();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1136,6 +1140,7 @@ export function createRuntime(
|
|||
} catch (err) {
|
||||
console.warn(`Failed to parse script: ${ref}`, err);
|
||||
state.failedScripts.add(normalized);
|
||||
options.progress?.completeItem();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1152,6 +1157,7 @@ export function createRuntime(
|
|||
|
||||
// Store the parsed AST
|
||||
state.scripts.set(normalized, depAst);
|
||||
options.progress?.completeItem();
|
||||
})();
|
||||
|
||||
loadingPromises.set(normalized, loadPromise);
|
||||
|
|
@ -1173,13 +1179,19 @@ export function createRuntime(
|
|||
return createLoadedScript(state.scripts.get(normalized)!, path);
|
||||
}
|
||||
|
||||
// Track this script in progress
|
||||
options.progress?.addItem(path);
|
||||
|
||||
// Pass original path to loader - it handles its own normalization
|
||||
const source = await loader(path);
|
||||
if (source == null) {
|
||||
options.progress?.completeItem();
|
||||
throw new Error(`Script not found: ${path}`);
|
||||
}
|
||||
|
||||
return loadFromSource(source, { path });
|
||||
const result = await loadFromSource(source, { path });
|
||||
options.progress?.completeItem();
|
||||
return result;
|
||||
}
|
||||
|
||||
async function loadFromSource(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { Program } from "./ast";
|
||||
import type { ProgressTrackerInternal } from "./progress";
|
||||
import type { CaseInsensitiveMap } from "./utils";
|
||||
|
||||
export type TorqueFunction = (...args: any[]) => any;
|
||||
|
|
@ -108,6 +109,12 @@ export interface TorqueRuntimeOptions {
|
|||
* Create with `createScriptCache()`.
|
||||
*/
|
||||
cache?: ScriptCache;
|
||||
/**
|
||||
* Progress tracker for monitoring script loading. If provided, the runtime
|
||||
* will report loading progress as scripts are discovered and loaded.
|
||||
* Create with `createProgressTracker()`.
|
||||
*/
|
||||
progress?: ProgressTrackerInternal;
|
||||
}
|
||||
|
||||
export interface LoadScriptOptions {
|
||||
|
|
|
|||
Loading…
Reference in a new issue