diff --git a/src/react/core.ts b/src/react/core.ts index 8a5f5a3ab9..69bb56595d 100644 --- a/src/react/core.ts +++ b/src/react/core.ts @@ -1,2 +1 @@ export * from './core/create-component.js'; -export * from './core/ssr-extensions.js'; diff --git a/src/react/core/ssr-extensions.ts b/src/react/core/ssr-extensions.ts deleted file mode 100644 index 7d5a1ba219..0000000000 --- a/src/react/core/ssr-extensions.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* eslint-disable import-x/default, import-x/no-named-as-default-member */ -import { isServer } from 'lit'; -import type { ComponentType, JSXElementConstructor, ReactNode } from 'react'; -import react, { Children, isValidElement } from 'react'; - -type WithChildren = T & { children: ReactNode[] }; - -/** - * Wraps a React wrapped Lit component with additional logic, to detect - * whether a component has children when rendered via SSR. - * If children are available, the attribute `data-named-slots` will be - * set with the detected slot names. - * - * @param component The React wrapped Lit component. - * @returns The React wrapped Lit component with SSR logic if rendered on server. - */ -export const withSsrDataSlotNames =

( - component: ComponentType

, -): ComponentType

=> { - return !isServer - ? component - : (originalProps) => { - const props = originalProps as WithChildren

; - if (!props.children) { - return react.createElement(component, props); - } - - const slots = new Set(); - Children.forEach(props.children, (child) => - slots.add( - (typeof child === 'object' && - !!child && - 'props' in child && - 'slot' in child.props && - child.props.slot) || - 'unnamed', - ), - ); - if (!slots.size) { - return react.createElement(component, props); - } - - const innerProps = { - ...props, - 'data-named-slots': [...slots].sort().join(' '), - }; - return react.createElement(component, innerProps); - }; -}; - -/** Checks whether a ReactNode type has a displayName property. */ -function hasDisplayName( - type: string | JSXElementConstructor, -): type is JSXElementConstructor & { displayName: string } { - return typeof (type as any).displayName === 'string'; -} - -/** - * Wraps a React wrapped Lit component with additional logic, to detect - * whether a component has children when rendered via SSR. - * If children are available, the attribute `data-ssr-child-count` will be - * set with the detected amount of named children. - * - * @param component The React wrapped Lit component. - * @param childNames The child names to check against. - * @returns The React wrapped Lit component with SSR logic if rendered on server. - */ -export const withSsrDataChildCount =

( - childNames: string[], - component: ComponentType

, -): ComponentType

=> { - return !isServer - ? component - : (originalProps) => { - const props = originalProps as WithChildren

; - if (!props.children) { - return react.createElement(component, props); - } - - let counter = 0; - Children.forEach(props.children, (child) => { - if ( - isValidElement(child) && - hasDisplayName(child.type) && - childNames.includes(child.type.displayName) - ) { - counter += 1; - } - }); - if (!counter) { - return react.createElement(component, props); - } - - const innerProps = { - ...props, - 'data-ssr-child-count': counter, - }; - return react.createElement(component, innerProps); - }; -}; diff --git a/tools/manifest/custom-elements-manifest.config.js b/tools/manifest/custom-elements-manifest.config.js index a51e1fdf8d..8dcb6b15cd 100644 --- a/tools/manifest/custom-elements-manifest.config.js +++ b/tools/manifest/custom-elements-manifest.config.js @@ -20,13 +20,6 @@ export function createManifestConfig(library = '') { plugins: [ { analyzePhase({ ts, node, moduleDoc }) { - function setSsrSlotState(classNode) { - const classDoc = moduleDoc.declarations.find( - (declaration) => declaration.name === classNode.name.getText(), - ); - classDoc['_ssrslotstate'] = true; - } - function replace(typeObj, typeName, typeValue) { if (typeObj && typeObj.text) { typeObj.text = typeObj.text.replace(typeName, typeValue); @@ -102,28 +95,7 @@ export function createManifestConfig(library = '') { }); } - if (ts.isNewExpression(node) && node.expression.getText() === 'SbbSlotStateController') { - let classNode = node; - while (classNode) { - if (ts.isClassDeclaration(classNode)) { - setSsrSlotState(classNode); - } - classNode = classNode.parent; - } - } - if (ts.isClassDeclaration(node)) { - /** - * The usage of the `slotState` decorator breaks the logic in the previous block of code, - * since the decorated class has no explicit usage of the `SbbSlotStateController` constructor any more. - */ - const decorators = ts.getDecorators(node); - if (decorators && decorators.length > 0) { - if (decorators.find((e) => e.getText() === '@slotState()')) { - setSsrSlotState(node); - } - } - /** * When a generic T type is used in a superclass declaration, it overrides the type defined in derived class * during the doc generation (as the `value` property in the `SbbFormAssociatedMixinType`). diff --git a/tools/vite/generate-react-wrappers.ts b/tools/vite/generate-react-wrappers.ts index 8367fa047d..edbd83395d 100644 --- a/tools/vite/generate-react-wrappers.ts +++ b/tools/vite/generate-react-wrappers.ts @@ -11,15 +11,7 @@ import { } from 'fs'; import { pathToFileURL } from 'node:url'; -import type { - Package, - Export, - CustomElementDeclaration, - Module, - ClassField, - ClassDeclaration, - Declaration, -} from 'custom-elements-manifest'; +import type { Package, Export, CustomElementDeclaration, Module } from 'custom-elements-manifest'; import type { PluginOption } from 'vite'; import { distDir } from './build-meta.js'; @@ -52,9 +44,6 @@ export function generateReactWrappers( name: 'generate-react-wrappers', config(config) { const packageRoot = pathToFileURL(config.root!); - const declarations = manifest.modules - .filter((m) => m.kind === 'javascript-module' && m.declarations?.length) - .reduce((current, next) => current.concat(next.declarations ?? []), [] as Declaration[]); const exports = manifest.modules.reduce( (current, next) => current.concat(next.exports ?? []), [] as Export[], @@ -70,7 +59,6 @@ export function generateReactWrappers( createDir(new URL('.', targetPath)); const reactTemplate = renderTemplate( declaration, - declarations, module, exports, library, @@ -122,26 +110,15 @@ export function generateReactWrappers( function renderTemplate( declaration: CustomElementDeclaration, - declarations: Declaration[], module: Module, exports: Export[], library: string, isMainLibrary: boolean, ): string { - const extensions = findExtensionUsage(declaration, declarations); const dirDepth = module.path.split('/').length - 1; const coreImportPath = isMainLibrary ? `${!dirDepth ? './' : '../'.repeat(dirDepth)}core.js` : `@sbb-esta/lyne-react/core.js`; - const extensionImport = !extensions.size - ? '' - : ` - - import { ${Array.from(extensions.keys()).join(', ')} } from '${coreImportPath}';`; - const extension = [...extensions.values()].reduce( - (current, next) => (v) => current(next(v)), - (v: string) => v, - ); const componentsImports = new Map().set(module.path, [declaration.name]); const customEventTypes = declaration.events @@ -183,10 +160,10 @@ function renderTemplate( ${Array.from(componentsImports) .map(([key, imports]) => `import { ${imports.join(', ')} } from '${library}/${key}';`) .join('\n')} - import react from 'react';${extensionImport} + import react from 'react'; // eslint-disable-next-line @typescript-eslint/naming-convention - export const ${declaration.name.replace(/Element$/, '')} = ${extension(`createComponent({ + export const ${declaration.name.replace(/Element$/, '')} = createComponent({ tagName: '${ // eslint-disable-next-line lyne/local-name-rule declaration.tagName @@ -208,67 +185,7 @@ function renderTemplate( ` : '' } - })`)}; + }); `; return reactTemplate; } - -function findExtensionUsage( - declaration: ClassDeclaration, - declarations: Declaration[], -): Map string> { - const extensions = new Map string>(); - if (usesSsrSlotState(declaration, declarations)) { - extensions.set('withSsrDataSlotNames', (v) => `withSsrDataSlotNames(${v})`); - } - const childTypes = namedSlotListElements(declaration); - if (childTypes.length) { - extensions.set( - 'withSsrDataChildCount', - (v) => `withSsrDataChildCount([${childTypes.map((t) => `'${t}'`).join(', ')}], ${v})`, - ); - } - return extensions; -} - -// eslint-disable-next-line @typescript-eslint/naming-convention -type ClassDeclarationSSR = ClassDeclaration & { _ssrslotstate?: boolean }; -const ssrSlotStateKey = '_ssrslotstate'; -function usesSsrSlotState( - declaration: ClassDeclarationSSR | undefined, - declarations: Declaration[], -): boolean { - while (declaration) { - if ( - declaration[ssrSlotStateKey] || - declaration.mixins?.some((m) => - declarations.find((d) => d.name === m.name && (d as ClassDeclarationSSR)[ssrSlotStateKey]), - ) - ) { - return true; - } - - declaration = declarations.find( - (d): d is ClassDeclarationSSR => d.name === declaration!.superclass?.name, - ); - } - - return false; -} - -function namedSlotListElements(declaration: ClassDeclaration): string[] { - return ( - declaration.members - ?.find( - (m): m is ClassField => - m.inheritedFrom?.name === 'NamedSlotListElement' && m.name === 'listChildLocalNames', - ) - ?.default?.match(/([\w-]+)/g) - ?.map((m) => - m - .split('-') - .map((s) => s[0] + s.substring(1).toLowerCase()) - .join(''), - ) ?? [] - ); -}