@microsoft/fast-element 2.10.4 → 3.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -2
- package/README.md +244 -1
- package/dist/arrays/arrays.api.json +2621 -0
- package/dist/context/context.api.json +13 -13
- package/dist/declarative/declarative.api.json +8483 -0
- package/dist/di/di.api.json +16 -16
- package/dist/dts/__test__/helpers.d.ts +6 -0
- package/dist/dts/array-observer.d.ts +2 -0
- package/dist/dts/arrays.d.ts +2 -0
- package/dist/dts/attr.d.ts +1 -0
- package/dist/dts/binding/binding.d.ts +15 -5
- package/dist/dts/binding/one-time.d.ts +1 -1
- package/dist/dts/binding/one-way.d.ts +1 -1
- package/dist/dts/binding/signal.d.ts +6 -6
- package/dist/dts/binding/two-way.d.ts +2 -1
- package/dist/dts/binding.d.ts +7 -0
- package/dist/dts/components/attributes.d.ts +1 -4
- package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
- package/dist/dts/components/element-controller.d.ts +80 -114
- package/dist/dts/components/element-hydration.d.ts +1 -1
- package/dist/dts/components/enable-hydration.d.ts +54 -0
- package/dist/dts/components/fast-definitions.d.ts +98 -46
- package/dist/dts/components/fast-element.d.ts +43 -16
- package/dist/dts/components/hydration-tracker.d.ts +83 -0
- package/dist/dts/components/hydration.d.ts +23 -53
- package/dist/dts/components/schema.d.ts +205 -0
- package/dist/dts/context.d.ts +13 -13
- package/dist/dts/css.d.ts +3 -0
- package/dist/dts/debug.d.ts +5 -1
- package/dist/dts/declarative/attribute-map.d.ts +58 -0
- package/dist/dts/declarative/debug.d.ts +4 -0
- package/dist/dts/declarative/index.d.ts +14 -0
- package/dist/dts/declarative/interfaces.d.ts +8 -0
- package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
- package/dist/dts/declarative/observer-map.d.ts +89 -0
- package/dist/dts/declarative/runtime.d.ts +5 -0
- package/dist/dts/declarative/syntax.d.ts +21 -0
- package/dist/dts/declarative/template-bridge.d.ts +33 -0
- package/dist/dts/declarative/template-parser.d.ts +98 -0
- package/dist/dts/declarative/template.d.ts +10 -0
- package/dist/dts/declarative/utilities.d.ts +358 -0
- package/dist/dts/di/di.d.ts +7 -7
- package/dist/dts/directives/children.d.ts +2 -0
- package/dist/dts/directives/node-observation.d.ts +2 -0
- package/dist/dts/directives/ref.d.ts +2 -0
- package/dist/dts/directives/repeat.d.ts +4 -0
- package/dist/dts/directives/slotted.d.ts +2 -0
- package/dist/dts/directives/when.d.ts +3 -0
- package/dist/dts/dom-policy.d.ts +23 -5
- package/dist/dts/dom.d.ts +4 -16
- package/dist/dts/html.d.ts +5 -0
- package/dist/dts/hydration/diagnostics.d.ts +93 -0
- package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
- package/dist/dts/hydration/messages.d.ts +62 -0
- package/dist/dts/hydration/runtime.d.ts +7 -0
- package/dist/dts/hydration/target-builder.d.ts +40 -12
- package/dist/dts/hydration.d.ts +18 -0
- package/dist/dts/index.d.ts +42 -42
- package/dist/dts/index.debug.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +2 -49
- package/dist/dts/observable.d.ts +3 -6
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.d.ts +3 -3
- package/dist/dts/observation/update-queue.d.ts +1 -1
- package/dist/dts/platform.d.ts +45 -8
- package/dist/dts/registry.d.ts +1 -0
- package/dist/dts/render.d.ts +7 -0
- package/dist/dts/schema.d.ts +1 -0
- package/dist/dts/state/exports.d.ts +1 -1
- package/dist/dts/state/state.d.ts +2 -2
- package/dist/dts/styles/css-directive.d.ts +5 -12
- package/dist/dts/styles/css.d.ts +5 -7
- package/dist/dts/styles/element-styles.d.ts +0 -10
- package/dist/dts/styles.d.ts +6 -0
- package/dist/dts/templating/compiler.d.ts +1 -1
- package/dist/dts/templating/html-binding-directive.d.ts +10 -2
- package/dist/dts/templating/html-directive.d.ts +19 -1
- package/dist/dts/templating/hydration-view.d.ts +130 -0
- package/dist/dts/templating/render.d.ts +1 -1
- package/dist/dts/templating/repeat.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +15 -7
- package/dist/dts/templating/view.d.ts +25 -102
- package/dist/dts/templating.d.ts +10 -0
- package/dist/dts/testing/exports.d.ts +2 -2
- package/dist/dts/testing/fakes.d.ts +4 -4
- package/dist/dts/updates.d.ts +1 -0
- package/dist/dts/volatile.d.ts +2 -0
- package/dist/esm/__test__/helpers.js +22 -0
- package/dist/esm/__test__/setup-node.js +18 -0
- package/dist/esm/array-observer.js +1 -0
- package/dist/esm/arrays.js +1 -0
- package/dist/esm/attr.js +1 -0
- package/dist/esm/binding/normalize.js +1 -1
- package/dist/esm/binding/signal.js +4 -4
- package/dist/esm/binding/two-way.js +3 -3
- package/dist/esm/binding.js +4 -0
- package/dist/esm/components/attributes.js +18 -11
- package/dist/esm/components/definition-schema-transforms.js +23 -0
- package/dist/esm/components/element-controller.js +206 -270
- package/dist/esm/components/element-hydration.js +1 -1
- package/dist/esm/components/enable-hydration.js +124 -0
- package/dist/esm/components/fast-definitions.js +219 -56
- package/dist/esm/components/fast-element.js +18 -27
- package/dist/esm/components/hydration-tracker.js +122 -0
- package/dist/esm/components/hydration.js +137 -140
- package/dist/esm/components/schema.js +253 -0
- package/dist/esm/context.js +6 -6
- package/dist/esm/css.js +3 -0
- package/dist/esm/debug.js +27 -26
- package/dist/esm/declarative/attribute-map.js +122 -0
- package/dist/esm/declarative/debug.js +4 -0
- package/dist/esm/declarative/index.js +4 -0
- package/dist/esm/declarative/interfaces.js +9 -0
- package/dist/esm/declarative/observer-map-utilities.js +565 -0
- package/dist/esm/declarative/observer-map.js +216 -0
- package/dist/esm/declarative/runtime.js +14 -0
- package/dist/esm/declarative/syntax.js +36 -0
- package/dist/esm/declarative/template-bridge.js +160 -0
- package/dist/esm/declarative/template-parser.js +306 -0
- package/dist/esm/declarative/template.js +143 -0
- package/dist/esm/declarative/utilities.js +1069 -0
- package/dist/esm/di/di.js +8 -9
- package/dist/esm/directives/children.js +1 -0
- package/dist/esm/directives/node-observation.js +1 -0
- package/dist/esm/directives/ref.js +1 -0
- package/dist/esm/directives/repeat.js +1 -0
- package/dist/esm/directives/slotted.js +1 -0
- package/dist/esm/directives/when.js +1 -0
- package/dist/esm/dom-policy.js +35 -6
- package/dist/esm/dom.js +1 -1
- package/dist/esm/html.js +2 -0
- package/dist/esm/hydration/diagnostics.js +50 -0
- package/dist/esm/hydration/hydration-debugger.js +112 -0
- package/dist/esm/hydration/messages.js +84 -0
- package/dist/esm/hydration/runtime.js +33 -0
- package/dist/esm/hydration/target-builder.js +144 -91
- package/dist/esm/hydration.js +6 -0
- package/dist/esm/index.debug.js +2 -1
- package/dist/esm/index.js +38 -29
- package/dist/esm/index.rollup.debug.js +3 -2
- package/dist/esm/index.rollup.js +1 -1
- package/dist/esm/interfaces.js +2 -45
- package/dist/esm/metadata.js +2 -8
- package/dist/esm/observable.js +1 -4
- package/dist/esm/observation/arrays.js +1 -1
- package/dist/esm/observation/notifier.js +2 -4
- package/dist/esm/observation/observable.js +5 -5
- package/dist/esm/observation/update-queue.js +47 -58
- package/dist/esm/platform.js +31 -30
- package/dist/esm/registry.js +1 -0
- package/dist/esm/render.js +1 -0
- package/dist/esm/schema.js +1 -0
- package/dist/esm/state/exports.js +1 -1
- package/dist/esm/styles/css-directive.js +1 -2
- package/dist/esm/styles/css.js +15 -56
- package/dist/esm/styles/element-styles.js +69 -15
- package/dist/esm/styles.js +2 -0
- package/dist/esm/templating/html-binding-directive.js +11 -9
- package/dist/esm/templating/hydration-view.js +228 -0
- package/dist/esm/templating/render.js +39 -18
- package/dist/esm/templating/repeat.js +69 -33
- package/dist/esm/templating/template.js +7 -7
- package/dist/esm/templating/view.js +25 -234
- package/dist/esm/templating.js +7 -0
- package/dist/esm/testing/exports.js +2 -2
- package/dist/esm/testing/fixture.js +2 -2
- package/dist/esm/testing/timeout.js +2 -2
- package/dist/esm/updates.js +1 -0
- package/dist/esm/volatile.js +1 -0
- package/dist/fast-element.api.json +14389 -11138
- package/dist/fast-element.d.ts +3651 -809
- package/dist/fast-element.debug.js +5666 -4722
- package/dist/fast-element.debug.min.js +2 -2
- package/dist/fast-element.js +5394 -4381
- package/dist/fast-element.min.js +2 -2
- package/dist/fast-element.untrimmed.d.ts +923 -472
- package/dist/hydration/hydration.api.json +6460 -0
- package/dist/styles/styles.api.json +2672 -0
- package/package.json +165 -45
- package/ARCHITECTURE_FASTELEMENT.md +0 -63
- package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
- package/ARCHITECTURE_INTRO.md +0 -10
- package/ARCHITECTURE_OVERVIEW.md +0 -52
- package/ARCHITECTURE_UPDATES.md +0 -11
- package/CHANGELOG.json +0 -2275
- package/DESIGN.md +0 -510
- package/api-extractor.context.json +0 -14
- package/api-extractor.di.json +0 -14
- package/biome.json +0 -4
- package/dist/dts/components/install-hydration.d.ts +0 -1
- package/dist/dts/pending-task.d.ts +0 -32
- package/dist/dts/styles/css-binding-directive.d.ts +0 -60
- package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
- package/dist/esm/components/install-hydration.js +0 -3
- package/dist/esm/pending-task.js +0 -28
- package/dist/esm/polyfills.js +0 -60
- package/dist/esm/styles/css-binding-directive.js +0 -76
- package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
- package/docs/ACKNOWLEDGEMENTS.md +0 -12
- package/docs/api-report.api.md +0 -1122
- package/docs/context/api-report.api.md +0 -69
- package/docs/di/api-report.api.md +0 -315
- package/docs/fast-element-2-changes.md +0 -15
- package/playwright.config.ts +0 -26
- package/scripts/run-api-extractor.js +0 -51
- package/test/index.html +0 -11
- package/test/main.ts +0 -104
- package/test/vite.config.ts +0 -19
- package/tsconfig.api-extractor.json +0 -6
- /package/dist/dts/{polyfills.d.ts → __test__/setup-node.d.ts} +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { installHydrationDiagnostic } from "../hydration/diagnostics.js";
|
|
2
|
+
import { ensureHydrationRuntime } from "../hydration/runtime.js";
|
|
3
|
+
import { SourceLifetime } from "../observation/observable.js";
|
|
4
|
+
import { ElementController } from "./element-controller.js";
|
|
5
|
+
import { isHydratable } from "./hydration.js";
|
|
6
|
+
import { HydrationTracker } from "./hydration-tracker.js";
|
|
7
|
+
export { StopHydration } from "./hydration-tracker.js";
|
|
8
|
+
/**
|
|
9
|
+
* No-op handler used during prerendered bind to discard the
|
|
10
|
+
* upgrade-time burst of attributeChangedCallbacks.
|
|
11
|
+
*/
|
|
12
|
+
function noopAttributeHandler() { }
|
|
13
|
+
let tracker = null;
|
|
14
|
+
let hookInstalled = false;
|
|
15
|
+
/**
|
|
16
|
+
* Enables hydration support for prerendered FAST elements.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* Call this before any FAST elements connect to the DOM. Hydration
|
|
20
|
+
* logic is not active unless this function is called, keeping
|
|
21
|
+
* `FASTElement` lightweight for client-side-only applications.
|
|
22
|
+
*
|
|
23
|
+
* Safe to call multiple times — the hydration hook is installed once
|
|
24
|
+
* and subsequent calls merge their options into the shared tracker.
|
|
25
|
+
* By default, the hook stops hydrating new prerendered elements after
|
|
26
|
+
* the global `hydrationComplete` callback. Set
|
|
27
|
+
* `stopHydration` to `StopHydration.never` for streaming scenarios
|
|
28
|
+
* that append hydratable Declarative Shadow DOM after the initial batch.
|
|
29
|
+
*
|
|
30
|
+
* Pass `debugger: hydrationDebugger()` to swap the default minimal
|
|
31
|
+
* hydration mismatch error message for a rich "Expected / Received"
|
|
32
|
+
* report including the SSR HTML snippet and structured
|
|
33
|
+
* `expected`/`received` fields on `HydrationBindingError` /
|
|
34
|
+
* `HydrationTargetElementError`. The debugger module is tree-shaken
|
|
35
|
+
* out of production hydration bundles unless explicitly imported.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { enableHydration, StopHydration } from "@microsoft/fast-element/hydration.js";
|
|
40
|
+
*
|
|
41
|
+
* enableHydration({
|
|
42
|
+
* stopHydration: StopHydration.never,
|
|
43
|
+
* hydrationComplete() {
|
|
44
|
+
* console.log("hydration complete");
|
|
45
|
+
* },
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Rich hydration mismatch diagnostics
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { enableHydration, hydrationDebugger } from "@microsoft/fast-element/hydration.js";
|
|
52
|
+
*
|
|
53
|
+
* enableHydration({ debugger: hydrationDebugger() });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param options - Optional global hydration callbacks and behavior.
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
export function enableHydration(options) {
|
|
60
|
+
ensureHydrationRuntime();
|
|
61
|
+
if (options === null || options === void 0 ? void 0 : options.debugger) {
|
|
62
|
+
installHydrationDiagnostic(options.debugger.diagnostic);
|
|
63
|
+
}
|
|
64
|
+
if (!hookInstalled) {
|
|
65
|
+
tracker = new HydrationTracker(options !== null && options !== void 0 ? options : {});
|
|
66
|
+
hookInstalled = true;
|
|
67
|
+
const activeTracker = tracker;
|
|
68
|
+
ElementController.installHydrationHook((controller, template, element, host) => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
if (!activeTracker.shouldHydrate || !isHydratable(template)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const callbacks = controller.definition.lifecycleCallbacks;
|
|
74
|
+
activeTracker.add(element);
|
|
75
|
+
try {
|
|
76
|
+
try {
|
|
77
|
+
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.elementWillHydrate) === null || _a === void 0 ? void 0 : _a.call(callbacks, element);
|
|
78
|
+
}
|
|
79
|
+
catch (_c) {
|
|
80
|
+
// A lifecycle callback must never prevent hydration.
|
|
81
|
+
}
|
|
82
|
+
const firstChild = host.firstChild;
|
|
83
|
+
const lastChild = host.lastChild;
|
|
84
|
+
const view = template.hydrate(firstChild, lastChild, element);
|
|
85
|
+
controller.view = view;
|
|
86
|
+
const realHandler = controller.onAttributeChangedCallback;
|
|
87
|
+
controller.onAttributeChangedCallback = noopAttributeHandler;
|
|
88
|
+
try {
|
|
89
|
+
view._skipAttrUpdates = true;
|
|
90
|
+
view.isPrerendered = Promise.resolve(true);
|
|
91
|
+
view.isHydrated = Promise.resolve(true);
|
|
92
|
+
view.bind(controller.source);
|
|
93
|
+
view._skipAttrUpdates = false;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
controller.onAttributeChangedCallback = realHandler;
|
|
97
|
+
}
|
|
98
|
+
view.sourceLifetime =
|
|
99
|
+
SourceLifetime.coupled;
|
|
100
|
+
controller.hasExistingShadowRoot = false;
|
|
101
|
+
try {
|
|
102
|
+
(_b = callbacks === null || callbacks === void 0 ? void 0 : callbacks.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(callbacks, element);
|
|
103
|
+
}
|
|
104
|
+
catch (_d) {
|
|
105
|
+
// A lifecycle callback must never prevent post-hydration work.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
activeTracker.remove(element);
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (options && tracker) {
|
|
115
|
+
// Merge options into existing tracker for subsequent calls
|
|
116
|
+
tracker.mergeOptions(options);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The attribute used to defer hydration of an element.
|
|
121
|
+
* Retained for intersection observer viewport hydration rendering.
|
|
122
|
+
* @beta
|
|
123
|
+
*/
|
|
124
|
+
export const deferHydrationAttribute = "defer-hydration";
|
|
@@ -7,27 +7,171 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
import { isString, KernelServiceId } from "../interfaces.js";
|
|
10
|
+
import { isFunction, isString } from "../interfaces.js";
|
|
12
11
|
import { Observable } from "../observation/observable.js";
|
|
13
|
-
import { createTypeRegistry,
|
|
12
|
+
import { createTypeRegistry, } from "../platform.js";
|
|
14
13
|
import { ElementStyles } from "../styles/element-styles.js";
|
|
15
|
-
import {
|
|
14
|
+
import { AttributeDefinition } from "./attributes.js";
|
|
16
15
|
const defaultShadowOptions = { mode: "open" };
|
|
17
16
|
const defaultElementOptions = {};
|
|
18
17
|
const fastElementBaseTypes = new Set();
|
|
19
18
|
/**
|
|
20
|
-
* The FAST custom element registry
|
|
19
|
+
* The FAST custom element registry.
|
|
20
|
+
* @remarks
|
|
21
|
+
* This registry stores FAST element definitions by constructor so consumers can
|
|
22
|
+
* look up the `FASTElementDefinition` associated with an element type or instance.
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const fastElementRegistry = createTypeRegistry();
|
|
26
|
+
const templateResolvers = new WeakMap();
|
|
27
|
+
const pendingTemplateResolutions = new WeakMap();
|
|
28
|
+
const templateResolutionErrors = new WeakMap();
|
|
29
|
+
const registeredTypesByRegistry = new WeakMap();
|
|
30
|
+
const extensionRegistries = new WeakMap();
|
|
31
|
+
const lateAttributeLookups = new WeakMap();
|
|
32
|
+
function isFASTElementTemplateResolver(value) {
|
|
33
|
+
return isFunction(value);
|
|
34
|
+
}
|
|
35
|
+
function isPromiseLike(value) {
|
|
36
|
+
return typeof (value === null || value === void 0 ? void 0 : value.then) === "function";
|
|
37
|
+
}
|
|
38
|
+
function getRegisteredTypes(registry = customElements) {
|
|
39
|
+
if (registry === customElements) {
|
|
40
|
+
return FASTElementDefinition.isRegistered;
|
|
41
|
+
}
|
|
42
|
+
let registeredTypes = registeredTypesByRegistry.get(registry);
|
|
43
|
+
if (!registeredTypes) {
|
|
44
|
+
registeredTypes = {};
|
|
45
|
+
registeredTypesByRegistry.set(registry, registeredTypes);
|
|
46
|
+
}
|
|
47
|
+
return registeredTypes;
|
|
48
|
+
}
|
|
49
|
+
function finalizeResolvedTemplate(definition, template) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
pendingTemplateResolutions.delete(definition);
|
|
52
|
+
if (definition.template === void 0 && template !== void 0) {
|
|
53
|
+
definition.template = template;
|
|
54
|
+
(_b = (_a = definition.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.templateDidUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, definition.name);
|
|
55
|
+
}
|
|
56
|
+
if (definition.template !== void 0) {
|
|
57
|
+
templateResolutionErrors.delete(definition);
|
|
58
|
+
templateResolvers.delete(definition);
|
|
59
|
+
return definition.template;
|
|
60
|
+
}
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Applies extension callbacks to a FAST element definition.
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export function applyFASTElementExtensions(definition, registry = definition.registry, extensions) {
|
|
68
|
+
if (!(extensions === null || extensions === void 0 ? void 0 : extensions.length)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const typedDefinition = definition;
|
|
72
|
+
let registries = extensionRegistries.get(typedDefinition);
|
|
73
|
+
if (registries === null || registries === void 0 ? void 0 : registries.has(registry)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (registries === void 0) {
|
|
77
|
+
registries = new WeakSet();
|
|
78
|
+
extensionRegistries.set(typedDefinition, registries);
|
|
79
|
+
}
|
|
80
|
+
registries.add(registry);
|
|
81
|
+
for (const extension of extensions) {
|
|
82
|
+
extension(definition);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Tracks attribute definitions that were added after the element was already
|
|
87
|
+
* registered with the platform and therefore are not covered by the browser's
|
|
88
|
+
* static observedAttributes snapshot.
|
|
21
89
|
* @internal
|
|
22
90
|
*/
|
|
23
|
-
export
|
|
91
|
+
export function trackLateAttributeDefinition(definition, attribute) {
|
|
92
|
+
const typedDefinition = definition;
|
|
93
|
+
let lateAttributeLookup = lateAttributeLookups.get(typedDefinition);
|
|
94
|
+
if (lateAttributeLookup === void 0) {
|
|
95
|
+
const lookup = Object.create(null);
|
|
96
|
+
lateAttributeLookups.set(typedDefinition, lookup);
|
|
97
|
+
lateAttributeLookup = lookup;
|
|
98
|
+
}
|
|
99
|
+
lateAttributeLookup[attribute.attribute] = attribute;
|
|
100
|
+
}
|
|
24
101
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @
|
|
102
|
+
* Gets the attribute definitions that were added after platform registration.
|
|
103
|
+
* @internal
|
|
27
104
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
105
|
+
export function getLateAttributeLookup(definition) {
|
|
106
|
+
var _a;
|
|
107
|
+
return ((_a = lateAttributeLookups.get(definition)) !== null && _a !== void 0 ? _a : null);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the concrete template for a FAST element definition when the
|
|
111
|
+
* definition was composed with a template resolver.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function resolveFASTElementTemplate(definition) {
|
|
115
|
+
if (definition.template !== void 0) {
|
|
116
|
+
templateResolutionErrors.delete(definition);
|
|
117
|
+
return definition.template;
|
|
118
|
+
}
|
|
119
|
+
const pendingResolution = pendingTemplateResolutions.get(definition);
|
|
120
|
+
if (pendingResolution) {
|
|
121
|
+
return pendingResolution;
|
|
122
|
+
}
|
|
123
|
+
const templateResolver = templateResolvers.get(definition);
|
|
124
|
+
if (!templateResolver) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
templateResolutionErrors.delete(definition);
|
|
128
|
+
let template;
|
|
129
|
+
try {
|
|
130
|
+
template = templateResolver(definition);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
templateResolutionErrors.set(definition, error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
if (isPromiseLike(template)) {
|
|
137
|
+
const resolution = Promise.resolve(template)
|
|
138
|
+
.then(resolvedTemplate => finalizeResolvedTemplate(definition, resolvedTemplate))
|
|
139
|
+
.catch(error => {
|
|
140
|
+
pendingTemplateResolutions.delete(definition);
|
|
141
|
+
templateResolutionErrors.set(definition, error);
|
|
142
|
+
throw error;
|
|
143
|
+
});
|
|
144
|
+
pendingTemplateResolutions.set(definition, resolution);
|
|
145
|
+
return resolution;
|
|
146
|
+
}
|
|
147
|
+
return finalizeResolvedTemplate(definition, template);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Indicates whether a definition still has a pending template resolver.
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export function hasFASTElementTemplateResolver(definition) {
|
|
154
|
+
return templateResolvers.has(definition);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Gets any pending template resolution error for a FAST element definition.
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
export function getFASTElementTemplateError(definition) {
|
|
161
|
+
return templateResolutionErrors.get(definition);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Sets or clears the template resolution error for a FAST element definition.
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
167
|
+
export function setFASTElementTemplateError(definition, error) {
|
|
168
|
+
const typedDefinition = definition;
|
|
169
|
+
if (error === void 0) {
|
|
170
|
+
templateResolutionErrors.delete(typedDefinition);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
templateResolutionErrors.set(typedDefinition, error);
|
|
174
|
+
}
|
|
31
175
|
/**
|
|
32
176
|
* Defines metadata for a FASTElement.
|
|
33
177
|
* @public
|
|
@@ -39,17 +183,22 @@ export class FASTElementDefinition {
|
|
|
39
183
|
get isDefined() {
|
|
40
184
|
return this.platformDefined;
|
|
41
185
|
}
|
|
42
|
-
constructor(type, nameOrConfig = type
|
|
43
|
-
|
|
186
|
+
constructor(type, nameOrConfig = type
|
|
187
|
+
.definition) {
|
|
188
|
+
var _a;
|
|
44
189
|
this.platformDefined = false;
|
|
45
190
|
if (isString(nameOrConfig)) {
|
|
46
191
|
nameOrConfig = { name: nameOrConfig };
|
|
47
192
|
}
|
|
48
193
|
this.type = type;
|
|
49
194
|
this.name = nameOrConfig.name;
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
195
|
+
this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
|
|
196
|
+
if (isFASTElementTemplateResolver(nameOrConfig.template)) {
|
|
197
|
+
templateResolvers.set(this, nameOrConfig.template);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this.template = nameOrConfig.template;
|
|
201
|
+
}
|
|
53
202
|
const proto = type.prototype;
|
|
54
203
|
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
55
204
|
const observedAttributes = new Array(attributes.length);
|
|
@@ -80,23 +229,47 @@ export class FASTElementDefinition {
|
|
|
80
229
|
? defaultElementOptions
|
|
81
230
|
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
82
231
|
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
232
|
+
this.schema = nameOrConfig.schema;
|
|
83
233
|
fastElementRegistry.register(this);
|
|
84
|
-
|
|
85
|
-
|
|
234
|
+
const registeredTypes = getRegisteredTypes(this.registry);
|
|
235
|
+
if (!Object.prototype.hasOwnProperty.call(registeredTypes, this.name)) {
|
|
236
|
+
Observable.defineProperty(registeredTypes, this.name);
|
|
237
|
+
}
|
|
238
|
+
registeredTypes[this.name] = this.type;
|
|
86
239
|
}
|
|
87
240
|
/**
|
|
88
241
|
* Defines a custom element based on this definition.
|
|
89
242
|
* @param registry - The element registry to define the element in.
|
|
243
|
+
* @param extensions - An optional array of extension callbacks to invoke
|
|
244
|
+
* with this definition before platform registration.
|
|
90
245
|
* @remarks
|
|
91
246
|
* This operation is idempotent per registry.
|
|
92
247
|
*/
|
|
93
|
-
define(registry = this.registry) {
|
|
94
|
-
var
|
|
248
|
+
define(registry = this.registry, extensions) {
|
|
249
|
+
var _a, _b;
|
|
95
250
|
const type = this.type;
|
|
96
251
|
if (!registry.get(this.name)) {
|
|
252
|
+
applyFASTElementExtensions(this, registry, extensions);
|
|
253
|
+
if (this.template === void 0 && templateResolvers.has(this)) {
|
|
254
|
+
void Promise.resolve()
|
|
255
|
+
.then(() => resolveFASTElementTemplate(this))
|
|
256
|
+
.then(template => {
|
|
257
|
+
var _a, _b;
|
|
258
|
+
if (template !== void 0 && !registry.get(this.name)) {
|
|
259
|
+
this.platformDefined = true;
|
|
260
|
+
registry.define(this.name, type, this.elementOptions);
|
|
261
|
+
(_b = (_a = this.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidDefine) === null || _b === void 0 ? void 0 : _b.call(_a, this.name);
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.catch(error => {
|
|
265
|
+
setFASTElementTemplateError(this, error);
|
|
266
|
+
Observable.notify(this, "template");
|
|
267
|
+
});
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
97
270
|
this.platformDefined = true;
|
|
98
271
|
registry.define(this.name, type, this.elementOptions);
|
|
99
|
-
(
|
|
272
|
+
(_b = (_a = this.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidDefine) === null || _b === void 0 ? void 0 : _b.call(_a, this.name);
|
|
100
273
|
}
|
|
101
274
|
return this;
|
|
102
275
|
}
|
|
@@ -107,11 +280,11 @@ export class FASTElementDefinition {
|
|
|
107
280
|
* that describes the element to define.
|
|
108
281
|
*/
|
|
109
282
|
static compose(type, nameOrDef) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}, nameOrDef)
|
|
113
|
-
|
|
114
|
-
return
|
|
283
|
+
const definition = fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)
|
|
284
|
+
? new FASTElementDefinition(class extends type {
|
|
285
|
+
}, nameOrDef)
|
|
286
|
+
: new FASTElementDefinition(type, nameOrDef);
|
|
287
|
+
return Promise.resolve(definition);
|
|
115
288
|
}
|
|
116
289
|
/**
|
|
117
290
|
* Registers a FASTElement base type.
|
|
@@ -121,33 +294,7 @@ export class FASTElementDefinition {
|
|
|
121
294
|
static registerBaseType(type) {
|
|
122
295
|
fastElementBaseTypes.add(type);
|
|
123
296
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Creates an instance of FASTElementDefinition asynchronously. This option assumes
|
|
126
|
-
* that a template and shadowOptions will be provided and completes when those requirements
|
|
127
|
-
* are met.
|
|
128
|
-
* @param type - The type this definition is being created for.
|
|
129
|
-
* @param nameOrDef - The name of the element to define or a config object
|
|
130
|
-
* that describes the element to define.
|
|
131
|
-
* @alpha
|
|
132
|
-
*/
|
|
133
|
-
static composeAsync(type, nameOrDef) {
|
|
134
|
-
return new Promise(resolve => {
|
|
135
|
-
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
136
|
-
resolve(new _a(class extends type {
|
|
137
|
-
}, nameOrDef));
|
|
138
|
-
}
|
|
139
|
-
const definition = new _a(type, nameOrDef);
|
|
140
|
-
Observable.getNotifier(definition).subscribe({
|
|
141
|
-
handleChange: () => {
|
|
142
|
-
var _b, _c;
|
|
143
|
-
(_c = (_b = definition.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.templateDidUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, definition.name);
|
|
144
|
-
resolve(definition);
|
|
145
|
-
},
|
|
146
|
-
}, "template");
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
297
|
}
|
|
150
|
-
_a = FASTElementDefinition;
|
|
151
298
|
/**
|
|
152
299
|
* The definition has been registered to the FAST element registry.
|
|
153
300
|
*/
|
|
@@ -167,12 +314,28 @@ FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
|
167
314
|
* @param name - The name of the defined custom element.
|
|
168
315
|
* @alpha
|
|
169
316
|
*/
|
|
170
|
-
FASTElementDefinition.
|
|
317
|
+
FASTElementDefinition.register = (name_1, ...args_1) => __awaiter(void 0, [name_1, ...args_1], void 0, function* (name, registry = customElements) {
|
|
318
|
+
const registeredTypes = getRegisteredTypes(registry);
|
|
319
|
+
if (!Object.prototype.hasOwnProperty.call(registeredTypes, name)) {
|
|
320
|
+
Observable.defineProperty(registeredTypes, name);
|
|
321
|
+
}
|
|
171
322
|
return new Promise(resolve => {
|
|
172
|
-
if (
|
|
173
|
-
resolve(
|
|
323
|
+
if (registeredTypes[name]) {
|
|
324
|
+
resolve(registeredTypes[name]);
|
|
325
|
+
return;
|
|
174
326
|
}
|
|
175
|
-
Observable.getNotifier(
|
|
327
|
+
const notifier = Observable.getNotifier(registeredTypes);
|
|
328
|
+
const subscriber = {
|
|
329
|
+
handleChange: () => {
|
|
330
|
+
const value = registeredTypes[name];
|
|
331
|
+
if (!value) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
notifier.unsubscribe(subscriber, name);
|
|
335
|
+
resolve(value);
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
notifier.subscribe(subscriber, name);
|
|
176
339
|
});
|
|
177
340
|
});
|
|
178
341
|
Observable.defineProperty(FASTElementDefinition.prototype, "template");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFunction } from "../interfaces.js";
|
|
2
2
|
import { ElementController } from "./element-controller.js";
|
|
3
|
-
import { FASTElementDefinition, } from "./fast-definitions.js";
|
|
3
|
+
import { applyFASTElementExtensions, FASTElementDefinition, resolveFASTElementTemplate, } from "./fast-definitions.js";
|
|
4
4
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
5
5
|
function createFASTElement(BaseType) {
|
|
6
6
|
const type = class extends BaseType {
|
|
@@ -31,29 +31,25 @@ function compose(type, nameOrDef) {
|
|
|
31
31
|
}
|
|
32
32
|
return FASTElementDefinition.compose(this, type);
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
return new Promise(resolve => {
|
|
37
|
-
FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
|
|
38
|
-
resolve(value);
|
|
39
|
-
});
|
|
40
|
-
}).then(value => {
|
|
41
|
-
return value.define().type;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return new Promise(resolve => {
|
|
45
|
-
FASTElementDefinition.composeAsync(this, type).then(value => {
|
|
46
|
-
resolve(value);
|
|
47
|
-
});
|
|
48
|
-
}).then(value => {
|
|
49
|
-
return value.define().type;
|
|
50
|
-
});
|
|
34
|
+
function isPromiseLike(value) {
|
|
35
|
+
return typeof (value === null || value === void 0 ? void 0 : value.then) === "function";
|
|
51
36
|
}
|
|
52
|
-
function define(type, nameOrDef) {
|
|
53
|
-
if (
|
|
54
|
-
|
|
37
|
+
function define(type, nameOrDef, extensions) {
|
|
38
|
+
if (Array.isArray(nameOrDef)) {
|
|
39
|
+
extensions = nameOrDef;
|
|
40
|
+
nameOrDef = undefined;
|
|
55
41
|
}
|
|
56
|
-
|
|
42
|
+
const composePromise = isFunction(type)
|
|
43
|
+
? FASTElementDefinition.compose(type, nameOrDef)
|
|
44
|
+
: FASTElementDefinition.compose(this, type);
|
|
45
|
+
return composePromise.then(def => {
|
|
46
|
+
applyFASTElementExtensions(def, def.registry, extensions);
|
|
47
|
+
const template = resolveFASTElementTemplate(def);
|
|
48
|
+
if (isPromiseLike(template)) {
|
|
49
|
+
return template.then(() => def.define().type);
|
|
50
|
+
}
|
|
51
|
+
return def.define().type;
|
|
52
|
+
});
|
|
57
53
|
}
|
|
58
54
|
function from(BaseType) {
|
|
59
55
|
return createFASTElement(BaseType);
|
|
@@ -82,11 +78,6 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
82
78
|
* @public
|
|
83
79
|
*/
|
|
84
80
|
compose,
|
|
85
|
-
/**
|
|
86
|
-
* Defines metadata for a FASTElement which can be used after it has been resolved to define the element.
|
|
87
|
-
* @alpha
|
|
88
|
-
*/
|
|
89
|
-
defineAsync,
|
|
90
81
|
});
|
|
91
82
|
/**
|
|
92
83
|
* Decorator: Defines a platform custom element based on `FASTElement`.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes when FAST should stop hydrating newly connected prerendered elements.
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export const StopHydration = Object.freeze({
|
|
6
|
+
/**
|
|
7
|
+
* Stop hydrating new prerendered elements after the active hydration batch completes.
|
|
8
|
+
*/
|
|
9
|
+
hydrationComplete: "hydration-complete",
|
|
10
|
+
/**
|
|
11
|
+
* Keep the hydration hook active for later prerendered elements.
|
|
12
|
+
*/
|
|
13
|
+
never: "never",
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Tracks prerendered elements through the hydration lifecycle and
|
|
17
|
+
* fires global callbacks at start and completion. Per-element callbacks
|
|
18
|
+
* (`elementWillHydrate`, `elementDidHydrate`) are handled through
|
|
19
|
+
* definition-level `TemplateLifecycleCallbacks`.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export class HydrationTracker {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
var _a;
|
|
26
|
+
this.elements = new Set();
|
|
27
|
+
this.started = false;
|
|
28
|
+
this.completed = false;
|
|
29
|
+
this.checkTimer = null;
|
|
30
|
+
this.callbacks = options;
|
|
31
|
+
this.stopHydration = (_a = options.stopHydration) !== null && _a !== void 0 ? _a : StopHydration.hydrationComplete;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Indicates whether the hydration hook should attempt to hydrate
|
|
35
|
+
* prerendered elements.
|
|
36
|
+
*/
|
|
37
|
+
get shouldHydrate() {
|
|
38
|
+
return !this.completed || this.stopHydration === StopHydration.never;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Registers an element as pending hydration.
|
|
42
|
+
* Fires `hydrationStarted` on the first call.
|
|
43
|
+
*/
|
|
44
|
+
add(element) {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
this.completed = false;
|
|
47
|
+
if (!this.started) {
|
|
48
|
+
this.started = true;
|
|
49
|
+
try {
|
|
50
|
+
(_b = (_a = this.callbacks).hydrationStarted) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
51
|
+
}
|
|
52
|
+
catch (_c) {
|
|
53
|
+
// A lifecycle callback must never prevent hydration.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
this.elements.add(element);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Removes an element from the pending set and schedules
|
|
60
|
+
* a debounced completion check.
|
|
61
|
+
*/
|
|
62
|
+
remove(element) {
|
|
63
|
+
this.elements.delete(element);
|
|
64
|
+
// Debounce: reset on every removal so we wait until no
|
|
65
|
+
// new elements arrive before declaring complete.
|
|
66
|
+
if (this.checkTimer !== null) {
|
|
67
|
+
clearTimeout(this.checkTimer);
|
|
68
|
+
}
|
|
69
|
+
if (this.elements.size === 0) {
|
|
70
|
+
this.checkTimer = setTimeout(() => {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
this.checkTimer = null;
|
|
73
|
+
if (this.elements.size === 0) {
|
|
74
|
+
try {
|
|
75
|
+
(_b = (_a = this.callbacks).hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
76
|
+
}
|
|
77
|
+
catch (_c) {
|
|
78
|
+
// A lifecycle callback must never prevent post-hydration cleanup.
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
this.started = false;
|
|
82
|
+
this.completed = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}, 0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Merges additional options into the tracker, chaining
|
|
90
|
+
* callbacks so both the original and new callbacks fire.
|
|
91
|
+
*/
|
|
92
|
+
mergeOptions(incoming) {
|
|
93
|
+
const prev = this.callbacks;
|
|
94
|
+
this.callbacks = {
|
|
95
|
+
hydrationStarted: chainCallback(prev.hydrationStarted, incoming.hydrationStarted),
|
|
96
|
+
hydrationComplete: chainCallback(prev.hydrationComplete, incoming.hydrationComplete),
|
|
97
|
+
};
|
|
98
|
+
if (incoming.stopHydration !== void 0) {
|
|
99
|
+
this.stopHydration = incoming.stopHydration;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function chainCallback(first, second) {
|
|
104
|
+
if (!first)
|
|
105
|
+
return second;
|
|
106
|
+
if (!second)
|
|
107
|
+
return first;
|
|
108
|
+
return () => {
|
|
109
|
+
try {
|
|
110
|
+
first();
|
|
111
|
+
}
|
|
112
|
+
catch (_a) {
|
|
113
|
+
// Isolate callbacks so one consumer cannot suppress another.
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
second();
|
|
117
|
+
}
|
|
118
|
+
catch (_b) {
|
|
119
|
+
// Isolate callbacks so one consumer cannot suppress another.
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|