fix(core): Fixed package and file path typos and removed GitLab CI configuration.
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartssr',
|
||||
version: '1.0.38',
|
||||
description: 'a smart server side renderer supporting shadow dom'
|
||||
version: '1.0.39',
|
||||
description: 'A smart server-side renderer that supports shadow DOM.'
|
||||
}
|
||||
|
@ -1,103 +1,151 @@
|
||||
/**
|
||||
* Represents a style object on a custom element.
|
||||
*/
|
||||
export interface IStyleObject {
|
||||
cssText: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a constructor for a custom element that may have a static `styles` property.
|
||||
*/
|
||||
export interface ICustomElementConstructor extends Function {
|
||||
styles?: IStyleObject[];
|
||||
}
|
||||
|
||||
declare var document: Document;
|
||||
export function serializeFunction(rootNode: Node) {
|
||||
const uuidv4 = () => {
|
||||
return 'unixxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
|
||||
/**
|
||||
* Serializes shadow-DOM-based elements into light DOM by:
|
||||
* 1. Moving slotted children out of their <slot> elements.
|
||||
* 2. Converting shadow-root CSS to scoping-based CSS.
|
||||
* 3. Appending styles and elements back onto the node.
|
||||
*
|
||||
* @param rootNode The root node under which all child nodes will be serialized.
|
||||
*/
|
||||
export function serializeFunction(rootNode: Node): void {
|
||||
/**
|
||||
* Instead of a UUID, we use a globally incrementing counter for stable,
|
||||
* predictable IDs across multiple serializations of the same tree.
|
||||
*/
|
||||
let globalIdCounter = 0;
|
||||
const generateStableId = (): string => {
|
||||
globalIdCounter++;
|
||||
// Feel free to adjust the prefix/suffix as you like
|
||||
return `unix-${globalIdCounter}`;
|
||||
};
|
||||
|
||||
const prependCss = (uuidID: string, styleTemplate: string) => {
|
||||
/**
|
||||
* Prepend the generated ID class to CSS in order to emulate :host scoping.
|
||||
*/
|
||||
const prependCss = (uuidID: string, styleTemplate: string): string => {
|
||||
// Ensure there's at least one :host, so the user code typically expects scoping
|
||||
if (!styleTemplate.includes(':host')) {
|
||||
styleTemplate = `:host {}\n\n${styleTemplate}`;
|
||||
}
|
||||
|
||||
// Replace patterns that should be placed under the .uuidID scope
|
||||
styleTemplate = styleTemplate.replace(/}[ \t\n]+\./g, `}\n\n.${uuidID} .`);
|
||||
styleTemplate = styleTemplate.replace(/}[ \t\n]+\*/g, `}\n\n.${uuidID} *`);
|
||||
styleTemplate = styleTemplate.replace(/\(\[/g, `[`);
|
||||
styleTemplate = styleTemplate.replace(/\]\)/g, `]`);
|
||||
styleTemplate = styleTemplate.replace(/:host/g, `.${uuidID}`);
|
||||
|
||||
// Replace :host with .uuidID
|
||||
styleTemplate = styleTemplate.replace(/:host/g, `.${uuidID}`);
|
||||
|
||||
styleTemplate = styleTemplate.replace(/{[ \t\n]+\./g, `{\n\n.${uuidID} .`);
|
||||
styleTemplate = styleTemplate.replace(/}[ \t\n]+img/g, `}\n\n.${uuidID} img`);
|
||||
styleTemplate = styleTemplate.replace(/}[ \t\n]+div/g, `}\n\n.${uuidID} div`);
|
||||
|
||||
return styleTemplate;
|
||||
};
|
||||
const loopProtection: any[] = [];
|
||||
|
||||
function serializeNode(nodeArg: HTMLElement | any, logThis = false) {
|
||||
if (loopProtection.includes(nodeArg)) {
|
||||
// Keep track of visited nodes to avoid loops
|
||||
const visitedNodes = new WeakSet<Node>();
|
||||
|
||||
/**
|
||||
* Recursively serializes a node, moving shadow DOM content into light DOM.
|
||||
*
|
||||
* @param nodeArg The node to serialize.
|
||||
*/
|
||||
function serializeNode(nodeArg: HTMLElement | Node): void {
|
||||
// Prevent re-serializing the same node
|
||||
if (visitedNodes.has(nodeArg)) {
|
||||
return;
|
||||
}
|
||||
loopProtection.push(nodeArg);
|
||||
// console.log(nodeArg.nodeName);
|
||||
if (nodeArg.shadowRoot) {
|
||||
visitedNodes.add(nodeArg);
|
||||
|
||||
// If the node has a shadowRoot, move everything into light DOM
|
||||
if (
|
||||
nodeArg instanceof HTMLElement &&
|
||||
nodeArg.shadowRoot &&
|
||||
nodeArg.shadowRoot instanceof ShadowRoot
|
||||
) {
|
||||
// Mark node for SSR
|
||||
nodeArg.setAttribute('smartssr', 'yes');
|
||||
|
||||
// lets handle the current node
|
||||
const nodeUUID = uuidv4();
|
||||
// Generate a stable ID for CSS scoping
|
||||
const nodeId = generateStableId();
|
||||
nodeArg.classList.add(nodeId);
|
||||
|
||||
nodeArg.classList.add(nodeUUID);
|
||||
|
||||
// find all slots
|
||||
// Move slotted nodes from <slot> to the light DOM
|
||||
const slots = nodeArg.shadowRoot.querySelectorAll('slot');
|
||||
|
||||
// handle slot element
|
||||
const slotsForMove: HTMLSlotElement[] = [];
|
||||
slots.forEach((slot: any) => {
|
||||
slotsForMove.push(slot);
|
||||
});
|
||||
|
||||
const slotsForMove: HTMLSlotElement[] = Array.from(slots);
|
||||
for (const slot of slotsForMove) {
|
||||
const slottedLightNodesForMove: any[] = [];
|
||||
slot.assignedNodes().forEach((lightNode) => slottedLightNodesForMove.push(lightNode));
|
||||
slottedLightNodesForMove.forEach((lightNode) =>
|
||||
slot.parentNode.insertBefore(lightNode, slot)
|
||||
);
|
||||
const slottedLightNodesForMove = slot.assignedNodes();
|
||||
slottedLightNodesForMove.forEach((lightNode) => {
|
||||
if (slot.parentNode) {
|
||||
slot.parentNode.insertBefore(lightNode, slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// lets modify the css
|
||||
const childNodes = nodeArg.shadowRoot.childNodes;
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
const noteForAppending: HTMLElement[] = [];
|
||||
// Prepare nodes to append after transformations
|
||||
const nodesToAppend: Node[] = [];
|
||||
|
||||
// lets care about static css first
|
||||
if (
|
||||
(nodeArg.constructor as any).styles &&
|
||||
(nodeArg.constructor as any).styles instanceof Array
|
||||
) {
|
||||
for (const objectArg of (nodeArg.constructor as any).styles) {
|
||||
// Some frameworks store static styles in a static `styles` property on the constructor
|
||||
const elementConstructor = nodeArg.constructor as ICustomElementConstructor;
|
||||
if (Array.isArray(elementConstructor.styles)) {
|
||||
for (const styleObj of elementConstructor.styles) {
|
||||
const styleTag = document.createElement('style');
|
||||
styleTag.textContent = prependCss(nodeUUID, objectArg.cssText);
|
||||
noteForAppending.push(styleTag);
|
||||
styleTag.textContent = prependCss(nodeId, styleObj.cssText);
|
||||
nodesToAppend.push(styleTag);
|
||||
}
|
||||
}
|
||||
|
||||
childNodes.forEach((childNode: ChildNode) => {
|
||||
// Convert existing shadow DOM childNodes
|
||||
nodeArg.shadowRoot.childNodes.forEach((childNode: ChildNode) => {
|
||||
if (childNode instanceof HTMLElement) {
|
||||
// If it's a <style>, prepend the scoping class
|
||||
if (childNode.tagName === 'STYLE') {
|
||||
childNode.textContent = prependCss(nodeUUID, childNode.textContent);
|
||||
childNode.textContent = prependCss(nodeId, childNode.textContent ?? '');
|
||||
} else {
|
||||
serializeNode(childNode, logThis);
|
||||
// Recursively serialize sub-elements
|
||||
serializeNode(childNode);
|
||||
}
|
||||
noteForAppending.push(childNode);
|
||||
nodesToAppend.push(childNode);
|
||||
} else {
|
||||
// For non-HTMLElement child nodes, just append
|
||||
nodesToAppend.push(childNode);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear the element and re-append the now converted nodes
|
||||
while (nodeArg.firstChild) {
|
||||
nodeArg.removeChild(nodeArg.firstChild);
|
||||
}
|
||||
noteForAppending.forEach((childNode) => {
|
||||
nodeArg.append(childNode);
|
||||
});
|
||||
for (const childNode of nodesToAppend) {
|
||||
nodeArg.appendChild(childNode);
|
||||
}
|
||||
} else {
|
||||
nodeArg.childNodes.forEach((nodeArg2: any) => {
|
||||
serializeNode(nodeArg2, logThis);
|
||||
// If it's a normal (light DOM) node, just recurse into its childNodes
|
||||
nodeArg.childNodes.forEach((child) => {
|
||||
serializeNode(child);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Start serialization from the children of the provided root
|
||||
rootNode.childNodes.forEach((nodeArg) => {
|
||||
serializeNode(nodeArg);
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user