1 | /**
|
2 | * This file describes the interface between compilation time
|
3 | * and runtime.
|
4 | *
|
5 | * # Locators
|
6 | *
|
7 | * Compile time and runtime must share the same Locator. A Locator is an
|
8 | * object that describes the location of a template, and is roughly a
|
9 | * module name. The compiler and runtime may use the Locator to locally
|
10 | * resolve names relative to the template the name was found in, but must
|
11 | * share resolution rules between compilation time and runtime.
|
12 | *
|
13 | * For example, given this template with Locator
|
14 | * `{ module: 'components/Articles/Container' }:
|
15 | *
|
16 | * ```
|
17 | * <TabBar />
|
18 | * ```
|
19 | *
|
20 | * The compiler may resolve `<TabBar>` to `components/Articles/TabBar`. The
|
21 | * important thing is that the compiler and runtime share resolution rules.
|
22 | *
|
23 | * # CompileTimeLookup
|
24 | *
|
25 | * When compiling an application, the `CompileTimeLookup` is responsible
|
26 | * for resolving helpers, modifiers, and components into "handles"
|
27 | * (numbers) that can be embedded into the program and used at runtime.
|
28 | *
|
29 | * # RuntimeResolver
|
30 | *
|
31 | * The `RuntimeResolver` has two responsibilities.
|
32 | *
|
33 | * 1. To turn handles created by the `CompileTimeLookup` into live helpers,
|
34 | * modifiers, and components.
|
35 | * 2. To resolve dynamic components at runtime that come from
|
36 | * calls to `{{component dynamic}}`.
|
37 | *
|
38 | * The `CompileTimeLookup` and `RuntimeResolver` must maintain symmetry
|
39 | * between:
|
40 | *
|
41 | * * `resolver.resolve(lookup.lookupComponentDefinition(name, referrer))`; and
|
42 | * * `resolver.lookupComponentDefinition(name, referrer))`
|
43 | *
|
44 | * # Coupling
|
45 | *
|
46 | * In practice, the `CompileTimeLookup` and `RuntimeResolver` are two parts
|
47 | * of one system. The goal of this system is to allow the `CompileTimeLookup`
|
48 | * to do as much resolution as possible ahead of time, while still allowing
|
49 | * the `RuntimeResolver` to do dynamic resolution when necessary.
|
50 | */
|
51 |
|
52 | import type {
|
53 | CapabilityMask,
|
54 | ComponentDefinitionState,
|
55 | ComponentInstanceState,
|
56 | } from './components.js';
|
57 | import type { Nullable } from './core.js';
|
58 | import type { InternalComponentManager } from './managers.js';
|
59 | import type { HelperDefinitionState, ModifierDefinitionState, Owner } from './runtime.js';
|
60 | import type { CompilableProgram, Template } from './template.js';
|
61 |
|
62 | export interface CompileTimeComponent {
|
63 | handle: number;
|
64 | capabilities: CapabilityMask;
|
65 | compilable: Nullable<CompilableProgram>;
|
66 | }
|
67 |
|
68 | export interface ResolvedComponentDefinition<
|
69 | D = ComponentDefinitionState,
|
70 | I = ComponentInstanceState,
|
71 | M extends InternalComponentManager<I, D> = InternalComponentManager<I, D>,
|
72 | > {
|
73 | state: D;
|
74 | manager: M;
|
75 | template: Template | null;
|
76 | }
|
77 |
|
78 | export enum ResolverContext {
|
79 | Component,
|
80 | Modifier,
|
81 | Helper,
|
82 | HelperOrComponent,
|
83 | }
|
84 |
|
85 | export interface CompileTimeResolver<O extends Owner = Owner> {
|
86 | lookupHelper(name: string, owner: O): Nullable<HelperDefinitionState>;
|
87 | lookupModifier(name: string, owner: O): Nullable<ModifierDefinitionState>;
|
88 | lookupComponent(name: string, owner: O): Nullable<ResolvedComponentDefinition>;
|
89 |
|
90 | // TODO: These are used to lookup keywords that are implemented as helpers/modifiers.
|
91 | // We should try to figure out a cleaner way to do this.
|
92 | lookupBuiltInHelper(name: string): Nullable<HelperDefinitionState>;
|
93 | lookupBuiltInModifier(name: string): Nullable<ModifierDefinitionState>;
|
94 | }
|
95 |
|
96 | export interface RuntimeResolver<O extends Owner = Owner> {
|
97 | lookupComponent(name: string, owner: O): Nullable<ResolvedComponentDefinition>;
|
98 | }
|