coolstyleserver 2.3.4

a proxy server for stylesheet hot reloading
let registry = new Map();
let sources = new Map();
let base = new URL(import.meta.url);
let coolBase = base.pathname.substring(0, base.pathname.lastIndexOf("/"));
let esrc = new EventSource(`${coolBase}/watch`);

esrc.addEventListener("message", async (event) => {
	let data = JSON.parse(event.data);
	let updates = new Set();

	for (let pathname of data) {
		pathname = new URL(pathname, base).pathname;

		let sheets = sources.get(pathname);

		if (!sheets) continue;

		for (let sheet of sheets) {
			updates.add(updateSheet(sheet, pathname));
		}
	}

	await Promise.allSettled([...updates]);
});

async function createSheet(root, index, pathname, media) {
	media ??= "all";

	let item = registry.get(pathname) ?? new Map();

	registry.set(pathname, item);

	let sheet = item.get(media) ?? new CSSStyleSheet({media: media});

	item.set(media, sheet);

	root.adoptedStyleSheets.splice(index, 1, sheet);

	await updateSheet(sheet, pathname);
}

async function updateSheet(sheet, pathname) {
	let url = new URL(`${coolBase}/fetch`, base);

	url.searchParams.append("pathname", pathname);

	let res = await fetch(url);
	let json = await res.json();

	if (json.css.includes("@import")) {
		return;
	}

	for (let src of [pathname].concat(json.sources ?? [])) {
		sources.set(src, sources.get(src) ?? new Set());

		sources.get(src).add(sheet);
	}

	sheet.replaceSync(json.css);
}

class CoolStylesheet extends HTMLLinkElement {
	static get observedAttributes() {
		return ["media"];
	}

	pathname;

	constructor() {
		super();

		let url = new URL(this.href);

		if (url.host !== new URL(import.meta.url).host) return;

		this.pathname = url.pathname;

		let root = this.getRootNode();
		let media = this.getAttribute("media");

		createSheet(
			root,
			root.adoptedStyleSheets.length,
			this.pathname,
			media
		).then(() => {
			this.sheet.disabled = true;
		});
	}

	async attributeChangedCallback(_, old_media, new_media) {
		if (old_media === new_media) {
			return;
		}

		let root = this.getRootNode();
		let old_sheet = registry.get(this.pathname)?.get(old_media ?? "all");
		let index = root.adoptedStyleSheets.lastIndexOf(old_sheet);

		await createSheet(root, index, this.pathname, new_media);
	}
}

customElements.define("cool-stylesheet", CoolStylesheet, {extends: "link"});