Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions .github/skills/typescript/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,47 @@ Sub-entry-points expose focused APIs through the `exports` map:
```ts
import { twoWay } from "@microsoft/fast-element/two-way.js";
import { reactive } from "@microsoft/fast-element/state.js";
import { composedParent } from "@microsoft/fast-element/utilities.js";
```

The barrel `index.ts` explicitly lists every re-export grouped by subsystem β€” no
`export *` statements.

Aside from the `index.ts` or `index.*.ts` files there should be no barrel exports.

## Side Effects

Do not add side effectful code and do not add `sideEffects` to `package.json` files.
APIs added should import named exports.

## Browser APIs

### Do not use

Our stance as a framework library is that certain browser APIs are best avoided by
the framework. We may facilitate the use of them through our provided APIs.

List of APIs to avoid implementing in FAST packages:
- `requestIdleCallback`
- `setTimeout`
- `setInterval`

### Avoid

Some browser APIs can be used but should be avoided as a developer may encounter
edge cases with task queuing or other logic their app is executing.

List of APIs to avoid in FAST packages:
- `requestAnimationFrame`
- `queueMicrotask`

## Custom elements

Elements extend `FASTElement`. Do **not** use the `@customElement` decorator.

Use `define()` for registration. `define()` returns a `Promise` that resolves
immediately when a template is provided:

Example define.ts (side-effect import):
```ts
export class MyElement extends FASTElement {
@observable items: string[] = [];
Expand All @@ -45,22 +73,6 @@ await MyElement.define({
});
```

Use `compose()` when registration should be deferred β€” downstream libraries like Fluent
Web Components use this pattern with a design-system registry. `compose()` returns a
`Promise<FASTElementDefinition>` that always resolves immediately:

```ts
// my-element.definition.ts
export const definition = await MyElement.compose({
name: "my-element",
template,
styles,
});

// define.ts (side-effect import)
definition.define();
```

## Templates

Templates use the `html` tagged template literal typed to the element class:
Expand Down Expand Up @@ -255,8 +267,7 @@ methods use `typeof` to carry overloaded signatures:
export const FASTElement: {
new (): FASTElement;
define: typeof define;
compose: typeof compose;
} = Object.assign(createFASTElement(HTMLElement), { define, compose });
} = Object.assign(createFASTElement(HTMLElement), { define });
```

### Tagged template intersection types
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Update typescript skill file and fast element design doc",
"packageName": "@microsoft/fast-element",
"email": "7559015+janechu@users.noreply.github.com",
"dependentChangeType": "none"
}
4 changes: 2 additions & 2 deletions packages/fast-element/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ logging, telemetry, or a devtools panel.
- `onAttributeChangedCallback()` is the standard handler that processes attribute changes. During the prerendered bind, it is temporarily swapped to a no-op (see above) to avoid redundant processing of server-rendered attribute values.
- Exposes `addBehavior` / `removeBehavior` for dynamic `HostBehavior` management (used by `ElementStyles`).

`FASTElementDefinition` wraps all the metadata for a custom element class: its tag name, template, styles, and observed attribute list. It is created by subclass `define()` calls or directly with `FASTElementDefinition.compose()` (which returns `Promise<FASTElementDefinition>`, always resolving immediately) and registered globally via `fastElementRegistry`. Consumers that need focused access to definition lookup can import `fastElementRegistry` from `@microsoft/fast-element/registry.js`; `fastElementRegistry.whenRegistered(tagName)` resolves with the matching definition once that tag name is registered. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate<InstanceType<TType>>` or a `FASTElementTemplateResolver<TType>` function that receives the composed definition and returns the concrete template (sync or async). `FASTElementDefinition.template` always stores the concrete `ElementViewTemplate<InstanceType<TType>>` after composition or resolver settlement. The subclass static `define()` method returns `Promise<TType>` β€” resolving immediately for complete definitions or definitions without an initial template, and resolving async template resolver functions only after extensions have had a chance to update the definition.
`FASTElementDefinition` wraps all the metadata for a custom element class: its tag name, template, styles, and observed attribute list. It is created by subclass `define()` calls and registered globally via `fastElementRegistry`. Consumers that need focused access to definition lookup can import `fastElementRegistry` from `@microsoft/fast-element/registry.js`; `fastElementRegistry.whenRegistered(tagName)` resolves with the matching definition once that tag name is registered. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate<InstanceType<TType>>` or a `FASTElementTemplateResolver<TType>` function that receives the composed definition and returns the concrete template (sync or async). `FASTElementDefinition.template` always stores the concrete `ElementViewTemplate<InstanceType<TType>>` after composition or resolver settlement. The subclass static `define()` method returns `Promise<TType>` β€” resolving immediately for complete definitions or definitions without an initial template, and resolving async template resolver functions only after extensions have had a chance to update the definition.

#### Extensions

Expand Down Expand Up @@ -621,7 +621,7 @@ Below is a conceptual map of the major subsystems and their relationships:
**Authoring flow summary**:

1. Developer writes a class extending `FASTElement`, decorates properties with `@observable` / `@attr`, and calls `MyElement.define({ name, template, styles })`.
2. `MyElement.define` β†’ `FASTElementDefinition.compose(...).define()` registers the element with the Custom Element Registry.
2. `MyElement.define` registers the element with the Custom Element Registry.
3. When the browser upgrades the element, `ElementController.forCustomElement(element)` is called in the constructor.
4. On `connectedCallback`, the controller renders the template into the shadow root. If the element already has a shadow root from SSR (prerendered content) and hydration has been enabled via `enableHydration()`, the installed hydration hook uses `template.hydrate()` to map existing DOM nodes to binding targets instead of cloning new DOM. If no template is available yet, the element connects without rendering until a later `definition.template` update recreates the controller. Compilation is lazy: the first render call triggers `Compiler.compile()`, subsequent calls clone the already-compiled `DocumentFragment`.
5. `HTMLView.bind(source)` wires up each `ViewBehavior`. `oneWay` bindings create `ExpressionNotifier`s that track observable dependencies automatically.
Expand Down
Loading