1 | declare module '@ember/template-compiler/lib/template' {
|
2 | import { type TemplateOnlyComponent } from '@ember/component/template-only';
|
3 | type ComponentClass = abstract new (...args: any[]) => object;
|
4 | /**
|
5 | * All possible options passed to `template()` may specify a `moduleName`.
|
6 | */
|
7 | export interface BaseTemplateOptions {
|
8 | moduleName?: string;
|
9 | /**
|
10 | * Whether the template should be treated as a strict-mode template. Defaults
|
11 | * to `true`.
|
12 | */
|
13 | strictMode?: boolean;
|
14 | }
|
15 | /**
|
16 | * When using `template` in a class, you call it in a `static` block and pass
|
17 | * the class as the `component` option.
|
18 | *
|
19 | * ```ts
|
20 | * class MyComponent extends Component {
|
21 | * static {
|
22 | * template('{{this.greeting}}, {{@place}}!',
|
23 | * { component: this },
|
24 | * // explicit or implicit option goes here
|
25 | * );
|
26 | * }
|
27 | * }
|
28 | * ```
|
29 | *
|
30 | * For the full explicit form, see {@linkcode ExplicitClassOptions}. For the
|
31 | * full implicit form, see {@linkcode ImplicitClassOptions}.
|
32 | */
|
33 | export interface BaseClassTemplateOptions<C extends ComponentClass> extends BaseTemplateOptions {
|
34 | component: C;
|
35 | }
|
36 | /**
|
37 | * When using `template` outside of a class (i.e. a "template-only component"), you can pass
|
38 | * a `scope` option that explicitly provides the lexical scope for the template.
|
39 | *
|
40 | * This is called the "explicit form".
|
41 | *
|
42 | * ```ts
|
43 | * const greeting = 'Hello';
|
44 | * const HelloWorld = template('{{greeting}} World!', { scope: () => ({ greeting }) });
|
45 | * ```
|
46 | */
|
47 | export interface ExplicitTemplateOnlyOptions extends BaseTemplateOptions {
|
48 | scope(): Record<string, unknown>;
|
49 | }
|
50 | /**
|
51 | * When using `template` *inside* a class (see
|
52 | * {@linkcode BaseClassTemplateOptions}), you can pass a `scope` option that
|
53 | * explicitly provides the lexical scope for the template, just like a template-only
|
54 | * component (see {@linkcode ExplicitTemplateOnlyOptions}).
|
55 | *
|
56 | * ```ts
|
57 | * class MyComponent extends Component {
|
58 | * static {
|
59 | * template('{{this.greeting}}, {{@place}}!',
|
60 | * { component: this },
|
61 | * // explicit or implicit option goes here
|
62 | * );
|
63 | * }
|
64 | * }
|
65 | * ```
|
66 | *
|
67 | * ## The Scope Function's `instance` Parameter
|
68 | *
|
69 | * However, the explicit `scope` function in a *class* also takes an `instance` option
|
70 | * that provides access to the component's instance.
|
71 | *
|
72 | * Once it's supported in Handlebars, this will make it possible to represent private
|
73 | * fields when using the explicit form.
|
74 | *
|
75 | * ```ts
|
76 | * class MyComponent extends Component {
|
77 | * static {
|
78 | * template('{{this.#greeting}}, {{@place}}!',
|
79 | * { component: this },
|
80 | * scope: (instance) => ({ '#greeting': instance.#greeting }),
|
81 | * );
|
82 | * }
|
83 | * }
|
84 | * ```
|
85 | */
|
86 | export interface ExplicitClassOptions<C extends ComponentClass>
|
87 | extends BaseClassTemplateOptions<C> {
|
88 | scope(instance?: InstanceType<C>): Record<string, unknown>;
|
89 | }
|
90 | /**
|
91 | * The *implicit* form of the `template` function takes an `eval` option that
|
92 | * allows the runtime compiler to evaluate local template variables without
|
93 | * needing to maintain an explicit list of the local variables used in the
|
94 | * template scope.
|
95 | *
|
96 | * The eval options *must* be passed in the following form:
|
97 | *
|
98 | * ```ts
|
99 | * {
|
100 | * eval() { return eval(arguments[0]) }
|
101 | * }
|
102 | * ```
|
103 | *
|
104 | * ## Requirements of the `eval` Option
|
105 | *
|
106 | * **The syntactic form presented above is the only form you should use when
|
107 | * passing an `eval` option.**
|
108 | *
|
109 | * This is _required_ if you want your code to be compatible with the
|
110 | * compile-time implementation of `@ember/template-compiler`. While the runtime
|
111 | * compiler offers a tiny bit of additional wiggle room, you still need to follow
|
112 | * very strict rules.
|
113 | *
|
114 | * We don't recommend trying to memorize the rules. Instead, we recommend using
|
115 | * the snippet presented above and supported by the compile-time implementation.
|
116 | *
|
117 | * ### The Technical Requirements of the `eval` Option
|
118 | *
|
119 | * The `eval` function is passed a single parameter that is a JavaScript
|
120 | * identifier. This will be extended in the future to support private fields.
|
121 | *
|
122 | * Since keywords in JavaScript are contextual (e.g. `await` and `yield`), the
|
123 | * parameter might be a keyword. The `@ember/template-compiler/runtime` expects
|
124 | * the function to throw a `SyntaxError` if the identifier name is not valid in
|
125 | * the current scope. (The direct `eval` function takes care of this out of the
|
126 | * box.)
|
127 | *
|
128 | * Requirements:
|
129 | *
|
130 | * 1. The `eval` method must receive its parameter as `arguments[0]`, which
|
131 | * ensures that the variable name passed to `eval()` is not shadowed by the
|
132 | * function's parameter name.
|
133 | * 2. The `eval` option must be a function or concise method, and not an arrow.
|
134 | * This is because arrows do not have their own `arguments`, which breaks
|
135 | * (1).
|
136 | * 3. The `eval` method must call "*direct* `eval`", and not an alias of `eval`.
|
137 | * Direct `eval` evaluates the code in the scope it was called from, while
|
138 | * aliased versions of `eval` (including `new Function`) evaluate the code in
|
139 | * the global scope.
|
140 | * 4. The `eval` method must return the result of calling "direct `eval`".
|
141 | *
|
142 | * The easiest way to achieve these requirements is to use the exact syntax
|
143 | * presented above. This is *also* the only way to be compatible
|
144 | *
|
145 | * ## Rationale
|
146 | *
|
147 | * This is useful for two reasons:
|
148 | *
|
149 | * 1. This form is a useful _intermediate_ form for the compile-time toolchain.
|
150 | * It allows the content-tag preprocessor to convert the `<template>` syntax
|
151 | * into valid JavaScript without needing to involve full-fledged lexical
|
152 | * analysis.
|
153 | * 2. This form is a convenient form for manual prototyping when using the
|
154 | * runtime compiler directly. While it requires some extra typing relative to
|
155 | * `<template>`, it's a mechanical 1:1 transformation of the syntax.
|
156 | *
|
157 | * In practice, implementations that use a runtime compiler (for example, a
|
158 | * playground running completely in the browser) should probably use the
|
159 | * `content-tag` preprocessor to convert the template into the implicit form,
|
160 | * and then rely on `@ember/template-compiler/runtime` to evaluate the template.
|
161 | */
|
162 | export interface ImplicitEvalOption {
|
163 | eval(): unknown;
|
164 | }
|
165 | /**
|
166 | * When using `template` outside of a class (i.e. a "template-only component"), you can pass
|
167 | * an `eval` option that _implicitly_ provides the lexical scope for the template.
|
168 | *
|
169 | * This is called the "implicit form".
|
170 | *
|
171 | * ```ts
|
172 | * const greeting = 'Hello';
|
173 | * const HelloWorld = template('{{greeting}} World!', {
|
174 | * eval() { return arguments[0] }
|
175 | * });
|
176 | * ```
|
177 | *
|
178 | * For more details on the requirements of the `eval` option, see {@linkcode ImplicitEvalOption}.
|
179 | */
|
180 | export type ImplicitTemplateOnlyOptions = BaseTemplateOptions & ImplicitEvalOption;
|
181 | /**
|
182 | * When using `template` inside of a class, you can pass an `eval` option that
|
183 | * _implicitly_ provides the lexical scope for the template, just as you can
|
184 | * with a {@linkcode ImplicitTemplateOnlyOptions | template-only component}.
|
185 | *
|
186 | * This is called the "implicit form".
|
187 | *
|
188 | * ```ts
|
189 | * class MyComponent extends Component {
|
190 | * static {
|
191 | * template('{{this.greeting}}, {{@place}}!',
|
192 | * { component: this },
|
193 | * eval() { return arguments[0] }
|
194 | * );
|
195 | * }
|
196 | * }
|
197 | * ```
|
198 | *
|
199 | * ## Note on Private Fields
|
200 | *
|
201 | * The current implementation of `@ember/template-compiler` does not support
|
202 | * private fields, but once the Handlebars parser adds support for private field
|
203 | * syntax and it's implemented in the Glimmer compiler, the implicit form should
|
204 | * be able to support them.
|
205 | */
|
206 | export type ImplicitClassOptions<C extends ComponentClass> = BaseClassTemplateOptions<C> &
|
207 | ImplicitEvalOption;
|
208 | export function template(
|
209 | templateString: string,
|
210 | options?: ExplicitTemplateOnlyOptions | ImplicitTemplateOnlyOptions
|
211 | ): TemplateOnlyComponent;
|
212 | export function template<C extends ComponentClass>(
|
213 | templateString: string,
|
214 | options: ExplicitClassOptions<C> | ImplicitClassOptions<C> | BaseClassTemplateOptions<C>
|
215 | ): C;
|
216 | export {};
|
217 | }
|