perf(post): lazy-link maplibre's stylesheet instead of bundling it
The engine was already a lazy import(), but its CSS was a static one — the usual workaround for esbuild never emitting a <link> for a code-split chunk's stylesheet (R10). That folded the whole vendor sheet into post-form.css, so every /post load paid for a panel most submits never open, and ~78% of those bytes were rules for controls this map never creates (popups, geolocate, zoom, compass, fullscreen, terrain, scale). Build the vendor sheet as its own css-compiled/maplibre-gl.css and have location-map.js <link> it at panel-open, concurrently with the engine's import(). Keeping the file intact rather than hand-picking the ~16 selectors in use means a maplibre upgrade can't silently un-style the map. post-form.css 92,244 → 26,784 raw (14,528 → 5,631 gzip) The href resolves from import.meta.url, so it is correct under any Grav base path without threading a URL through the template — the panel is built entirely in JS, so there is no element to hang a data-attr on. A failed stylesheet load resolves rather than rejects: the map degrades to unstyled, never to absent. The existing lazy-load guard spec already matched every maplibre-gl URL, so it covers the stylesheet too; its message now says so, and a new test asserts the other half — that opening the panel does link and apply the sheet.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -6,7 +6,8 @@
|
||||
* needs (KTD1).
|
||||
*
|
||||
* maplibre-gl itself is lazy-imported on first call (KTD3) so an ordinary
|
||||
* GPS-only submit, where the panel is never opened, never fetches it. The
|
||||
* GPS-only submit, where the panel is never opened, never fetches it. Its
|
||||
* STYLESHEET is lazy too — see ensureMaplibreCss below. The
|
||||
* created map/marker are cached in module scope and reused on every
|
||||
* subsequent call — see the panel's toggle-open handler in post-form.js,
|
||||
* which calls this on every open and then always calls the returned
|
||||
@@ -22,7 +23,48 @@
|
||||
*/
|
||||
import { MAP_STYLE } from './map-style.js';
|
||||
|
||||
var cached = null; // { container, promise }
|
||||
var cached = null; // { container, promise }
|
||||
var cssPromise = null; // in-flight/settled <link> load for maplibre's stylesheet
|
||||
|
||||
/**
|
||||
* Inject maplibre-gl's stylesheet on demand, once.
|
||||
*
|
||||
* esbuild will not emit a <link> for a code-split chunk's CSS (R10), so the
|
||||
* usual fix is a static `import 'maplibre-gl/dist/maplibre-gl.css'` up in
|
||||
* post-form.js — but that defeats the lazy import it accompanies: every /post
|
||||
* load would then pay ~9 KB gzip of vendor CSS for a panel most submits never
|
||||
* open. So package.json builds the vendor stylesheet as its own
|
||||
* css-compiled/maplibre-gl.css and we <link> it here instead, in parallel with
|
||||
* the engine's import(). Keeping the vendor file intact (rather than
|
||||
* hand-picking the ~16 selectors this panel actually uses) means a maplibre
|
||||
* upgrade can't silently un-style the map.
|
||||
*
|
||||
* The href is resolved from import.meta.url — the bundle is ESM, so this is the
|
||||
* URL of this chunk under `js/post/`, which makes the link correct under any
|
||||
* Grav base path without threading a URL through the template. (The panel is
|
||||
* built entirely in JS, so there is no Twig element to hang a data-attr on.)
|
||||
*
|
||||
* Resolves on error as well as load: a missing stylesheet must degrade to an
|
||||
* unstyled-but-functional map, never block it. On error the dead <link> and the
|
||||
* memo are dropped so a later retry re-attempts it, mirroring the map cache's
|
||||
* clear-on-failure below.
|
||||
*/
|
||||
function ensureMaplibreCss() {
|
||||
if (cssPromise) return cssPromise;
|
||||
cssPromise = new Promise(function (resolve) {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = new URL('../../css-compiled/maplibre-gl.css', import.meta.url).href;
|
||||
link.onload = function () { resolve(); };
|
||||
link.onerror = function () {
|
||||
link.remove();
|
||||
cssPromise = null;
|
||||
resolve();
|
||||
};
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
return cssPromise;
|
||||
}
|
||||
|
||||
function buildPinElement() {
|
||||
var el = document.createElement('div');
|
||||
@@ -35,7 +77,14 @@ export function getOrCreateLocationMap(container, onDragEnd) {
|
||||
return cached.promise;
|
||||
}
|
||||
|
||||
var promise = import('maplibre-gl').then(function (mod) {
|
||||
// Stylesheet and engine fetch concurrently; awaiting the CSS before
|
||||
// constructing the Map means maplibre never measures the container against
|
||||
// half-applied styles.
|
||||
var promise = Promise.all([
|
||||
import('maplibre-gl'),
|
||||
ensureMaplibreCss()
|
||||
]).then(function (loaded) {
|
||||
var mod = loaded[0];
|
||||
var maplibregl = mod.default || mod;
|
||||
var map = new maplibregl.Map({
|
||||
container: container,
|
||||
|
||||
@@ -10,9 +10,13 @@
|
||||
import EasyMDE from 'easymde';
|
||||
import Sortable from 'sortablejs';
|
||||
import 'easymde/dist/easymde.min.css';
|
||||
// maplibre-gl's CSS is a static import — a dynamically-imported chunk's CSS is
|
||||
// never auto-linked (R10). The JS itself stays a lazy import (see location-map.js).
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
// NOTE: maplibre-gl's CSS is deliberately NOT imported here. esbuild never emits
|
||||
// a <link> for a dynamically-imported chunk's CSS (R10), and a static import
|
||||
// would have folded 68 KB / ~9 KB gzip of vendor stylesheet — ~78% of it rules
|
||||
// for controls this panel never creates — into post-form.css on every /post
|
||||
// load. location-map.js instead injects a <link> to the separately-built
|
||||
// css-compiled/maplibre-gl.css at the moment it lazy-imports the engine, so an
|
||||
// ordinary GPS-only submit pays nothing for either half.
|
||||
import './post-form.css';
|
||||
import { apiSend, apiErrorMsg } from './api-utils.js';
|
||||
import { getOrCreateLocationMap } from './location-map.js';
|
||||
|
||||
Reference in New Issue
Block a user