import {
ComponentReturn,
AnyFunction,
ModifierReturn,
FlattenBlockParams,
Invokable,
NamedArgNames,
UnwrapNamedArgs,
} from './integration';
import {
ComponentSignatureArgs,
ComponentSignatureBlocks,
ComponentSignatureElement,
Get,
InvokableArgs,
MaybeNamed,
PrebindArgs,
SliceFrom,
} from './signature';
/**
* Any value that can be safely emitted into the DOM as top-level content,
* i.e. as `
{{value}}
`.
*
* This includes primitives like strings, numbers and booleans; "nothing"
* values like `null` and `undefined`; DOM nodes; and blockless curly
* component invocations.
*/
export type ContentValue =
| string
| number
| boolean
| null
| undefined
| void
| SafeString
| Node
| ArglessCurlyComponent;
// This encompasses both @glimmer/runtime and @ember/template's notion of `SafeString`s,
// and this coverage is tested in `emit-content.test.ts`.
type SafeString = { toHTML(): string };
// `{{foo}}` becomes `emitContent(resolveOrReturn(foo)({})`, which means if `foo`
// is a component that accepts no args, then this is a valid invocation.
type ArglessCurlyComponent = ComponentReturn<{}, any>;
/**
* Any value that can be safely set as an HTML attribute on a DOM node.
* This includes strings, numbers, booleans and `null`/`undefined`.
*
* Note that this does not include functions, as writing something like
* `onclick={{this.handleClick}}` in a template ultimately relies on
* fallback behavior in the VM to set the `onclick` property, and is
* better performed using the `{{on}}` modifier.
*/
export type AttrValue = string | number | boolean | null | undefined | SafeString;
/**
* A value that is invokable like a component in a template. In an
* appropriate Glint environment, subclasses of `EmberComponent` and
* `GlimmerComponent` are examples of `ComponentLike` values, as are
* the values returned from the `{{component}}` helper.
*
* The `S` signature parameter here is of the same form as the one
* accepted by both the Ember and Glimmer `Component` base classes.
*/
export type ComponentLike = Invokable<
(
...args: InvokableArgs>
) => ComponentReturn<
FlattenBlockParams>,
ComponentSignatureElement
>
>;
/**
* A value that is invokable like a helper in a template. Notably,
* subclasses of `Helper` and the return value of `helper()` from
* `@ember/component/helper` are both `HelperLike` in an appropriate
* Glint environment.
*
* The `S` signature parameter here is of the same form as the one
* accepted by `Helper` and `helper`.
*/
export type HelperLike = Invokable<
(...args: InvokableArgs>) => Get
>;
/**
* A value that is invokable like a modifier in a template. Notably,
* subclasses of `Modifier` and the return value of `modifier()` from
* `ember-modifier` are both `ModifierLike` in an appropriate Glint
* environment.
*
* The `S` signature parameter here is of the same form as the ones
* accepted by `Modifier` and `modifier`.
*/
export type ModifierLike = Invokable<
(element: Get, ...args: InvokableArgs>) => ModifierReturn
>;
/**
* Given a `ComponentLike`, `HelperLike` or `ModifierLike` value
* along with a union representing named args that have been
* pre-bound, this helper returns the same item back, but with those
* named arguments made optional.
*
* This is typically useful in conjunction with something like the
* `{{component}}` helper; for instance, if you wrote this in a
* template:
*
* ```handlebars
* {{yield (component MyComponent message="Hello")}}
* ```
*
* Consumers of that yielded value would be able to invoke the
* component without having to provide a value for `@message`
* themselves. You could represent this in your signature as:
*
* ```ts
* Blocks: {
* default: [WithBoundArgs];
* };
* ```
*
* If you had instead just written `default: [typeof MyComponent]`,
* consumers would still be obligated to provide a `@message`
* arg when invoking the yielded component.
*/
export type WithBoundArgs, BoundArgs extends NamedArgNames> =
T extends Invokable<(...args: [...positional: infer P, named: infer N]) => infer R>
? Invokable<
(
...args: [
...positional: P,
...named: MaybeNamed>, BoundArgs>>,
]
) => R
>
: never;
/**
* Similar to `WithBoundArgs`, this utility type provides a shorthand
* for specifying the type of a helper or modifier whose positional
* argument(s) have been pre-bound.
*
* Given the type of the helper or modifier in question and the number
* of positional arguments that have been pre-bound, this helper returns
* the same item back, but without those leading positional arguments.
*
* For instance, given a helper `FooHelper` with positional arg types
* `[secret: symbol, name: string, age: number]`, then if you wrote
* the following in a template:
*
* ```handlebars
* {{yield (helper FooHelper this.secret)}}
* ```
*
* You could represent that in your signature as:
*
* ```ts
* Blocks: {
* default: [WithBoundPositionals];
* };
* ```
*
* Note that you can use `WithBoundPositionals` and `WithBoundArgs`
* together if you're pre-binding both positional and named arguments
* in the same location.
*/
export type WithBoundPositionals<
T extends Invokable | AnyFunction,
Count extends number,
> =
T extends Invokable<(el: infer El, ...args: infer A) => ModifierReturn>
? Invokable<(el: El, ...args: SliceFrom) => ModifierReturn>
: T extends Invokable<(...args: infer A) => infer R>
? Invokable<(...args: SliceFrom) => R>
: T extends (...args: infer A) => infer R
? Invokable<(...args: SliceFrom) => R>
: never;
export {};