diff --git a/change/@microsoft-fast-element-098902d3-3705-4f80-a43c-cca0635c0332.json b/change/@microsoft-fast-element-098902d3-3705-4f80-a43c-cca0635c0332.json new file mode 100644 index 00000000000..e76f9ffbcb3 --- /dev/null +++ b/change/@microsoft-fast-element-098902d3-3705-4f80-a43c-cca0635c0332.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Add a registry path export for FAST element definition lookups.", + "packageName": "@microsoft/fast-element", + "email": "7559015+janechu@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/DESIGN.md b/packages/fast-element/DESIGN.md index 752c5f110e5..fdf37345624 100644 --- a/packages/fast-element/DESIGN.md +++ b/packages/fast-element/DESIGN.md @@ -53,7 +53,9 @@ For deep dives into specific areas, see the linked detailed documents. The library's kernel is module-scoped rather than stored on `globalThis`: import `FAST` from `@microsoft/fast-element`, `Updates` from `@microsoft/fast-element`, and `Observable` from `@microsoft/fast-element`. The root entrypoint exports the FAST Element implementation APIs. Focused package -path exports remain available when a consumer wants a narrower entrypoint. +path exports remain available when a consumer wants a narrower entrypoint, +including `@microsoft/fast-element/registry.js` for FAST element definition +lookups. --- @@ -99,7 +101,7 @@ The previous `FAST.getById()` slot registry, `FASTGlobal` type, and `KernelServi - `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 `FASTElement.compose()` (which returns `Promise`, always resolving immediately) and registered globally via `fastElementRegistry`. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate>` or a `FASTElementTemplateResolver` function that receives the composed definition and returns the concrete template (sync or async). `FASTElementDefinition.template` always stores the concrete `ElementViewTemplate>` after composition or resolver settlement. `FASTElement.define()` returns `Promise` — 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.register()` returns `Promise` — resolving when a definition with the given name has been registered. +`FASTElementDefinition` wraps all the metadata for a custom element class: its tag name, template, styles, and observed attribute list. It is created by `FASTElement.compose()` (which returns `Promise`, 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`. `PartialFASTElementDefinition.template` may be either a concrete `ElementViewTemplate>` or a `FASTElementTemplateResolver` function that receives the composed definition and returns the concrete template (sync or async). `FASTElementDefinition.template` always stores the concrete `ElementViewTemplate>` after composition or resolver settlement. `FASTElement.define()` returns `Promise` — 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.register()` returns `Promise` — resolving when a definition with the given name has been registered. #### Extensions @@ -535,6 +537,7 @@ src/ ├── metadata.ts # Reflect-based metadata helpers ├── utilities.ts # UnobservableMutationObserver and other helpers ├── debug.ts # Exports enableDebug() for human-readable FAST errors +├── registry.ts # fastElementRegistry focused path export ├── observation/ │ ├── observable.ts # Observable, @observable, ExpressionNotifier, ExecutionContext │ ├── notifier.ts # Subscriber, Notifier, SubscriberSet, PropertyChangeNotifier diff --git a/packages/fast-element/README.md b/packages/fast-element/README.md index 6d4a720f3ee..69b634a250d 100644 --- a/packages/fast-element/README.md +++ b/packages/fast-element/README.md @@ -81,8 +81,10 @@ Bundle sizes for each tree-shakeable export are tracked in [`SIZES.md`](./SIZES. The root `@microsoft/fast-element` entrypoint exports the FAST Element implementation APIs, including the element base class, kernel, controller, definition APIs, template APIs, binding helpers, directives, styles, and schema -helpers. Declarative, hydration, context, and dependency injection APIs are -available from their focused path exports. +helpers. The FAST element registry is also available from +`@microsoft/fast-element/registry.js` for focused definition lookups. +Declarative, hydration, context, and dependency injection APIs are available +from their focused path exports. Focused package path exports remain available for consumers that want to import a narrower entrypoint directly. The website's diff --git a/packages/fast-element/docs/api-report.api.md b/packages/fast-element/docs/api-report.api.md index 8e3e048863c..71c58ee5feb 100644 --- a/packages/fast-element/docs/api-report.api.md +++ b/packages/fast-element/docs/api-report.api.md @@ -571,9 +571,7 @@ export class FASTElementDefinition = Co // @public export type FASTElementExtension = (definition: FASTElementDefinition) => void; -// Warning: (ae-internal-missing-underscore) The name "fastElementRegistry" should be prefixed with an underscore because the declaration is marked as @internal -// -// @internal +// @public export const fastElementRegistry: TypeRegistry; // @public @@ -1177,16 +1175,15 @@ export const TwoWaySettings: Readonly<{ configure(settings: TwoWaySettings): void; }>; -// Warning: (ae-forgotten-export) The symbol "TypeDefinition" needs to be exported by the entry point index.d.ts -// Warning: (ae-internal-missing-underscore) The name "TypeRegistry" should be prefixed with an underscore because the declaration is marked as @internal -// -// @internal +// @public +export interface TypeDefinition { + type: Function; +} + +// @public export interface TypeRegistry { - // (undocumented) getByType(key: Function): TDefinition | undefined; - // (undocumented) getForInstance(object: any): TDefinition | undefined; - // (undocumented) register(definition: TDefinition): boolean; } diff --git a/packages/fast-element/docs/declarative-rendering-lifecycle.md b/packages/fast-element/docs/declarative-rendering-lifecycle.md index 87d20b44564..343b6c12189 100644 --- a/packages/fast-element/docs/declarative-rendering-lifecycle.md +++ b/packages/fast-element/docs/declarative-rendering-lifecycle.md @@ -118,7 +118,9 @@ The DOM after hydration should look like this: ### Fast Element Registry -The `fastElementRegistry` serves as the central coordination point between the two packages: +The `fastElementRegistry` serves as the central coordination point between the +two packages and is available to consumers from +`@microsoft/fast-element/registry.js`: - Stores partial element definitions created by `define()` - Provides lookup mechanism via `register()` for template attachment diff --git a/packages/fast-element/package.json b/packages/fast-element/package.json index 70e592e087e..8307f85c1d3 100644 --- a/packages/fast-element/package.json +++ b/packages/fast-element/package.json @@ -35,6 +35,11 @@ "test": "./src/components/fast-element.ts", "default": "./dist/esm/components/fast-element.js" }, + "./registry.js": { + "types": "./dist/dts/registry.d.ts", + "test": "./src/registry.ts", + "default": "./dist/esm/registry.js" + }, "./declarative.js": { "types": "./dist/dts/declarative/index.d.ts", "test": "./src/declarative/index.ts", diff --git a/packages/fast-element/src/components/fast-definitions.ts b/packages/fast-element/src/components/fast-definitions.ts index 684ab291935..95bd9865c98 100644 --- a/packages/fast-element/src/components/fast-definitions.ts +++ b/packages/fast-element/src/components/fast-definitions.ts @@ -1,6 +1,10 @@ import { type Constructable, isFunction, isString } from "../interfaces.js"; import { Observable } from "../observation/observable.js"; -import { createTypeRegistry, type TypeRegistry } from "../platform.js"; +import { + createTypeRegistry, + type TypeDefinition, + type TypeRegistry, +} from "../platform.js"; import { type ComposableStyles, ElementStyles } from "../styles/element-styles.js"; import type { ElementViewTemplate } from "../templating/template.js"; import { type AttributeConfiguration, AttributeDefinition } from "./attributes.js"; @@ -11,13 +15,16 @@ const defaultElementOptions: ElementDefinitionOptions = {}; const fastElementBaseTypes = new Set(); /** - * The FAST custom element registry - * @internal + * The FAST custom element registry. + * @remarks + * This registry stores FAST element definitions by constructor so consumers can + * look up the `FASTElementDefinition` associated with an element type or instance. + * @public */ export const fastElementRegistry: TypeRegistry = createTypeRegistry(); -export type { TypeRegistry }; +export type { TypeDefinition, TypeRegistry }; /** * Shadow root initialization options. diff --git a/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts b/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts index 7209da14bcc..0e26caa3ea8 100644 --- a/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts +++ b/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts @@ -30,6 +30,41 @@ test.describe("extension subpaths", () => { expect(result.hasSchema).toBe(true); }); + test("exports the FAST element registry from the registry subpath", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { FASTElementDefinition, fastElementRegistry } = await import( + "/extension-subpaths-main.js" + ); + + class RegistrySubpathElement extends HTMLElement {} + + const name = `registry-subpath-element-${crypto.randomUUID()}`; + const definition = await FASTElementDefinition.compose( + RegistrySubpathElement, + { name }, + ); + definition.define(); + const instance = document.createElement(name); + + return { + hasRegistry: typeof fastElementRegistry.getByType === "function", + getByTypeReturnsDefinition: + fastElementRegistry.getByType(RegistrySubpathElement) === definition, + getForInstanceReturnsDefinition: + fastElementRegistry.getForInstance(instance) === definition, + }; + }); + + expect(result.hasRegistry).toBe(true); + expect(result.getByTypeReturnsDefinition).toBe(true); + expect(result.getForInstanceReturnsDefinition).toBe(true); + }); + test("observerMap accepts a schema for non-declarative use", async ({ page }) => { await page.goto("/"); diff --git a/packages/fast-element/src/index.ts b/packages/fast-element/src/index.ts index 6422c0b9c99..bb2980e6a69 100644 --- a/packages/fast-element/src/index.ts +++ b/packages/fast-element/src/index.ts @@ -33,6 +33,7 @@ export type { FASTElementTemplateResolver, PartialFASTElementDefinition, ShadowRootOptions, + TypeDefinition, TypeRegistry, } from "./components/fast-definitions.js"; export { diff --git a/packages/fast-element/src/platform.ts b/packages/fast-element/src/platform.ts index 715e7b45edb..9d1276ec04b 100644 --- a/packages/fast-element/src/platform.ts +++ b/packages/fast-element/src/platform.ts @@ -50,20 +50,38 @@ export function getDebugMessageLookup(): Record { export const emptyArray = Object.freeze([]); /** - * Do not change. Part of shared kernel contract. - * @internal + * A type that can be registered with a `TypeRegistry`. + * @public */ export interface TypeDefinition { + /** + * The registered type constructor. + */ type: Function; } /** - * Do not change. Part of shared kernel contract. - * @internal + * A registry that stores definitions by type. + * @public */ export interface TypeRegistry { + /** + * Registers a type definition. + * @param definition - The type definition to register. + * @returns `true` when the definition was registered, otherwise `false`. + */ register(definition: TDefinition): boolean; + + /** + * Gets a definition by type. + * @param key - The type to retrieve the definition for. + */ getByType(key: Function): TDefinition | undefined; + + /** + * Gets a definition by instance. + * @param object - The instance to retrieve the definition for. + */ getForInstance(object: any): TDefinition | undefined; } diff --git a/packages/fast-element/src/registry.ts b/packages/fast-element/src/registry.ts new file mode 100644 index 00000000000..1ae8a215c30 --- /dev/null +++ b/packages/fast-element/src/registry.ts @@ -0,0 +1,6 @@ +export { + FASTElementDefinition, + fastElementRegistry, + type TypeDefinition, + type TypeRegistry, +} from "./components/fast-definitions.js"; diff --git a/packages/fast-element/test/extension-subpaths-main.ts b/packages/fast-element/test/extension-subpaths-main.ts index 4e50f4721b1..5475576e63e 100644 --- a/packages/fast-element/test/extension-subpaths-main.ts +++ b/packages/fast-element/test/extension-subpaths-main.ts @@ -6,4 +6,8 @@ export { ObserverMap, observerMap, } from "@microsoft/fast-element/observer-map.js"; +export { + FASTElementDefinition, + fastElementRegistry, +} from "@microsoft/fast-element/registry.js"; export { Schema } from "../src/components/schema.js"; diff --git a/sites/website/src/docs/3.x/advanced/path-exports.md b/sites/website/src/docs/3.x/advanced/path-exports.md index 89f81cce322..d9f451f120d 100644 --- a/sites/website/src/docs/3.x/advanced/path-exports.md +++ b/sites/website/src/docs/3.x/advanced/path-exports.md @@ -26,6 +26,7 @@ This table is generated from the `exports` field in `@microsoft/fast-element/pac | `@microsoft/fast-element` | Root implementation export for FAST Element APIs. | `./dist/esm/index.js` | `./dist/dts/index.d.ts` | | `@microsoft/fast-element/debug.js` | Debug message helpers. | `./dist/esm/debug.js` | `./dist/dts/debug.d.ts` | | `@microsoft/fast-element/fast-element.js` | FASTElement and the customElement decorator. | `./dist/esm/components/fast-element.js` | `./dist/dts/components/fast-element.d.ts` | +| `@microsoft/fast-element/registry.js` | FAST element definition registry APIs. | `./dist/esm/registry.js` | `./dist/dts/registry.d.ts` | | `@microsoft/fast-element/declarative.js` | Path-only declarative template APIs. | `./dist/esm/declarative/index.js` | `./dist/dts/declarative/index.d.ts` | | `@microsoft/fast-element/declarative-utilities.js` | Declarative parser and observer-map utilities. | `./dist/esm/declarative/utilities.js` | `./dist/dts/declarative/utilities.d.ts` | | `@microsoft/fast-element/attribute-map.js` | Schema-driven attribute map extension. | `./dist/esm/declarative/attribute-map.js` | `./dist/dts/declarative/attribute-map.d.ts` | diff --git a/sites/website/src/docs/3.x/api/fast-element.fastelementregistry.md b/sites/website/src/docs/3.x/api/fast-element.fastelementregistry.md new file mode 100644 index 00000000000..6ddb20b0170 --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.fastelementregistry.md @@ -0,0 +1,28 @@ +--- +id: "fast-element.fastelementregistry" +title: "fastElementRegistry variable" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.fastelementregistry" + parent: "api3xfast-element" + title: "fastElementRegistry variable" +navigationOptions: + activeKey: "api3xfast-element.fastelementregistry" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [fastElementRegistry](../fast-element.fastelementregistry/index.html) + +## fastElementRegistry variable + +The FAST custom element registry. + +**Signature:** + +```typescript +fastElementRegistry: TypeRegistry +``` + +## Remarks + +This registry stores FAST element definitions by constructor so consumers can look up the `FASTElementDefinition` associated with an element type or instance. diff --git a/sites/website/src/docs/3.x/api/fast-element.md b/sites/website/src/docs/3.x/api/fast-element.md index 028687d8d48..de90b6e0f53 100644 --- a/sites/website/src/docs/3.x/api/fast-element.md +++ b/sites/website/src/docs/3.x/api/fast-element.md @@ -1284,6 +1284,28 @@ Lifecycle callbacks for template events. The settings required to enable two-way binding. + + + +[TypeDefinition](../fast-element.typedefinition/) + + + + +A type that can be registered with a `TypeRegistry`. + + + + + +[TypeRegistry](../fast-element.typeregistry/) + + + + +A registry that stores definitions by type. + + @@ -1519,6 +1541,17 @@ The FAST messaging API for warnings and errors. A minimal base class for FASTElements that also provides static helpers for working with FASTElements. + + + +[fastElementRegistry](../fast-element.fastelementregistry/) + + + + +The FAST custom element registry. + + diff --git a/sites/website/src/docs/3.x/api/fast-element.typedefinition.md b/sites/website/src/docs/3.x/api/fast-element.typedefinition.md new file mode 100644 index 00000000000..ce6259c929f --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typedefinition.md @@ -0,0 +1,68 @@ +--- +id: "fast-element.typedefinition" +title: "TypeDefinition interface" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typedefinition" + parent: "api3xfast-element" + title: "TypeDefinition interface" +navigationOptions: + activeKey: "api3xfast-element.typedefinition" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeDefinition](../fast-element.typedefinition/index.html) + +## TypeDefinition interface + +A type that can be registered with a `TypeRegistry`. + +**Signature:** + +```typescript +export interface TypeDefinition +``` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[type](../fast-element.typedefinition.type/) + + + + + + + +Function + + + + +The registered type constructor. + + +
diff --git a/sites/website/src/docs/3.x/api/fast-element.typedefinition.type.md b/sites/website/src/docs/3.x/api/fast-element.typedefinition.type.md new file mode 100644 index 00000000000..1772de59326 --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typedefinition.type.md @@ -0,0 +1,24 @@ +--- +id: "fast-element.typedefinition.type" +title: "TypeDefinition.type property" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typedefinition.type" + parent: "api3xfast-element" + title: "TypeDefinition.type property" +navigationOptions: + activeKey: "api3xfast-element.typedefinition.type" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeDefinition](../fast-element.typedefinition/index.html) > [type](../fast-element.typedefinition.type/index.html) + +## TypeDefinition.type property + +The registered type constructor. + +**Signature:** + +```typescript +type: Function; +``` \ No newline at end of file diff --git a/sites/website/src/docs/3.x/api/fast-element.typeregistry.getbytype.md b/sites/website/src/docs/3.x/api/fast-element.typeregistry.getbytype.md new file mode 100644 index 00000000000..1c2fa3533c5 --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typeregistry.getbytype.md @@ -0,0 +1,64 @@ +--- +id: "fast-element.typeregistry.getbytype" +title: "TypeRegistry.getByType() method" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typeregistry.getbytype" + parent: "api3xfast-element" + title: "TypeRegistry.getByType() method" +navigationOptions: + activeKey: "api3xfast-element.typeregistry.getbytype" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeRegistry](../fast-element.typeregistry/index.html) > [getByType](../fast-element.typeregistry.getbytype/index.html) + +## TypeRegistry.getByType() method + +Gets a definition by type. + +**Signature:** + +```typescript +getByType(key: Function): TDefinition | undefined; +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +key + + + + +Function + + + + +The type to retrieve the definition for. + + +
+ +**Returns:** + +TDefinition \| undefined diff --git a/sites/website/src/docs/3.x/api/fast-element.typeregistry.getforinstance.md b/sites/website/src/docs/3.x/api/fast-element.typeregistry.getforinstance.md new file mode 100644 index 00000000000..01d961778fd --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typeregistry.getforinstance.md @@ -0,0 +1,64 @@ +--- +id: "fast-element.typeregistry.getforinstance" +title: "TypeRegistry.getForInstance() method" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typeregistry.getforinstance" + parent: "api3xfast-element" + title: "TypeRegistry.getForInstance() method" +navigationOptions: + activeKey: "api3xfast-element.typeregistry.getforinstance" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeRegistry](../fast-element.typeregistry/index.html) > [getForInstance](../fast-element.typeregistry.getforinstance/index.html) + +## TypeRegistry.getForInstance() method + +Gets a definition by instance. + +**Signature:** + +```typescript +getForInstance(object: any): TDefinition | undefined; +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +object + + + + +any + + + + +The instance to retrieve the definition for. + + +
+ +**Returns:** + +TDefinition \| undefined diff --git a/sites/website/src/docs/3.x/api/fast-element.typeregistry.md b/sites/website/src/docs/3.x/api/fast-element.typeregistry.md new file mode 100644 index 00000000000..2683ad82564 --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typeregistry.md @@ -0,0 +1,72 @@ +--- +id: "fast-element.typeregistry" +title: "TypeRegistry interface" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typeregistry" + parent: "api3xfast-element" + title: "TypeRegistry interface" +navigationOptions: + activeKey: "api3xfast-element.typeregistry" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeRegistry](../fast-element.typeregistry/index.html) + +## TypeRegistry interface + +A registry that stores definitions by type. + +**Signature:** + +```typescript +export interface TypeRegistry +``` + +## Methods + + + + + +
+ +Method + + + + +Description + + +
+ +[getByType(key)](../fast-element.typeregistry.getbytype/) + + + + +Gets a definition by type. + + +
+ +[getForInstance(object)](../fast-element.typeregistry.getforinstance/) + + + + +Gets a definition by instance. + + +
+ +[register(definition)](../fast-element.typeregistry.register/) + + + + +Registers a type definition. + + +
diff --git a/sites/website/src/docs/3.x/api/fast-element.typeregistry.register.md b/sites/website/src/docs/3.x/api/fast-element.typeregistry.register.md new file mode 100644 index 00000000000..ca718146737 --- /dev/null +++ b/sites/website/src/docs/3.x/api/fast-element.typeregistry.register.md @@ -0,0 +1,66 @@ +--- +id: "fast-element.typeregistry.register" +title: "TypeRegistry.register() method" +layout: 3x-api +eleventyNavigation: + key: "api3xfast-element.typeregistry.register" + parent: "api3xfast-element" + title: "TypeRegistry.register() method" +navigationOptions: + activeKey: "api3xfast-element.typeregistry.register" +--- + + +[@microsoft/fast-element](../fast-element/index.html) > [TypeRegistry](../fast-element.typeregistry/index.html) > [register](../fast-element.typeregistry.register/index.html) + +## TypeRegistry.register() method + +Registers a type definition. + +**Signature:** + +```typescript +register(definition: TDefinition): boolean; +``` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +definition + + + + +TDefinition + + + + +The type definition to register. + + +
+ +**Returns:** + +boolean + +`true` when the definition was registered, otherwise `false`.