fix(core): Fixed package and file path typos and removed GitLab CI configuration.

This commit is contained in:
Philipp Kunz 2025-01-01 07:14:01 +01:00
parent 51fe0fe5d2
commit 74f494131f
8 changed files with 8323 additions and 3402 deletions

View File

@ -119,6 +119,6 @@ jobs:
run: | run: |
npmci node install stable npmci node install stable
npmci npm install npmci npm install
pnpm install -g @gitzone/tsdoc pnpm install -g @git.zone/tsdoc
npmci command tsdoc npmci command tsdoc
continue-on-error: true continue-on-error: true

View File

@ -1,128 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
before_script:
- pnpm install -g pnpm
- pnpm install -g @shipzone/npmci
- npmci npm prepare
# ====================
# security stage
# ====================
# ====================
# security stage
# ====================
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod
tags:
- lossless
- docker
allow_failure: true
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --dev
tags:
- lossless
- docker
allow_failure: true
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testBuild:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci npm build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- lossless
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g typescript
- npmci npm prepare
- npmci npm install
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install stable
- npmci npm install
- npmci command npm run buildDocs
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

51
changelog.md Normal file
View File

@ -0,0 +1,51 @@
# Changelog
## 2025-01-01 - 1.0.39 - fix(core)
Fixed package and file path typos and removed GitLab CI configuration.
- Corrected the package scope typos from '@gitzone' to '@git.zone'.
- Removed unused GitLab CI YAML configuration file.
- Normalized CSS scoping for shadow DOM serialization with incremental ID generation.
- Updated smartfile dependency version in package.json.
## 2024-05-29 - 1.0.38 - Configuration Updates
Minor updates to project configuration files.
- Updated description
- Revised tsconfig
- Updated npmextra.json: githost
## 2023-07-27 - 1.0.35 to 1.0.37 - Core Fixes
Maintenance releases focusing on core updates.
- Multiple fixes to the core module
## 2021-08-16 - 1.0.27 to 1.0.34 - Core Enhancements
Several updates targeting enhancements in the core functionality.
- Series of core module updates
## 2021-05-19 - 1.0.23 to 1.0.26 - Core Updates
Focused update cycle on improving core stability.
- Addressed core update issues
## 2021-01-15 - 1.0.19 to 1.0.22 - Core Fix Cycle
Incremental core updates for better performance.
- Applied multiple core updates
## 2020-08-06 - 1.0.10 to 1.0.18 - Core Adjustments
Consistent improvements and fixes in the core module.
- Ongoing core updates
## 2020-03-01 - 1.0.6 to 1.0.9 - Iterative Core Fixes
Maintaining core functionality with multiple updates.
- Continued core updates
## 2020-02-12 - 1.0.1 to 1.0.5 - Initial Core Fixes
Initial round of updates on core functionality.
- Initial set of core fixes

View File

@ -1,4 +1,4 @@
Copyright (c) 2020 Lossless GmbH (hello@lossless.com) Copyright (c) 2020 Task Venture Capital GmbH (hello@lossless.com)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -14,15 +14,15 @@
"buildDocs": "tsdoc" "buildDocs": "tsdoc"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.66", "@git.zone/tsbuild": "^2.1.66",
"@gitzone/tsrun": "^1.2.44", "@git.zone/tsrun": "^1.2.44",
"@gitzone/tstest": "^1.0.77", "@git.zone/tstest": "^1.0.77",
"@push.rocks/tapbundle": "^5.0.8", "@push.rocks/tapbundle": "^5.0.8",
"@types/node": "^20.4.5" "@types/node": "^22.10.3"
}, },
"dependencies": { "dependencies": {
"@push.rocks/smartdelay": "^3.0.1", "@push.rocks/smartdelay": "^3.0.1",
"@push.rocks/smartfile": "^10.0.26", "@push.rocks/smartfile": "^11.0.23",
"@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartpromise": "^4.0.2", "@push.rocks/smartpromise": "^4.0.2",
"@push.rocks/smartpuppeteer": "^2.0.2", "@push.rocks/smartpuppeteer": "^2.0.2",

11370
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/** /**
* autocreated commitinfo by @pushrocks/commitinfo * autocreated commitinfo by @push.rocks/commitinfo
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartssr', name: '@push.rocks/smartssr',
version: '1.0.38', version: '1.0.39',
description: 'a smart server side renderer supporting shadow dom' description: 'A smart server-side renderer that supports shadow DOM.'
} }

View File

@ -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; declare var document: Document;
export function serializeFunction(rootNode: Node) {
const uuidv4 = () => { /**
return 'unixxxxxxxxxxx'.replace(/[xy]/g, (c) => { * Serializes shadow-DOM-based elements into light DOM by:
const r = (Math.random() * 16) | 0; * 1. Moving slotted children out of their <slot> elements.
const v = c === 'x' ? r : (r & 0x3) | 0x8; * 2. Converting shadow-root CSS to scoping-based CSS.
return v.toString(16); * 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')) { if (!styleTemplate.includes(':host')) {
styleTemplate = `:host {}\n\n${styleTemplate}`; 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(/}[ \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(/\]\)/g, `]`); styleTemplate = styleTemplate.replace(/\]\)/g, `]`);
styleTemplate = styleTemplate.replace(/:host/g, `.${uuidID}`);
// Replace :host with .uuidID
styleTemplate = styleTemplate.replace(/:host/g, `.${uuidID}`); styleTemplate = styleTemplate.replace(/:host/g, `.${uuidID}`);
styleTemplate = styleTemplate.replace(/{[ \t\n]+\./g, `{\n\n.${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]+img/g, `}\n\n.${uuidID} img`);
styleTemplate = styleTemplate.replace(/}[ \t\n]+div/g, `}\n\n.${uuidID} div`); styleTemplate = styleTemplate.replace(/}[ \t\n]+div/g, `}\n\n.${uuidID} div`);
return styleTemplate; return styleTemplate;
}; };
const loopProtection: any[] = [];
function serializeNode(nodeArg: HTMLElement | any, logThis = false) { // Keep track of visited nodes to avoid loops
if (loopProtection.includes(nodeArg)) { 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; return;
} }
loopProtection.push(nodeArg); visitedNodes.add(nodeArg);
// console.log(nodeArg.nodeName);
if (nodeArg.shadowRoot) { // 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'); nodeArg.setAttribute('smartssr', 'yes');
// lets handle the current node // Generate a stable ID for CSS scoping
const nodeUUID = uuidv4(); const nodeId = generateStableId();
nodeArg.classList.add(nodeId);
nodeArg.classList.add(nodeUUID); // Move slotted nodes from <slot> to the light DOM
// find all slots
const slots = nodeArg.shadowRoot.querySelectorAll('slot'); const slots = nodeArg.shadowRoot.querySelectorAll('slot');
const slotsForMove: HTMLSlotElement[] = Array.from(slots);
// handle slot element
const slotsForMove: HTMLSlotElement[] = [];
slots.forEach((slot: any) => {
slotsForMove.push(slot);
});
for (const slot of slotsForMove) { for (const slot of slotsForMove) {
const slottedLightNodesForMove: any[] = []; const slottedLightNodesForMove = slot.assignedNodes();
slot.assignedNodes().forEach((lightNode) => slottedLightNodesForMove.push(lightNode)); slottedLightNodesForMove.forEach((lightNode) => {
slottedLightNodesForMove.forEach((lightNode) => if (slot.parentNode) {
slot.parentNode.insertBefore(lightNode, slot) slot.parentNode.insertBefore(lightNode, slot);
); }
});
} }
// lets modify the css // Prepare nodes to append after transformations
const childNodes = nodeArg.shadowRoot.childNodes; const nodesToAppend: Node[] = [];
// tslint:disable-next-line: prefer-for-of
const noteForAppending: HTMLElement[] = [];
// lets care about static css first // Some frameworks store static styles in a static `styles` property on the constructor
if ( const elementConstructor = nodeArg.constructor as ICustomElementConstructor;
(nodeArg.constructor as any).styles && if (Array.isArray(elementConstructor.styles)) {
(nodeArg.constructor as any).styles instanceof Array for (const styleObj of elementConstructor.styles) {
) {
for (const objectArg of (nodeArg.constructor as any).styles) {
const styleTag = document.createElement('style'); const styleTag = document.createElement('style');
styleTag.textContent = prependCss(nodeUUID, objectArg.cssText); styleTag.textContent = prependCss(nodeId, styleObj.cssText);
noteForAppending.push(styleTag); nodesToAppend.push(styleTag);
} }
} }
childNodes.forEach((childNode: ChildNode) => { // Convert existing shadow DOM childNodes
nodeArg.shadowRoot.childNodes.forEach((childNode: ChildNode) => {
if (childNode instanceof HTMLElement) { if (childNode instanceof HTMLElement) {
// If it's a <style>, prepend the scoping class
if (childNode.tagName === 'STYLE') { if (childNode.tagName === 'STYLE') {
childNode.textContent = prependCss(nodeUUID, childNode.textContent); childNode.textContent = prependCss(nodeId, childNode.textContent ?? '');
} else { } 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) { while (nodeArg.firstChild) {
nodeArg.removeChild(nodeArg.firstChild); nodeArg.removeChild(nodeArg.firstChild);
} }
noteForAppending.forEach((childNode) => { for (const childNode of nodesToAppend) {
nodeArg.append(childNode); nodeArg.appendChild(childNode);
}); }
} else { } else {
nodeArg.childNodes.forEach((nodeArg2: any) => { // If it's a normal (light DOM) node, just recurse into its childNodes
serializeNode(nodeArg2, logThis); nodeArg.childNodes.forEach((child) => {
serializeNode(child);
}); });
} }
} }
// Start serialization from the children of the provided root
rootNode.childNodes.forEach((nodeArg) => { rootNode.childNodes.forEach((nodeArg) => {
serializeNode(nodeArg); serializeNode(nodeArg);
}); });
} }