xtal-sip
Version:
Dynamically "water" a custom element tag with the necessary dependencies to sprout the tag from an inert seedling to a thing of beauty.
107 lines (97 loc) • 4.35 kB
text/typescript
import {preemptiveImport} from './preemptiveImport.js';
import {ICssObserve} from 'css-observe/types.d.js';
import {ConditionalLoadingLookup, PreemptiveLoadingArgumentJS, ConditionalLoadingTuple} from './types.d.js';
const loadedTags = new Set<string>();
let addedCssObserveImport = false;
export function conditionalImport(shadowOrShadowPeer: HTMLElement | DocumentFragment, lookup: ConditionalLoadingLookup){
doManualCheck(shadowOrShadowPeer, lookup);
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', e => {
doManualCheck(shadowOrShadowPeer, lookup);
});
}
const unloadedTags = [];
for(const tagName in lookup){
if(!loadedTags.has(tagName)) unloadedTags.push(tagName);
//loadedTags.add(tagName);
}
if(unloadedTags.length === 0) return;
const parsedTags = unloadedTags.map(tag => parseTag(tag)).flat();
const cssSelector = parsedTags.join(',');
const cssObserve = document.createElement('css-observe') as ICssObserve;
cssObserve.observe = true;
cssObserve.selector = cssSelector;
cssObserve.addEventListener('latest-match-changed', (e: CustomEvent) => {
const tag = e.detail.value as HTMLElement;
// const loadingInstructions = lookup[tag.localName];
// loadingInstructions.forEach(instruction =>{
// preemptiveImport(instruction as PreemptiveLoadingArgumentJS);
// });
doManualCheck(shadowOrShadowPeer, lookup, tag.localName);
});
switch(shadowOrShadowPeer.nodeType){
case 9:
case 11:{
shadowOrShadowPeer.appendChild(cssObserve);
break;
}
default:{
(shadowOrShadowPeer as HTMLElement).insertAdjacentElement('afterend', cssObserve);
}
}
if(!addedCssObserveImport){
addedCssObserveImport = true;
// conditionalImport(shadowOrShadowPeer, {
// 'css-observe':[
// ['css-observe/css-observe.js', () => import('css-observe/css-observe.js'), ({path}) => `//unpkg.com/${path}?module`]
// ]
// });
preemptiveImport(['css-observe/css-observe.js', () => import('css-observe/css-observe.js'), ({path}) => `//unpkg.com/${path}?module`,,]);
}
}
function getContext(loadingInstruction: ConditionalLoadingTuple){
let importOptions = loadingInstruction[3];
if(importOptions === undefined){
importOptions = {};
loadingInstruction[3] = importOptions;
}
return importOptions;
}
function doManualCheck(shadowOrShadowPeer: HTMLElement | DocumentFragment, lookup: ConditionalLoadingLookup, foundTag?: string){
let host = shadowOrShadowPeer.nodeType === 11 ? shadowOrShadowPeer : shadowOrShadowPeer.getRootNode() as Element;
if(host.nodeType === 9){
host = document.firstElementChild;
}
for(const tagName in lookup){
const loadingInstructions = lookup[tagName];
const tags = parseTag(tagName);
let count = 0;
for(const tagName2 of tags){
if(loadedTags.has(tagName2)) continue;
if(tagName2 === foundTag || host.querySelector(tagName2) !== null){
loadedTags.add(tagName2);
loadingInstructions.forEach(loadingInstruction =>{
const clonedLoadingInstruction = {...loadingInstruction};
if(Array.isArray(clonedLoadingInstruction[1])){
clonedLoadingInstruction[1] = clonedLoadingInstruction[1][count];
}
const importOptions = getContext(clonedLoadingInstruction);
importOptions.localName = tagName2;
preemptiveImport(clonedLoadingInstruction as PreemptiveLoadingArgumentJS);
});
}
count++;
}
}
}
const re = /\{([^)]+)\}/g;
function parseTag(tag: string){
const braceSplit = tag.split(re);
if(braceSplit.length === 1) return [tag];
if(braceSplit.length === 3){
const returnArr = [];
const names = braceSplit[1].split('|');
return names.map(name => `${braceSplit[0]}${name}${braceSplit[2]}`);
}
console.error(braceSplit);
}