Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dom shim so importing components in SSR works #8184

Merged
merged 3 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
"directory": "packages/base"
},
"exports": {
"./dist/ssr-dom.js": {
"browser": "./dist/ssr-dom.js",
"node": "./dist/ssr-dom-shim.js"
pskelin marked this conversation as resolved.
Show resolved Hide resolved
},
".": "./index.js",
"./dist/*": "./dist/*",
"./package.json": "./package.json",
Expand All @@ -35,6 +39,7 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.1.2",
"lit-html": "^2.0.1"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions packages/base/src/ManagedStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { StyleData, StyleDataCSP } from "./types.js";
import { isSafari } from "./Device.js";
import { getCurrentRuntimeIndex, compareRuntimes } from "./Runtimes.js";

const isSSR = typeof document === "undefined";

const getStyleId = (name: string, value: string) => {
return value ? `${name}|${value}` : name;
};
Expand Down Expand Up @@ -106,6 +108,9 @@ const updateStyle = (data: StyleData, name: string, value = "", theme?: string)
};

const hasStyle = (name: string, value = ""): boolean => {
if (isSSR) {
return true;
}
if (shouldUseLinks()) {
return !!document.querySelector(`head>link[${name}="${value}"]`);
}
Expand Down
12 changes: 7 additions & 5 deletions packages/base/src/UI5Element.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import "@ui5/webcomponents-base/dist/ssr-dom.js";
import merge from "./thirdparty/merge.js";
import { boot } from "./Boot.js";
import UI5ElementMetadata, {
Expand Down Expand Up @@ -338,17 +340,17 @@ abstract class UI5Element extends HTMLElement {
const shouldWaitForCustomElement = localName.includes("-") && !shouldIgnoreCustomElement(localName);

if (shouldWaitForCustomElement) {
const isDefined = window.customElements.get(localName);
const isDefined = customElements.get(localName);
if (!isDefined) {
const whenDefinedPromise = window.customElements.whenDefined(localName); // Class registered, but instances not upgraded yet
const whenDefinedPromise = customElements.whenDefined(localName); // Class registered, but instances not upgraded yet
let timeoutPromise = elementTimeouts.get(localName);
if (!timeoutPromise) {
timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000));
elementTimeouts.set(localName, timeoutPromise);
}
await Promise.race([whenDefinedPromise, timeoutPromise]);
}
window.customElements.upgrade(child);
customElements.upgrade(child);
}
}

Expand Down Expand Up @@ -1131,14 +1133,14 @@ abstract class UI5Element extends HTMLElement {
const tag = this.getMetadata().getTag();

const definedLocally = isTagRegistered(tag);
const definedGlobally = window.customElements.get(tag);
const definedGlobally = customElements.get(tag);

if (definedGlobally && !definedLocally) {
recordTagRegistrationFailure(tag);
} else if (!definedGlobally) {
this._generateAccessors();
registerTag(tag);
window.customElements.define(tag, this as unknown as CustomElementConstructor);
customElements.define(tag, this as unknown as CustomElementConstructor);
preloadLinks(this);
}
return this;
Expand Down
12 changes: 12 additions & 0 deletions packages/base/src/ssr-dom-shim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable max-classes-per-file */
import { HTMLElement, Element, customElements } from "@lit-labs/ssr-dom-shim";

globalThis.HTMLElement ??= HTMLElement;
globalThis.Element ??= Element;
globalThis.customElements ??= customElements;

class NodeShim {}
globalThis.Node ??= NodeShim as object as typeof Node;

class FileListShim {}
globalThis.FileList ??= FileListShim as object as typeof FileList;
1 change: 1 addition & 0 deletions packages/base/src/ssr-dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// no shims in the browser when imported via conditional export
5 changes: 5 additions & 0 deletions packages/base/src/util/detectNavigatorLanguage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { DEFAULT_LANGUAGE } from "../generated/AssetParameters.js";

const isSSR = typeof document === "undefined";

const detectNavigatorLanguage = () => {
if (isSSR) {
return DEFAULT_LANGUAGE;
}
const browserLanguages = navigator.languages;

const navigatorLanguage = () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/fiori/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"generate": "nps generate",
"generateAPI": "nps generateAPI",
"bundle": "nps build.bundle",
"test": "wc-dev test",
"test": "wc-dev test && yarn test:ssr",
"test:ssr": "node -e \"import('./test/ssr/component-imports.js')\"",
"create-ui5-element": "wc-create-ui5-element",
"prepublishOnly": "tsc"
},
Expand Down
27 changes: 27 additions & 0 deletions packages/fiori/test/ssr/component-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Bar from "../../dist/Bar.js";
// zxing is using window
// import BarcodeScannerDialog from "../../dist/BarcodeScannerDialog.js";
import DynamicSideContent from "../../dist/DynamicSideContent.js";
import FilterItem from "../../dist/FilterItem.js";
import FilterItemOption from "../../dist/FilterItemOption.js";
import FlexibleColumnLayout from "../../dist/FlexibleColumnLayout.js";
import IllustratedMessage from "../../dist/IllustratedMessage.js";
import MediaGallery from "../../dist/MediaGallery.js";
import MediaGalleryItem from "../../dist/MediaGalleryItem.js";
import NotificationAction from "../../dist/NotificationAction.js";
import NotificationListGroupItem from "../../dist/NotificationListGroupItem.js";
import NotificationListItem from "../../dist/NotificationListItem.js";
import Page from "../../dist/Page.js";
import ProductSwitch from "../../dist/ProductSwitch.js";
import ProductSwitchItem from "../../dist/ProductSwitchItem.js";
import ShellBar from "../../dist/ShellBar.js";
import ShellBarItem from "../../dist/ShellBarItem.js";
import SideNavigation from "../../dist/SideNavigation.js";
import SideNavigationItem from "../../dist/SideNavigationItem.js";
import SideNavigationSubItem from "../../dist/SideNavigationSubItem.js";
import SortItem from "../../dist/SortItem.js";
import Timeline from "../../dist/Timeline.js";
import UploadCollection from "../../dist/UploadCollection.js";
import UploadCollectionItem from "../../dist/UploadCollectionItem.js";
import ViewSettingsDialog from "../../dist/ViewSettingsDialog.js";
import Wizard from "../../dist/Wizard.js";
7 changes: 7 additions & 0 deletions packages/localization/src/sap/base/util/ObjectPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ObjectPath is accessing window which breaks SSR, hence the overlay

const ObjectPath = {
set() {},
};

export default ObjectPath;
3 changes: 2 additions & 1 deletion packages/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"build": "wc-dev build",
"bundle": "nps build.bundle",
"test": "wc-dev test",
"test:suite-1": "wc-dev test-suite-1",
"test:ssr": "node -e \"import('./test/ssr/component-imports.js')\"",
"test:suite-1": "wc-dev test-suite-1 && yarn test:ssr",
"test:suite-2": "wc-dev test-suite-2",
"create-ui5-element": "wc-create-ui5-element",
"prepublishOnly": "tsc"
Expand Down
8 changes: 6 additions & 2 deletions packages/main/src/Toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const MAX_DURATION = 1000;
const openedToasts: Array<Toast> = [];
let opener: HTMLElement | null;

let globalListenerAdded = false;
const handleGlobalKeydown = (e: KeyboardEvent) => {
const isCtrl = e.metaKey || (!isMac() && e.ctrlKey);
const isMKey = e.key.toLowerCase() === "m";
Expand All @@ -43,8 +44,6 @@ const handleGlobalKeydown = (e: KeyboardEvent) => {
}
};

document.addEventListener("keydown", handleGlobalKeydown);

/**
* @class
*
Expand Down Expand Up @@ -161,6 +160,11 @@ class Toast extends UI5Element {
super();

this._reopen = false;

if (!globalListenerAdded) {
document.addEventListener("keydown", handleGlobalKeydown);
globalListenerAdded = true;
}
}

onAfterRendering() {
Expand Down
83 changes: 83 additions & 0 deletions packages/main/test/ssr/component-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Avatar from "../../dist/Avatar.js";
import AvatarGroup from "../../dist/AvatarGroup.js";
import Badge from "../../dist/Badge.js";
import Breadcrumbs from "../../dist/Breadcrumbs.js";
import BusyIndicator from "../../dist/BusyIndicator.js";
import Button from "../../dist/Button.js";
import Card from "../../dist/Card.js";
import CardHeader from "../../dist/CardHeader.js";
import Carousel from "../../dist/Carousel.js";
import CheckBox from "../../dist/CheckBox.js";
import ColorPalette from "../../dist/ColorPalette.js";
import ColorPaletteItem from "../../dist/ColorPaletteItem.js";
import ColorPalettePopover from "../../dist/ColorPalettePopover.js";
import ColorPicker from "../../dist/ColorPicker.js";
import ComboBox from "../../dist/ComboBox.js";
import DatePicker from "../../dist/DatePicker.js";
import DateRangePicker from "../../dist/DateRangePicker.js";
import DateTimePicker from "../../dist/DateTimePicker.js";
import Dialog from "../../dist/Dialog.js";
import FileUploader from "../../dist/FileUploader.js";
import Icon from "../../dist/Icon.js";
import Input from "../../dist/Input.js";
import MultiInput from "../../dist/MultiInput.js";
import Label from "../../dist/Label.js";
import Link from "../../dist/Link.js";
import Menu from "../../dist/Menu.js";
import NavigationMenu from "../../dist/NavigationMenu.js";
import NavigationMenuItem from "../../dist/NavigationMenuItem.js";
import MenuItem from "../../dist/MenuItem.js";
import Popover from "../../dist/Popover.js";
import Panel from "../../dist/Panel.js";
import RadioButton from "../../dist/RadioButton.js";
import ResponsivePopover from "../../dist/ResponsivePopover.js";
import SegmentedButton from "../../dist/SegmentedButton.js";
import SegmentedButtonItem from "../../dist/SegmentedButtonItem.js";
import Select from "../../dist/Select.js";
import SelectMenu from "../../dist/SelectMenu.js";
import SelectMenuOption from "../../dist/SelectMenuOption.js";
import Slider from "../../dist/Slider.js";
import SplitButton from "../../dist/SplitButton.js";
import StepInput from "../../dist/StepInput.js";
import RangeSlider from "../../dist/RangeSlider.js";
import Switch from "../../dist/Switch.js";
import MessageStrip from "../../dist/MessageStrip.js";
import MultiComboBox from "../../dist/MultiComboBox.js";
import ProgressIndicator from "../../dist/ProgressIndicator.js";
import RatingIndicator from "../../dist/RatingIndicator.js";
import TabContainer from "../../dist/TabContainer.js";
import Tab from "../../dist/Tab.js";
import TabSeparator from "../../dist/TabSeparator.js";
import Table from "../../dist/Table.js";
import TableColumn from "../../dist/TableColumn.js";
import TableRow from "../../dist/TableRow.js";
import TableGroupRow from "../../dist/TableGroupRow.js";
import TableCell from "../../dist/TableCell.js";
import TextArea from "../../dist/TextArea.js";
import TimeSelection from "../../dist/TimeSelection.js";
import TimePicker from "../../dist/TimePicker.js";
import TimePickerClock from "../../dist/TimePickerClock.js";
import TimeSelectionClocks from "../../dist/TimeSelectionClocks.js";
import Title from "../../dist/Title.js";
import Toast from "../../dist/Toast.js";
import ToggleButton from "../../dist/ToggleButton.js";
// console.log({ToggleButton})
import Toolbar from "../../dist/Toolbar.js";
import ToolbarButton from "../../dist/ToolbarButton.js";
import ToolbarSeparator from "../../dist/ToolbarSeparator.js";
import ToolbarSpacer from "../../dist/ToolbarSpacer.js";
import ToolbarSelect from "../../dist/ToolbarSelect.js";
import Tree from "../../dist/Tree.js";
import TreeList from "../../dist/TreeList.js";
import TreeItem from "../../dist/TreeItem.js";
import TreeItemCustom from "../../dist/TreeItemCustom.js";
import List from "../../dist/List.js";
// console.log({List})
import StandardListItem from "../../dist/StandardListItem.js";
import CustomListItem from "../../dist/CustomListItem.js";
import GroupHeaderListItem from "../../dist/GroupHeaderListItem.js";

// Features
import "../../dist/features/InputElementsFormSupport.js";
import "../../dist/features/ColorPaletteMoreColors.js";
import "../../dist/features/InputSuggestions.js";
3 changes: 3 additions & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const customResolver = (id, source, options) => {
if (resolved.endsWith("dist/sap/base/util/LoaderExtensions.js")) {
resolved = resolved.replace("/dist/", "/src/").replace(".js", ".ts");
}
if (resolved.endsWith("dist/sap/base/util/ObjectPath.js")) {
resolved = resolved.replace("/dist/", "/src/").replace(".js", ".ts");
}
return resolved;
}
}
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2051,6 +2051,11 @@
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz#64df34e2f12e68e78ac57e571d25ec07fa460ca9"
integrity sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==

"@lit-labs/ssr-dom-shim@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.2.tgz#d693d972974a354034454ec1317eb6afd0b00312"
integrity sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==

"@lit/reactive-element@^1.3.0", "@lit/reactive-element@^1.6.0":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-1.6.2.tgz#c256690f82f2d7d0ffb0b1cdf68dcb1ec86cea28"
Expand Down
Loading