From 35d79e012b802b61ecf958e2b956a24aae6ef62c Mon Sep 17 00:00:00 2001 From: Florian Bischof Date: Sat, 6 Jul 2024 17:17:43 +0200 Subject: [PATCH] Add support for SSR runtimes (#9385) Co-authored-by: Matt Hova --- .github/workflows/main.yml | 28 ++++++++++++++++++++++++++++ spec/ssr/ssr_deno.js | 2 ++ spec/ssr/ssr_node.mjs | 2 ++ src/core/Browser.js | 14 ++++++++------ src/core/Util.js | 4 ++-- src/dom/DomEvent.js | 2 +- src/dom/DomUtil.js | 2 +- 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 spec/ssr/ssr_deno.js create mode 100644 spec/ssr/ssr_node.mjs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e53633fc0..8bb3d90f3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -95,6 +95,34 @@ jobs: env: CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + test-ssr: + needs: setup + runs-on: ubuntu-latest + + steps: + - name: Restore setup + uses: actions/cache@v4 + with: + path: ./* + key: ${{ runner.os }}-${{ github.ref }}-${{ github.sha }}-setup + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + + - name: Run Node.js SSR script + run: node ./spec/ssr/ssr_node.mjs + + - name: Set up Deno + uses: denoland/setup-deno@v1 + with: + deno-version: v1.x + + - name: Run Deno SSR script + run: deno run ./spec/ssr/ssr_deno.js + test: needs: setup runs-on: ${{ matrix.os || 'ubuntu-latest' }} diff --git a/spec/ssr/ssr_deno.js b/spec/ssr/ssr_deno.js new file mode 100644 index 000000000..3feb96f14 --- /dev/null +++ b/spec/ssr/ssr_deno.js @@ -0,0 +1,2 @@ +import L from '../../dist/leaflet-src.esm.js'; +console.log(L.version); diff --git a/spec/ssr/ssr_node.mjs b/spec/ssr/ssr_node.mjs new file mode 100644 index 000000000..f679ef63b --- /dev/null +++ b/spec/ssr/ssr_node.mjs @@ -0,0 +1,2 @@ +import L from '../../dist/leaflet-src.js'; +console.log(L.version); diff --git a/src/core/Browser.js b/src/core/Browser.js index 064895864..07e9034f0 100644 --- a/src/core/Browser.js +++ b/src/core/Browser.js @@ -24,14 +24,14 @@ const mobile = typeof orientation !== 'undefined' || userAgentContains('mobile') // @property pointer: Boolean // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). -const pointer = !!window.PointerEvent; +const pointer = typeof window === 'undefined' ? false : !!window.PointerEvent; // @property touchNative: Boolean // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). // **This does not necessarily mean** that the browser is running in a computer with // a touchscreen, it only means that the browser is capable of understanding // touch events. -const touchNative = 'ontouchstart' in window || !!window.TouchEvent; +const touchNative = typeof window === 'undefined' ? false : 'ontouchstart' in window || !!(window.TouchEvent); // @property touch: Boolean // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events. @@ -40,19 +40,21 @@ const touch = touchNative || pointer; // @property retina: Boolean // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. -const retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; +const retina = typeof window === 'undefined' || typeof window.devicePixelRatio === 'undefined' ? false : window.devicePixelRatio > 1; // @property mac: Boolean; `true` when the browser is running in a Mac platform -const mac = navigator.platform.startsWith('Mac'); +const mac = typeof navigator === 'undefined' || typeof navigator.platform === 'undefined' ? false : navigator.platform.startsWith('Mac'); // @property mac: Boolean; `true` when the browser is running in a Linux platform -const linux = navigator.platform.startsWith('Linux'); +const linux = typeof navigator === 'undefined' || typeof navigator.platform === 'undefined' ? false : navigator.platform.startsWith('Linux'); function userAgentContains(str) { + if (typeof navigator === 'undefined' || typeof navigator.userAgent === 'undefined') { + return false; + } return navigator.userAgent.toLowerCase().includes(str); } - export default { chrome, safari, diff --git a/src/core/Util.js b/src/core/Util.js index 85471f93c..136c8a979 100644 --- a/src/core/Util.js +++ b/src/core/Util.js @@ -156,8 +156,8 @@ export function template(str, data) { // mobile devices (by setting image `src` to this string). export const emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; -const requestFn = window.requestAnimationFrame; -const cancelFn = window.cancelAnimationFrame; +const requestFn = typeof window === 'undefined' ? falseFn : window.requestAnimationFrame; +const cancelFn = typeof window === 'undefined' ? falseFn : window.cancelAnimationFrame; // @function requestAnimFrame(fn: Function, context?: Object): Number // Schedules `fn` to be executed when the browser repaints. `fn` is bound to `context` if given. diff --git a/src/dom/DomEvent.js b/src/dom/DomEvent.js index c262295ca..643a62067 100644 --- a/src/dom/DomEvent.js +++ b/src/dom/DomEvent.js @@ -96,7 +96,7 @@ function batchRemove(obj, filterFn) { const mouseSubst = { mouseenter: 'mouseover', mouseleave: 'mouseout', - wheel: !('onwheel' in window) && 'mousewheel' + wheel: typeof window === 'undefined' ? false : !('onwheel' in window) && 'mousewheel' }; function addOne(obj, type, fn, context) { diff --git a/src/dom/DomUtil.js b/src/dom/DomUtil.js index e58db61fe..5d66fc97d 100644 --- a/src/dom/DomUtil.js +++ b/src/dom/DomUtil.js @@ -78,7 +78,7 @@ export function getPosition(el) { return positions.get(el) ?? new Point(0, 0); } -const documentStyle = document.documentElement.style; +const documentStyle = typeof document === 'undefined' ? {} : document.documentElement.style; // Safari still needs a vendor prefix, we need to detect with property name is supported. const userSelectProp = ['userSelect', 'WebkitUserSelect'].find(prop => prop in documentStyle); let prevUserSelect;