mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-11 16:04:30 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f69d1f73b1 |
+3
-1
@@ -6638,7 +6638,9 @@ window.feedBack.on('screen:changed', (ev) => {
|
||||
if (id === 'player') _hideResumePill();
|
||||
else _maybeShowResumePill();
|
||||
});
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded',
|
||||
() => { try { _maybeShowResumePill(); } catch (_) {} }, { once: true });
|
||||
} else {
|
||||
|
||||
@@ -519,7 +519,9 @@ window.feedBack.audio = Object.assign(window.feedBack.audio || {}, {
|
||||
readSongVolume: _readSongVolume,
|
||||
});
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', _init);
|
||||
} else {
|
||||
_init();
|
||||
|
||||
@@ -111,7 +111,9 @@
|
||||
|
||||
// Announce once after the document parses, so any listener wired during page
|
||||
// load can sync without special-casing (consumers may also just call get()).
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', announce, { once: true });
|
||||
} else {
|
||||
announce();
|
||||
|
||||
+3
-1
@@ -592,6 +592,8 @@
|
||||
sm.on('working-tuning-changed', () => renderInstrument());
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
else boot();
|
||||
})();
|
||||
|
||||
@@ -271,6 +271,8 @@
|
||||
sm.on('v3:profile-updated', () => render());
|
||||
}
|
||||
function boot() { render(); }
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
else boot();
|
||||
})();
|
||||
|
||||
@@ -273,7 +273,9 @@
|
||||
// the stage observer attaches.
|
||||
window.addEventListener('feedBack-minigames-ready', () => { ensureStageObserver(); refresh(); });
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
|
||||
+70
-54
@@ -99,23 +99,39 @@
|
||||
<link rel="stylesheet" href="/static/tour-engine.css">
|
||||
<!-- v0.3.0 shell styles (radial-gradient bg, custom scrollbars). -->
|
||||
<link rel="stylesheet" href="/static/v3/v3.css">
|
||||
<!-- EVERY external script below is `defer`. Do not add a plain one.
|
||||
`defer` and `type="module"` scripts share a single "execute after
|
||||
parsing" list and run in DOCUMENT ORDER; a plain classic script runs
|
||||
DURING parse, ahead of all of them. So one plain tag would jump the
|
||||
queue — and once the capabilities become modules (they defer), a
|
||||
still-plain app.js would run BEFORE the bus exists and die on its
|
||||
top-level `window.feedBack.on(...)` calls. Keeping every tag deferred
|
||||
is what preserves this file's order through the ES-module migration.
|
||||
Enforced by test_every_external_script_defers_so_document_order_is_execution_order.
|
||||
|
||||
The scripts themselves boot on DOMContentLoaded, which fires only after
|
||||
all of the above have evaluated — that is what lets a script's boot()
|
||||
use a global another script defines further down this list (there are
|
||||
~43 such forward references). Their readyState guards therefore treat
|
||||
'interactive' as not-ready; see the note at each one. -->
|
||||
|
||||
<!-- Diagnostics console capture must wrap console.* before any other
|
||||
script logs anything; load it as early as possible. See
|
||||
docs/diagnostics-bundle-spec.md (feedBack#166). -->
|
||||
<script src="/static/diagnostics.js"></script>
|
||||
<script src="/static/capabilities.js"></script>
|
||||
<script src="/static/capabilities/library.js"></script>
|
||||
<script src="/static/capabilities/tuning.js"></script>
|
||||
<script src="/static/capabilities/working-tuning.js"></script>
|
||||
<script src="/static/capabilities/audio-session.js"></script>
|
||||
<script src="/static/capabilities/audio-effects.js"></script>
|
||||
<script src="/static/capabilities/playback.js"></script>
|
||||
<script defer src="/static/diagnostics.js"></script>
|
||||
<script defer src="/static/capabilities.js"></script>
|
||||
<script defer src="/static/capabilities/library.js"></script>
|
||||
<script defer src="/static/capabilities/tuning.js"></script>
|
||||
<script defer src="/static/capabilities/working-tuning.js"></script>
|
||||
<script defer src="/static/capabilities/audio-session.js"></script>
|
||||
<script defer src="/static/capabilities/audio-effects.js"></script>
|
||||
<script defer src="/static/capabilities/playback.js"></script>
|
||||
<!-- fee[dB]ack v0.3.0: ui.library-card-injection capability (plugin card actions). -->
|
||||
<script src="/static/capabilities/library-card-actions.js"></script>
|
||||
<script src="/static/capabilities/visualization.js"></script>
|
||||
<script src="/static/capabilities/note-detection.js"></script>
|
||||
<script src="/static/capabilities/midi-input.js"></script>
|
||||
<script src="/static/capabilities/interface-scale.js"></script>
|
||||
<script defer src="/static/capabilities/library-card-actions.js"></script>
|
||||
<script defer src="/static/capabilities/visualization.js"></script>
|
||||
<script defer src="/static/capabilities/note-detection.js"></script>
|
||||
<script defer src="/static/capabilities/midi-input.js"></script>
|
||||
<script defer src="/static/capabilities/interface-scale.js"></script>
|
||||
</head>
|
||||
<body class="h-screen flex overflow-hidden bg-fb-sidebar text-fb-text font-display">
|
||||
|
||||
@@ -1225,64 +1241,64 @@
|
||||
</main>
|
||||
<!-- /#v3-main -->
|
||||
|
||||
<script src="/static/highway.js"></script>
|
||||
<script src="/static/vendor/lottie.min.js"></script>
|
||||
<script src="/static/lottie-api.js"></script>
|
||||
<script src="/static/app.js"></script>
|
||||
<script src="/static/audio-mixer.js"></script>
|
||||
<script src="/static/vendor/shepherd.min.js"></script>
|
||||
<script src="/static/tour-engine.js"></script>
|
||||
<script defer src="/static/highway.js"></script>
|
||||
<script defer src="/static/vendor/lottie.min.js"></script>
|
||||
<script defer src="/static/lottie-api.js"></script>
|
||||
<script defer src="/static/app.js"></script>
|
||||
<script defer src="/static/audio-mixer.js"></script>
|
||||
<script defer src="/static/vendor/shepherd.min.js"></script>
|
||||
<script defer src="/static/tour-engine.js"></script>
|
||||
<!-- fee[dB]ack v0.3.0 shell: brand helper, then the shell (sidebar/topbar/
|
||||
routing). Loaded after app.js/audio-mixer so window.showScreen and
|
||||
window.feedBack(.audio) exist; dashboard.js is filled in prompt 13. -->
|
||||
<script src="/static/v3/brand.js"></script>
|
||||
<script src="/static/v3/shell.js"></script>
|
||||
<script defer src="/static/v3/brand.js"></script>
|
||||
<script defer src="/static/v3/shell.js"></script>
|
||||
<!-- Progression (spec 010): theme-core before profile.js so the equipped
|
||||
theme/avatar frame apply with the first badge render; progression-core
|
||||
registers the `progression` capability owner + window.v3Progression. -->
|
||||
<script src="/static/v3/theme-core.js"></script>
|
||||
<script src="/static/v3/progression-core.js"></script>
|
||||
<script src="/static/v3/notifications.js"></script>
|
||||
<script src="/static/v3/profile.js"></script>
|
||||
<script src="/static/v3/progress.js"></script>
|
||||
<script src="/static/v3/shop.js"></script>
|
||||
<script src="/static/v3/tuner-core.js"></script>
|
||||
<script src="/static/v3/badges.js"></script>
|
||||
<script src="/static/v3/stats-recorder.js"></script>
|
||||
<script src="/static/v3/live-performance-hud.js"></script>
|
||||
<script src="/static/v3/scoreboard-pref.js"></script>
|
||||
<script src="/static/v3/venue-viz.js"></script>
|
||||
<script src="/static/v3/venue-instrument-pov.js"></script>
|
||||
<script defer src="/static/v3/theme-core.js"></script>
|
||||
<script defer src="/static/v3/progression-core.js"></script>
|
||||
<script defer src="/static/v3/notifications.js"></script>
|
||||
<script defer src="/static/v3/profile.js"></script>
|
||||
<script defer src="/static/v3/progress.js"></script>
|
||||
<script defer src="/static/v3/shop.js"></script>
|
||||
<script defer src="/static/v3/tuner-core.js"></script>
|
||||
<script defer src="/static/v3/badges.js"></script>
|
||||
<script defer src="/static/v3/stats-recorder.js"></script>
|
||||
<script defer src="/static/v3/live-performance-hud.js"></script>
|
||||
<script defer src="/static/v3/scoreboard-pref.js"></script>
|
||||
<script defer src="/static/v3/venue-viz.js"></script>
|
||||
<script defer src="/static/v3/venue-instrument-pov.js"></script>
|
||||
<!-- venue-mood-fx must load before venue-scene-3d: the scene bridge reads
|
||||
window.v3VenueMoodFx.getMotion() synchronously at boot when the saved
|
||||
viz is 'venue'; loading it after falls back to 'subtle' and ignores a
|
||||
saved 'off'/'full' motion preference on first paint. -->
|
||||
<script src="/static/v3/venue-mood-fx.js"></script>
|
||||
<script src="/static/v3/venue-scene-3d.js"></script>
|
||||
<script src="/static/v3/playlists.js"></script>
|
||||
<script src="/static/v3/audio-routing.js"></script>
|
||||
<script src="/static/v3/live-guitar-tone-source.js"></script>
|
||||
<script src="/static/v3/pedal-cables.js"></script>
|
||||
<script src="/static/v3/plugins-page.js"></script>
|
||||
<script src="/static/v3/card-actions-core.js"></script>
|
||||
<script defer src="/static/v3/venue-mood-fx.js"></script>
|
||||
<script defer src="/static/v3/venue-scene-3d.js"></script>
|
||||
<script defer src="/static/v3/playlists.js"></script>
|
||||
<script defer src="/static/v3/audio-routing.js"></script>
|
||||
<script defer src="/static/v3/live-guitar-tone-source.js"></script>
|
||||
<script defer src="/static/v3/pedal-cables.js"></script>
|
||||
<script defer src="/static/v3/plugins-page.js"></script>
|
||||
<script defer src="/static/v3/card-actions-core.js"></script>
|
||||
<!-- Before songs.js: the songs toolbar calls the match-review chip hook
|
||||
on build, so the module must already be registered. -->
|
||||
<script src="/static/v3/match-review.js"></script>
|
||||
<script defer src="/static/v3/match-review.js"></script>
|
||||
<!-- Before songs.js: the drawer art click + card ⋮ "Change cover…" open
|
||||
the cover picker (window.__fbOpenImagePicker). -->
|
||||
<script src="/static/v3/image-picker.js"></script>
|
||||
<script src="/static/v3/songs.js"></script>
|
||||
<script src="/static/v3/lessons.js"></script>
|
||||
<script src="/static/v3/dashboard.js"></script>
|
||||
<script src="/static/v3/settings.js"></script>
|
||||
<script src="/static/v3/interface-size-ui.js"></script>
|
||||
<script defer src="/static/v3/image-picker.js"></script>
|
||||
<script defer src="/static/v3/songs.js"></script>
|
||||
<script defer src="/static/v3/lessons.js"></script>
|
||||
<script defer src="/static/v3/dashboard.js"></script>
|
||||
<script defer src="/static/v3/settings.js"></script>
|
||||
<script defer src="/static/v3/interface-size-ui.js"></script>
|
||||
<!-- First-run home tour: spotlights the home cards via the shared tour
|
||||
engine (tour-engine.js, loaded above). Auto-runs once after onboarding
|
||||
(triggered from profile.js finish()); replayable from the "?" menu. -->
|
||||
<script src="/static/v3/onboarding-tour.js"></script>
|
||||
<script src="/static/v3/interface-size-nudge.js"></script>
|
||||
<script src="/static/v3/feedbarcade.js"></script>
|
||||
<script src="/static/v3/player-chrome.js"></script>
|
||||
<script defer src="/static/v3/onboarding-tour.js"></script>
|
||||
<script defer src="/static/v3/interface-size-nudge.js"></script>
|
||||
<script defer src="/static/v3/feedbarcade.js"></script>
|
||||
<script defer src="/static/v3/player-chrome.js"></script>
|
||||
<script>
|
||||
// Navbar scroll effect
|
||||
window.addEventListener('scroll', () => {
|
||||
|
||||
@@ -71,7 +71,9 @@
|
||||
setTimeout(maybeNudge, 4000);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', start, { once: true });
|
||||
} else {
|
||||
start();
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
// Settings markup is static, but re-sync when settings.js signals it wired.
|
||||
document.addEventListener('v3:settings-rendered', function () { sync(); });
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', function () { sync(); }, { once: true });
|
||||
} else {
|
||||
sync();
|
||||
|
||||
@@ -94,7 +94,9 @@
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
|
||||
@@ -303,7 +303,9 @@
|
||||
const sm = root && root.feedBack;
|
||||
if (sm) bindRuntime(sm);
|
||||
};
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -913,7 +913,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
wireSettingsCard();
|
||||
wireScreenTeardown();
|
||||
|
||||
@@ -362,6 +362,8 @@
|
||||
syncActivation();
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', init);
|
||||
else init();
|
||||
})();
|
||||
|
||||
@@ -440,6 +440,8 @@
|
||||
});
|
||||
}
|
||||
function boot() { renderPlaylists(); renderSaved(); }
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
else boot();
|
||||
})();
|
||||
|
||||
@@ -579,6 +579,8 @@
|
||||
}, { passive: true });
|
||||
|
||||
function boot() { render(); }
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
else boot();
|
||||
})();
|
||||
|
||||
@@ -792,7 +792,9 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -320,7 +320,9 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -241,7 +241,9 @@
|
||||
};
|
||||
|
||||
_registerOwner();
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', () => { refresh(); }, { once: true });
|
||||
} else {
|
||||
refresh();
|
||||
|
||||
@@ -200,7 +200,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', init, { once: true });
|
||||
} else {
|
||||
init();
|
||||
|
||||
+3
-1
@@ -399,7 +399,9 @@
|
||||
setTimeout(refreshHomeTitle, 700);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
|
||||
+3
-1
@@ -203,7 +203,9 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -511,7 +511,9 @@
|
||||
const sm = root && root.feedBack;
|
||||
if (sm) bindRuntime(sm);
|
||||
};
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -242,7 +242,9 @@
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
const boot = () => bindRuntime();
|
||||
if (document.readyState === 'loading') {
|
||||
// `defer` runs this at readyState 'interactive' — later scripts have not
|
||||
// evaluated yet, so wait for DOMContentLoaded (see static/v3/index.html).
|
||||
if (document.readyState !== 'complete') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -71,13 +72,39 @@ def test_capability_visualizer_waits_for_registry_instead_of_hard_error():
|
||||
def test_app_shell_loads_capability_registry_before_app_runtime():
|
||||
source = (ROOT / "static" / "v3" / "index.html").read_text(encoding="utf-8")
|
||||
|
||||
assert '<script src="/static/capabilities.js"></script>' in source
|
||||
assert '<script src="/static/capabilities/library.js"></script>' in source
|
||||
assert re.search(r'<script[^>]+src="/static/capabilities\.js"', source)
|
||||
assert re.search(r'<script[^>]+src="/static/capabilities/library\.js"', source)
|
||||
assert source.index('/static/diagnostics.js') < source.index('/static/capabilities.js')
|
||||
assert source.index('/static/capabilities.js') < source.index('/static/capabilities/library.js')
|
||||
assert source.index('/static/capabilities/library.js') < source.index('/static/app.js')
|
||||
|
||||
|
||||
def test_every_external_script_defers_so_document_order_is_execution_order():
|
||||
"""The shell's scripts must all execute in document order.
|
||||
|
||||
`capabilities.js` builds the `window.feedBack` bus and must run before
|
||||
`app.js`, which calls `window.feedBack.on(...)` at top level. Today document
|
||||
order gives that for free, because every script is a parse-time classic one.
|
||||
|
||||
That guarantee survives the ES-module migration ONLY while no script is a
|
||||
*plain* classic script: `defer` and `type="module"` scripts share one
|
||||
"execute after parsing" list and run in document order, but a plain classic
|
||||
script runs DURING parse — ahead of every deferred one. So the moment
|
||||
capabilities.js becomes a module while app.js is still plain, app.js runs
|
||||
first and `window.feedBack.on` is undefined.
|
||||
|
||||
Pinning "no plain external scripts" is what keeps that from silently
|
||||
regressing as tags flip to type="module" one at a time.
|
||||
"""
|
||||
source = (ROOT / "static" / "v3" / "index.html").read_text(encoding="utf-8")
|
||||
|
||||
plain = [
|
||||
tag for tag in re.findall(r'<script\b[^>]*\bsrc=[^>]*>', source)
|
||||
if 'defer' not in tag and 'async' not in tag and 'type="module"' not in tag
|
||||
]
|
||||
assert not plain, f"external scripts that would jump the deferred queue: {plain}"
|
||||
|
||||
|
||||
def test_capability_registry_exposes_claim_dispatch_and_ready_contracts():
|
||||
source = (ROOT / "static" / "capabilities.js").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user