/ shared / components / src / utils / uniqueId.ts
uniqueId.ts
 1  import { getContext } from 'svelte';
 2  
 3  export const UNIQUE_ID_CONTEXT_NAME = 'amp-web-unique-id';
 4  
 5  interface UniqueContext {
 6      nextId: number;
 7  }
 8  
 9  // TODO: rdar://84029606 (Extract logger into shared util)
10  interface Logger {
11      warn(...args: any[]): string;
12  }
13  interface LoggerFactory {
14      loggerFor(name: string): Logger;
15  }
16  
17  export function initializeUniqueIdContext(
18      context: Map<string, unknown>,
19      loggerFactory: LoggerFactory,
20  ): void {
21      const logger = loggerFactory.loggerFor('uniqueIdContext');
22  
23      if (context.has(UNIQUE_ID_CONTEXT_NAME)) {
24          logger.warn(
25              `${UNIQUE_ID_CONTEXT_NAME} context has already been created. Cannot be created more than once`,
26          );
27      } else {
28          const INITAL_STATE: UniqueContext = { nextId: 0 };
29          context.set(UNIQUE_ID_CONTEXT_NAME, INITAL_STATE);
30      }
31  }
32  
33  /**
34   * Creates a unique Id string based on string provided
35   *
36   * @returns unique id string
37   */
38  export type UniqueIdGenerator = () => string;
39  
40  // Custom elements most likely will not be used in an environment has that initialized the Svelte
41  // context. Components that are later wrapped by a custom element should use this function so that
42  // they can generate unique ids automatically when used inside a Svelte app, but not throw an error
43  // when used in other contexts.
44  //
45  export function maybeGetUniqueIdGenerator(): UniqueIdGenerator | undefined {
46      const UNIQUE_ID_PREFIX = 'uid-';
47      const state: UniqueContext = getContext(UNIQUE_ID_CONTEXT_NAME);
48      const isNextIdANumber = typeof state?.nextId === 'number';
49  
50      if (!isNextIdANumber) {
51          return;
52      }
53  
54      return () => {
55          const id = `${UNIQUE_ID_PREFIX}${state.nextId}`;
56          state.nextId += 1;
57          return id;
58      };
59  }
60  
61  export function getUniqueIdGenerator(): UniqueIdGenerator {
62      const uniqueIdGenerator = maybeGetUniqueIdGenerator();
63  
64      if (!uniqueIdGenerator) {
65          throw new Error(
66              `${UNIQUE_ID_CONTEXT_NAME} context has not been initialized. Initialize at application bootstrap.`,
67          );
68      }
69  
70      return uniqueIdGenerator;
71  }