1 | /**
|
2 | * Represents source text that can be missing.
|
3 | */
|
4 | export type NullableSourceText = string | null;
|
5 |
|
6 | export interface SourceStore {
|
7 | /**
|
8 | * Registers a `url`, optionally with its source text.
|
9 | *
|
10 | * @param url URL of the source text.
|
11 | * @param sourceText Source text corresponding to the provided URL, or `null`.
|
12 | * @returns Boolean indicating if the source store was updated.
|
13 | */
|
14 | set(url: string, sourceText: NullableSourceText): boolean;
|
15 |
|
16 | // TODO: Find out why TSLint complains about whitespace
|
17 | // tslint:disable:whitespace
|
18 | /**
|
19 | * Returns the current source text for the provided `url`.
|
20 | *
|
21 | * - If the `url` is unknown, returns `undefined`.
|
22 | * - If the `url` is known but does not have any associated source text,
|
23 | * returns `null`.
|
24 | * - If the `url` is known and the store has its source text, returns the
|
25 | * source text (a `string`).
|
26 | *
|
27 | * @param url URL of the source text.
|
28 | */
|
29 | get(url: string): NullableSourceText | undefined;
|
30 |
|
31 | // tslint:enable
|
32 |
|
33 | [Symbol.iterator](): Iterator<[string, NullableSourceText]>;
|
34 | }
|
35 |
|
36 | /**
|
37 | * This is basically a `Map` with a setter checking for changes instead
|
38 | * of returning `this`.
|
39 | */
|
40 | export class MemorySourceStore implements SourceStore {
|
41 | private readonly urlToSourceText: Map<string, NullableSourceText>;
|
42 |
|
43 | constructor(initialData?: Iterable<[string, NullableSourceText]>) {
|
44 | this.urlToSourceText = initialData === undefined ? new Map() : new Map(initialData);
|
45 | }
|
46 |
|
47 | /**
|
48 | * Registers a `url`, optionally with its source text.
|
49 | *
|
50 | * Throws an error if the URL is already set and the value differs.
|
51 | *
|
52 | * @param url URL to set.
|
53 | * @param sourceText Source text corresponding to the provided URL.
|
54 | * @returns Boolean indicating if the source store was updated.
|
55 | */
|
56 | set(url: string, sourceText: NullableSourceText): boolean {
|
57 | const old: NullableSourceText | undefined = this.urlToSourceText.get(url);
|
58 | if (old === undefined || old === null) {
|
59 | this.urlToSourceText.set(url, sourceText);
|
60 | } else if (sourceText !== null && sourceText !== old) {
|
61 | throw new Error(`Incompatible sources for URL: ${url}\nold: ${old}\nnew: ${sourceText}`);
|
62 | }
|
63 | return sourceText !== old;
|
64 | }
|
65 |
|
66 | get(url: string): NullableSourceText | undefined {
|
67 | return this.urlToSourceText.get(url);
|
68 | }
|
69 |
|
70 | [Symbol.iterator](): Iterator<[string, NullableSourceText]> {
|
71 | return this.urlToSourceText[Symbol.iterator]();
|
72 | }
|
73 | }
|