1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2023 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Worker// Return true if value is not nullish - i.e. not null or undefined 16*6dbdd20aSAndroid Build Coastguard Worker// Allows doing the following 17*6dbdd20aSAndroid Build Coastguard Worker// exists(val) && m('div', val) 18*6dbdd20aSAndroid Build Coastguard Worker// Even if val is a non-nullish falsey value like 0 or '' 19*6dbdd20aSAndroid Build Coastguard Workerexport function exists<T>(value: T): value is NonNullable<T> { 20*6dbdd20aSAndroid Build Coastguard Worker return value !== undefined && value !== null; 21*6dbdd20aSAndroid Build Coastguard Worker} 22*6dbdd20aSAndroid Build Coastguard Worker 23*6dbdd20aSAndroid Build Coastguard Worker// Type util to make sure that exactly one of the passed keys is defined. 24*6dbdd20aSAndroid Build Coastguard Worker// Example usage: 25*6dbdd20aSAndroid Build Coastguard Worker// type FooOrBar = ExactlyOne<{foo: number; bar: number}>; 26*6dbdd20aSAndroid Build Coastguard Worker// const x : FooOrBar = {foo: 42}; // OK 27*6dbdd20aSAndroid Build Coastguard Worker// const x : FooOrBar = {bar: 42}; // OK 28*6dbdd20aSAndroid Build Coastguard Worker// const x : FooOrBar = {}; // Compiler error 29*6dbdd20aSAndroid Build Coastguard Worker// const x : FooOrBar = {foo:1, bar:2}; // Compiler error 30*6dbdd20aSAndroid Build Coastguard Workerexport type ExactlyOne<T, K extends keyof T = keyof T> = K extends keyof T 31*6dbdd20aSAndroid Build Coastguard Worker ? {[P in K]: T[P]} & {[P in Exclude<keyof T, K>]?: undefined} 32*6dbdd20aSAndroid Build Coastguard Worker : never; 33*6dbdd20aSAndroid Build Coastguard Worker 34*6dbdd20aSAndroid Build Coastguard Worker// Escape characters that are not allowed inside a css selector 35*6dbdd20aSAndroid Build Coastguard Workerexport function escapeCSSSelector(selector: string): string { 36*6dbdd20aSAndroid Build Coastguard Worker return selector.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1'); 37*6dbdd20aSAndroid Build Coastguard Worker} 38*6dbdd20aSAndroid Build Coastguard Worker 39*6dbdd20aSAndroid Build Coastguard Worker// Make field K required in T 40*6dbdd20aSAndroid Build Coastguard Workerexport type RequiredField<T, K extends keyof T> = Omit<T, K> & 41*6dbdd20aSAndroid Build Coastguard Worker Required<Pick<T, K>>; 42*6dbdd20aSAndroid Build Coastguard Worker 43*6dbdd20aSAndroid Build Coastguard Worker// The lowest common denoninator between Map<> and WeakMap<>. 44*6dbdd20aSAndroid Build Coastguard Worker// This is just to avoid duplication of the getOrCreate below. 45*6dbdd20aSAndroid Build Coastguard Workerinterface MapLike<K, V> { 46*6dbdd20aSAndroid Build Coastguard Worker get(key: K): V | undefined; 47*6dbdd20aSAndroid Build Coastguard Worker set(key: K, value: V): this; 48*6dbdd20aSAndroid Build Coastguard Worker} 49*6dbdd20aSAndroid Build Coastguard Worker 50*6dbdd20aSAndroid Build Coastguard Workerexport function getOrCreate<K, V>( 51*6dbdd20aSAndroid Build Coastguard Worker map: MapLike<K, V>, 52*6dbdd20aSAndroid Build Coastguard Worker key: K, 53*6dbdd20aSAndroid Build Coastguard Worker factory: () => V, 54*6dbdd20aSAndroid Build Coastguard Worker): V { 55*6dbdd20aSAndroid Build Coastguard Worker let value = map.get(key); 56*6dbdd20aSAndroid Build Coastguard Worker if (value !== undefined) return value; 57*6dbdd20aSAndroid Build Coastguard Worker value = factory(); 58*6dbdd20aSAndroid Build Coastguard Worker map.set(key, value); 59*6dbdd20aSAndroid Build Coastguard Worker return value; 60*6dbdd20aSAndroid Build Coastguard Worker} 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Worker// Allows to take an existing class instance (`target`) and override some of its 63*6dbdd20aSAndroid Build Coastguard Worker// methods via `overrides`. We use this for cases where we want to expose a 64*6dbdd20aSAndroid Build Coastguard Worker// "manager" (e.g. TrackManager, SidebarManager) to the plugins, but we want to 65*6dbdd20aSAndroid Build Coastguard Worker// override few of its methods (e.g. to inject the pluginId in the args). 66*6dbdd20aSAndroid Build Coastguard Workerexport function createProxy<T extends object>( 67*6dbdd20aSAndroid Build Coastguard Worker target: T, 68*6dbdd20aSAndroid Build Coastguard Worker overrides: Partial<T>, 69*6dbdd20aSAndroid Build Coastguard Worker): T { 70*6dbdd20aSAndroid Build Coastguard Worker return new Proxy(target, { 71*6dbdd20aSAndroid Build Coastguard Worker get: (target: T, prop: string | symbol, receiver) => { 72*6dbdd20aSAndroid Build Coastguard Worker // If the property is overriden, use that; otherwise, use target 73*6dbdd20aSAndroid Build Coastguard Worker const overrideValue = (overrides as {[key: symbol | string]: {}})[prop]; 74*6dbdd20aSAndroid Build Coastguard Worker if (overrideValue !== undefined) { 75*6dbdd20aSAndroid Build Coastguard Worker return typeof overrideValue === 'function' 76*6dbdd20aSAndroid Build Coastguard Worker ? overrideValue.bind(overrides) 77*6dbdd20aSAndroid Build Coastguard Worker : overrideValue; 78*6dbdd20aSAndroid Build Coastguard Worker } 79*6dbdd20aSAndroid Build Coastguard Worker const baseValue = Reflect.get(target, prop, receiver); 80*6dbdd20aSAndroid Build Coastguard Worker return typeof baseValue === 'function' 81*6dbdd20aSAndroid Build Coastguard Worker ? baseValue.bind(target) 82*6dbdd20aSAndroid Build Coastguard Worker : baseValue; 83*6dbdd20aSAndroid Build Coastguard Worker }, 84*6dbdd20aSAndroid Build Coastguard Worker }) as T; 85*6dbdd20aSAndroid Build Coastguard Worker} 86