From 0863ff948210b92bd0b68064247a0e5f520066f9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 27 Feb 2021 16:18:08 +0100 Subject: [PATCH] core-api: new internal utility for accessing global singletons --- .../core-api/src/extensions/componentData.tsx | 20 ++------- .../core-api/src/lib/globalObject.test.ts | 42 +++++++++++++++++++ packages/core-api/src/lib/globalObject.ts | 17 +++++++- 3 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 packages/core-api/src/lib/globalObject.test.ts diff --git a/packages/core-api/src/extensions/componentData.tsx b/packages/core-api/src/extensions/componentData.tsx index 3d2558276d..d18379d25f 100644 --- a/packages/core-api/src/extensions/componentData.tsx +++ b/packages/core-api/src/extensions/componentData.tsx @@ -15,7 +15,7 @@ */ import { ComponentType, ReactNode } from 'react'; -import { globalObject } from '../lib/globalObject'; +import { getGlobalSingleton } from '../lib/globalObject'; // TODO(Rugvip): Access via symbol is deprecated, remove once on 0.3.x const DATA_KEY = Symbol('backstage-component-data'); @@ -32,22 +32,10 @@ type MaybeComponentNode = ReactNode & { type?: ComponentType & { [DATA_KEY]?: DataContainer }; }; -const GLOBAL_KEY = '__@backstage/component-data-store__'; - // The store is bridged across versions using the global object -function getStore() { - let storeObj = globalObject[GLOBAL_KEY] as - | { store: WeakMap, DataContainer> } - | undefined; - if (!storeObj) { - const store = new WeakMap, DataContainer>(); - storeObj = { store }; - globalObject[GLOBAL_KEY] = storeObj; - } - return storeObj.store; -} - -const store = getStore(); +const { store } = getGlobalSingleton('component-data-store', () => ({ + store: new WeakMap, DataContainer>(), +})); export function attachComponentData

( component: ComponentType

, diff --git a/packages/core-api/src/lib/globalObject.test.ts b/packages/core-api/src/lib/globalObject.test.ts new file mode 100644 index 0000000000..f539483a83 --- /dev/null +++ b/packages/core-api/src/lib/globalObject.test.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getGlobalSingleton } from './globalObject'; + +const anyGlobal = global as any; + +describe('getGlobalSingleton', () => { + beforeEach(() => { + delete anyGlobal['__@backstage/my-thing__']; + }); + + it('should return an existing value', () => { + const myThing = {}; + anyGlobal['__@backstage/my-thing__'] = myThing; + + expect(getGlobalSingleton('my-thing', () => ({}))).toBe(myThing); + expect(getGlobalSingleton('my-thing', () => ({}))).toBe(myThing); + }); + + it('should should create a new value', () => { + const myNewThing = {}; + + expect(anyGlobal['__@backstage/my-thing__']).toBe(undefined); + expect(getGlobalSingleton('my-thing', () => myNewThing)).toBe(myNewThing); + expect(anyGlobal['__@backstage/my-thing__']).toBe(myNewThing); + expect(getGlobalSingleton('my-thing', () => ({}))).toBe(myNewThing); + }); +}); diff --git a/packages/core-api/src/lib/globalObject.ts b/packages/core-api/src/lib/globalObject.ts index 85f16212fe..c7f9f98882 100644 --- a/packages/core-api/src/lib/globalObject.ts +++ b/packages/core-api/src/lib/globalObject.ts @@ -15,7 +15,7 @@ */ // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -function getGlobal() { +function getGlobalObject() { if (typeof window !== 'undefined' && window.Math === Math) { return window; } @@ -26,4 +26,17 @@ function getGlobal() { return Function('return this')(); } -export const globalObject = getGlobal(); +export const globalObject = getGlobalObject(); + +export function getGlobalSingleton(id: string, supplier: () => T): T { + const key = `__@backstage/${id}__`; + + let value = globalObject[key]; + if (value) { + return value; + } + + value = supplier(); + globalObject[key] = value; + return value; +}