summaryrefslogtreecommitdiff
path: root/shared/components/src/utils/uniqueId.ts
blob: 3a6d21d3e2e82bd9f537e52a45e2f9f22aff7f75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import { getContext } from 'svelte';

export const UNIQUE_ID_CONTEXT_NAME = 'amp-web-unique-id';

interface UniqueContext {
    nextId: number;
}

// TODO: rdar://84029606 (Extract logger into shared util)
interface Logger {
    warn(...args: any[]): string;
}
interface LoggerFactory {
    loggerFor(name: string): Logger;
}

export function initializeUniqueIdContext(
    context: Map<string, unknown>,
    loggerFactory: LoggerFactory,
): void {
    const logger = loggerFactory.loggerFor('uniqueIdContext');

    if (context.has(UNIQUE_ID_CONTEXT_NAME)) {
        logger.warn(
            `${UNIQUE_ID_CONTEXT_NAME} context has already been created. Cannot be created more than once`,
        );
    } else {
        const INITAL_STATE: UniqueContext = { nextId: 0 };
        context.set(UNIQUE_ID_CONTEXT_NAME, INITAL_STATE);
    }
}

/**
 * Creates a unique Id string based on string provided
 *
 * @returns unique id string
 */
export type UniqueIdGenerator = () => string;

// Custom elements most likely will not be used in an environment has that initialized the Svelte
// context. Components that are later wrapped by a custom element should use this function so that
// they can generate unique ids automatically when used inside a Svelte app, but not throw an error
// when used in other contexts.
//
export function maybeGetUniqueIdGenerator(): UniqueIdGenerator | undefined {
    const UNIQUE_ID_PREFIX = 'uid-';
    const state: UniqueContext = getContext(UNIQUE_ID_CONTEXT_NAME);
    const isNextIdANumber = typeof state?.nextId === 'number';

    if (!isNextIdANumber) {
        return;
    }

    return () => {
        const id = `${UNIQUE_ID_PREFIX}${state.nextId}`;
        state.nextId += 1;
        return id;
    };
}

export function getUniqueIdGenerator(): UniqueIdGenerator {
    const uniqueIdGenerator = maybeGetUniqueIdGenerator();

    if (!uniqueIdGenerator) {
        throw new Error(
            `${UNIQUE_ID_CONTEXT_NAME} context has not been initialized. Initialize at application bootstrap.`,
        );
    }

    return uniqueIdGenerator;
}