@microsoft/fast-element 2.0.0-beta.17 → 2.0.0-beta.18
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.json +21 -0
- package/CHANGELOG.md +10 -1
- package/dist/dts/dom-policy.d.ts +68 -0
- package/dist/dts/dom.d.ts +116 -0
- package/dist/dts/index.d.ts +3 -2
- package/dist/dts/index.rollup.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +15 -24
- package/dist/dts/polyfills.d.ts +0 -1
- package/dist/dts/templating/binding-signal.d.ts +3 -1
- package/dist/dts/templating/binding-two-way.d.ts +3 -1
- package/dist/dts/templating/binding.d.ts +16 -5
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/dangerous-html.d.ts +18 -0
- package/dist/dts/templating/html-directive.d.ts +50 -119
- package/dist/dts/templating/node-observation.d.ts +11 -1
- package/dist/dts/templating/ref.d.ts +4 -0
- package/dist/dts/templating/render.d.ts +30 -6
- package/dist/dts/templating/repeat.d.ts +1 -5
- package/dist/dts/templating/template.d.ts +39 -13
- package/dist/dts/templating/view.d.ts +2 -2
- package/dist/dts/utilities.d.ts +39 -0
- package/dist/esm/components/attributes.js +1 -1
- package/dist/esm/debug.js +4 -1
- package/dist/esm/dom-policy.js +337 -0
- package/dist/esm/dom.js +117 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/platform.js +1 -1
- package/dist/esm/polyfills.js +3 -7
- package/dist/esm/templating/binding-signal.js +3 -2
- package/dist/esm/templating/binding-two-way.js +3 -2
- package/dist/esm/templating/binding.js +31 -54
- package/dist/esm/templating/compiler.js +31 -38
- package/dist/esm/templating/dangerous-html.js +23 -0
- package/dist/esm/templating/html-directive.js +38 -135
- package/dist/esm/templating/node-observation.js +14 -8
- package/dist/esm/templating/ref.js +1 -1
- package/dist/esm/templating/render.js +17 -6
- package/dist/esm/templating/repeat.js +2 -6
- package/dist/esm/templating/template.js +81 -56
- package/dist/esm/testing/fixture.js +1 -1
- package/dist/esm/utilities.js +68 -0
- package/dist/fast-element.api.json +1088 -608
- package/dist/fast-element.d.ts +190 -147
- package/dist/fast-element.debug.js +708 -384
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +679 -358
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +190 -147
- package/docs/api-report.md +66 -56
- package/package.json +5 -1
- package/yarn-error.log +177 -0
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/templating/dom.js +0 -49
|
@@ -3,7 +3,7 @@ import { isFunction, isString } from "../interfaces.js";
|
|
|
3
3
|
import { bind, normalizeBinding, oneTime, } from "./binding.js";
|
|
4
4
|
import { Binding, HTMLDirective, } from "./html-directive.js";
|
|
5
5
|
import { Markup } from "./markup.js";
|
|
6
|
-
import { html, } from "./template.js";
|
|
6
|
+
import { html, ViewTemplate, } from "./template.js";
|
|
7
7
|
/**
|
|
8
8
|
* A Behavior that enables advanced rendering.
|
|
9
9
|
* @public
|
|
@@ -27,7 +27,7 @@ export class RenderBehavior {
|
|
|
27
27
|
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
28
28
|
*/
|
|
29
29
|
bind(controller) {
|
|
30
|
-
this.location = controller.targets[this.directive.
|
|
30
|
+
this.location = controller.targets[this.directive.targetNodeId];
|
|
31
31
|
this.controller = controller;
|
|
32
32
|
this.data = this.dataBindingObserver.bind(controller);
|
|
33
33
|
this.template = this.templateBindingObserver.bind(controller);
|
|
@@ -141,7 +141,7 @@ function instructionToTemplate(def) {
|
|
|
141
141
|
}
|
|
142
142
|
return def.template;
|
|
143
143
|
}
|
|
144
|
-
function createElementTemplate(tagName, attributes, content) {
|
|
144
|
+
function createElementTemplate(tagName, attributes, content, policy) {
|
|
145
145
|
const markup = [];
|
|
146
146
|
const values = [];
|
|
147
147
|
if (attributes) {
|
|
@@ -170,7 +170,7 @@ function createElementTemplate(tagName, attributes, content) {
|
|
|
170
170
|
const lastIndex = markup.length - 1;
|
|
171
171
|
markup[lastIndex] = `${markup[lastIndex]}${content !== null && content !== void 0 ? content : ""}</${tagName}>`;
|
|
172
172
|
}
|
|
173
|
-
return
|
|
173
|
+
return ViewTemplate.create(markup, values, policy);
|
|
174
174
|
}
|
|
175
175
|
function create(options) {
|
|
176
176
|
var _a, _b;
|
|
@@ -187,7 +187,7 @@ function create(options) {
|
|
|
187
187
|
throw new Error("Invalid element for model rendering.");
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
template = createElementTemplate(tagName, (_b = options.attributes) !== null && _b !== void 0 ? _b : defaultAttributes, options.content);
|
|
190
|
+
template = createElementTemplate(tagName, (_b = options.attributes) !== null && _b !== void 0 ? _b : defaultAttributes, options.content, options.policy);
|
|
191
191
|
}
|
|
192
192
|
else {
|
|
193
193
|
template = options.template;
|
|
@@ -239,6 +239,11 @@ export const RenderInstruction = Object.freeze({
|
|
|
239
239
|
/**
|
|
240
240
|
* Creates a RenderInstruction for a set of options.
|
|
241
241
|
* @param options - The options to use when creating the RenderInstruction.
|
|
242
|
+
* @remarks
|
|
243
|
+
* This API should be used with caution. When providing attributes or content,
|
|
244
|
+
* if not done properly, you can open up the application to XSS attacks. When using this API,
|
|
245
|
+
* provide a strong DOMPolicy that can properly sanitize and also be sure to manually sanitize
|
|
246
|
+
* content and attribute values particularly if they can come from user input.
|
|
242
247
|
*/
|
|
243
248
|
create,
|
|
244
249
|
/**
|
|
@@ -246,7 +251,13 @@ export const RenderInstruction = Object.freeze({
|
|
|
246
251
|
* @param tagName - The tag name to use when creating the template.
|
|
247
252
|
* @param attributes - The attributes to apply to the element.
|
|
248
253
|
* @param content - The content to insert into the element.
|
|
254
|
+
* @param policy - The DOMPolicy to create the template with.
|
|
249
255
|
* @returns A template based on the provided specifications.
|
|
256
|
+
* @remarks
|
|
257
|
+
* This API should be used with caution. When providing attributes or content,
|
|
258
|
+
* if not done properly, you can open up the application to XSS attacks. When using this API,
|
|
259
|
+
* provide a strong DOMPolicy that can properly sanitize and also be sure to manually sanitize
|
|
260
|
+
* content and attribute values particularly if they can come from user input.
|
|
250
261
|
*/
|
|
251
262
|
createElementTemplate,
|
|
252
263
|
/**
|
|
@@ -356,7 +367,7 @@ export function render(value, template) {
|
|
|
356
367
|
result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
|
|
357
368
|
}
|
|
358
369
|
return result;
|
|
359
|
-
}, true);
|
|
370
|
+
}, void 0, true);
|
|
360
371
|
}
|
|
361
372
|
else if (isString(template)) {
|
|
362
373
|
templateBindingDependsOnData = true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Observable } from "../observation/observable.js";
|
|
2
2
|
import { emptyArray } from "../platform.js";
|
|
3
3
|
import { ArrayObserver } from "../observation/arrays.js";
|
|
4
|
-
import { Markup
|
|
4
|
+
import { Markup } from "./markup.js";
|
|
5
5
|
import { HTMLDirective, } from "./html-directive.js";
|
|
6
6
|
import { HTMLView } from "./view.js";
|
|
7
7
|
import { normalizeBinding } from "./binding.js";
|
|
@@ -53,7 +53,7 @@ export class RepeatBehavior {
|
|
|
53
53
|
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
54
54
|
*/
|
|
55
55
|
bind(controller) {
|
|
56
|
-
this.location = controller.targets[this.directive.
|
|
56
|
+
this.location = controller.targets[this.directive.targetNodeId];
|
|
57
57
|
this.controller = controller;
|
|
58
58
|
this.items = this.itemsBindingObserver.bind(controller);
|
|
59
59
|
this.template = this.templateBindingObserver.bind(controller);
|
|
@@ -233,10 +233,6 @@ export class RepeatDirective {
|
|
|
233
233
|
this.dataBinding = dataBinding;
|
|
234
234
|
this.templateBinding = templateBinding;
|
|
235
235
|
this.options = options;
|
|
236
|
-
/**
|
|
237
|
-
* The unique id of the factory.
|
|
238
|
-
*/
|
|
239
|
-
this.id = nextId();
|
|
240
236
|
ArrayObserver.enable();
|
|
241
237
|
}
|
|
242
238
|
/**
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import { isFunction,
|
|
1
|
+
import { isFunction, noop } from "../interfaces.js";
|
|
2
|
+
import { FAST } from "../platform.js";
|
|
2
3
|
import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
|
|
3
4
|
import { Compiler } from "./compiler.js";
|
|
4
|
-
import {
|
|
5
|
+
import { Binding, HTMLDirective, } from "./html-directive.js";
|
|
5
6
|
import { nextId } from "./markup.js";
|
|
7
|
+
// Much thanks to LitHTML for working this out!
|
|
8
|
+
const lastAttributeNameRegex =
|
|
9
|
+
/* eslint-disable-next-line no-control-regex */
|
|
10
|
+
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
11
|
+
function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
|
|
12
|
+
if (definition.aspected) {
|
|
13
|
+
const match = lastAttributeNameRegex.exec(prevString);
|
|
14
|
+
if (match !== null) {
|
|
15
|
+
HTMLDirective.assignAspect(value, match[2]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return value.createHTML(add);
|
|
19
|
+
}
|
|
6
20
|
/**
|
|
7
21
|
* A template capable of creating HTMLView instances or rendering directly to DOM.
|
|
8
22
|
* @public
|
|
@@ -12,8 +26,10 @@ export class ViewTemplate {
|
|
|
12
26
|
* Creates an instance of ViewTemplate.
|
|
13
27
|
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
14
28
|
* @param factories - The directives that will be connected to placeholders in the html.
|
|
29
|
+
* @param policy - The security policy to use when compiling this template.
|
|
15
30
|
*/
|
|
16
|
-
constructor(html, factories) {
|
|
31
|
+
constructor(html, factories = {}, policy) {
|
|
32
|
+
this.policy = policy;
|
|
17
33
|
this.result = null;
|
|
18
34
|
/**
|
|
19
35
|
* Opts out of JSON stringification.
|
|
@@ -29,10 +45,28 @@ export class ViewTemplate {
|
|
|
29
45
|
*/
|
|
30
46
|
create(hostBindingTarget) {
|
|
31
47
|
if (this.result === null) {
|
|
32
|
-
this.result = Compiler.compile(this.html, this.factories);
|
|
48
|
+
this.result = Compiler.compile(this.html, this.factories, this.policy);
|
|
33
49
|
}
|
|
34
50
|
return this.result.createView(hostBindingTarget);
|
|
35
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Sets the DOMPolicy for this template.
|
|
54
|
+
* @param policy - The policy to associated with this template.
|
|
55
|
+
* @returns The modified template instance.
|
|
56
|
+
* @remarks
|
|
57
|
+
* The DOMPolicy can only be set once for a template and cannot be
|
|
58
|
+
* set after the template is compiled.
|
|
59
|
+
*/
|
|
60
|
+
withPolicy(policy) {
|
|
61
|
+
if (this.result) {
|
|
62
|
+
throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
|
|
63
|
+
}
|
|
64
|
+
if (this.policy) {
|
|
65
|
+
throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
|
|
66
|
+
}
|
|
67
|
+
this.policy = policy;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
36
70
|
/**
|
|
37
71
|
* Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
|
|
38
72
|
* @param source - The data source to bind the template to.
|
|
@@ -46,17 +80,47 @@ export class ViewTemplate {
|
|
|
46
80
|
view.appendTo(host);
|
|
47
81
|
return view;
|
|
48
82
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Creates a template based on a set of static strings and dynamic values.
|
|
85
|
+
* @param strings - The static strings to create the template with.
|
|
86
|
+
* @param values - The dynamic values to create the template with.
|
|
87
|
+
* @param policy - The DOMPolicy to associated with the template.
|
|
88
|
+
* @returns A ViewTemplate.
|
|
89
|
+
* @remarks
|
|
90
|
+
* This API should not be used directly under normal circumstances because constructing
|
|
91
|
+
* a template in this way, if not done properly, can open up the application to XSS
|
|
92
|
+
* attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
|
|
93
|
+
* and also be sure to manually sanitize all static strings particularly if they can
|
|
94
|
+
* come from user input.
|
|
95
|
+
*/
|
|
96
|
+
static create(strings, values, policy) {
|
|
97
|
+
let html = "";
|
|
98
|
+
const factories = Object.create(null);
|
|
99
|
+
const add = (factory) => {
|
|
100
|
+
var _a;
|
|
101
|
+
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
102
|
+
factories[id] = factory;
|
|
103
|
+
return id;
|
|
104
|
+
};
|
|
105
|
+
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
106
|
+
const currentString = strings[i];
|
|
107
|
+
let currentValue = values[i];
|
|
108
|
+
let definition;
|
|
109
|
+
html += currentString;
|
|
110
|
+
if (isFunction(currentValue)) {
|
|
111
|
+
currentValue = new HTMLBindingDirective(bind(currentValue));
|
|
112
|
+
}
|
|
113
|
+
else if (currentValue instanceof Binding) {
|
|
114
|
+
currentValue = new HTMLBindingDirective(currentValue);
|
|
115
|
+
}
|
|
116
|
+
else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
|
|
117
|
+
const staticValue = currentValue;
|
|
118
|
+
currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
|
|
119
|
+
}
|
|
120
|
+
html += createHTML(currentValue, currentString, add, definition);
|
|
121
|
+
}
|
|
122
|
+
return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
|
|
58
123
|
}
|
|
59
|
-
return value.createHTML(add);
|
|
60
124
|
}
|
|
61
125
|
/**
|
|
62
126
|
* Transforms a template literal string into a ViewTemplate.
|
|
@@ -68,47 +132,8 @@ function createAspectedHTML(value, prevString, add) {
|
|
|
68
132
|
* @public
|
|
69
133
|
*/
|
|
70
134
|
export function html(strings, ...values) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const add = (factory) => {
|
|
74
|
-
var _a;
|
|
75
|
-
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
76
|
-
factories[id] = factory;
|
|
77
|
-
return id;
|
|
78
|
-
};
|
|
79
|
-
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
80
|
-
const currentString = strings[i];
|
|
81
|
-
const currentValue = values[i];
|
|
82
|
-
let definition;
|
|
83
|
-
html += currentString;
|
|
84
|
-
if (isFunction(currentValue)) {
|
|
85
|
-
html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
|
|
86
|
-
}
|
|
87
|
-
else if (isString(currentValue)) {
|
|
88
|
-
const match = lastAttributeNameRegex.exec(currentString);
|
|
89
|
-
if (match !== null) {
|
|
90
|
-
const directive = new HTMLBindingDirective(oneTime(() => currentValue));
|
|
91
|
-
Aspect.assign(directive, match[2]);
|
|
92
|
-
html += directive.createHTML(add);
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
html += currentValue;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else if (currentValue instanceof Binding) {
|
|
99
|
-
html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
|
|
100
|
-
}
|
|
101
|
-
else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
|
|
102
|
-
html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
if (definition.aspected) {
|
|
106
|
-
html += createAspectedHTML(currentValue, currentString, add);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
html += currentValue.createHTML(add);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
135
|
+
if (Array.isArray(strings) && Array.isArray(strings.raw)) {
|
|
136
|
+
return ViewTemplate.create(strings, values);
|
|
112
137
|
}
|
|
113
|
-
|
|
138
|
+
throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
|
|
114
139
|
}
|
|
@@ -46,7 +46,7 @@ export function fixture(templateNameOrType, options = {}) {
|
|
|
46
46
|
}
|
|
47
47
|
if (typeof templateNameOrType === "string") {
|
|
48
48
|
const html = `<${templateNameOrType}></${templateNameOrType}>`;
|
|
49
|
-
templateNameOrType = new ViewTemplate(html
|
|
49
|
+
templateNameOrType = new ViewTemplate(html);
|
|
50
50
|
}
|
|
51
51
|
const view = templateNameOrType.create();
|
|
52
52
|
const element = findElement(view);
|
package/dist/esm/utilities.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { DOM } from "./dom.js";
|
|
2
|
+
import { ExecutionContext } from "./observation/observable.js";
|
|
3
|
+
import { nextId } from "./templating/markup.js";
|
|
1
4
|
/**
|
|
2
5
|
* Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
|
|
3
6
|
* When the parent of a node is a shadow-root, it will return the host
|
|
@@ -69,3 +72,68 @@ export class UnobservableMutationObserver extends MutationObserver {
|
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
77
|
+
* control ViewBehaviors.
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export const ViewBehaviorOrchestrator = Object.freeze({
|
|
81
|
+
/**
|
|
82
|
+
* Creates a ViewBehaviorOrchestrator.
|
|
83
|
+
* @param source - The source to to associate behaviors with.
|
|
84
|
+
* @returns A ViewBehaviorOrchestrator.
|
|
85
|
+
*/
|
|
86
|
+
create(source) {
|
|
87
|
+
const behaviors = [];
|
|
88
|
+
const targets = {};
|
|
89
|
+
let unbindables = null;
|
|
90
|
+
let isConnected = false;
|
|
91
|
+
return {
|
|
92
|
+
source,
|
|
93
|
+
context: ExecutionContext.default,
|
|
94
|
+
targets,
|
|
95
|
+
get isBound() {
|
|
96
|
+
return isConnected;
|
|
97
|
+
},
|
|
98
|
+
addBehaviorFactory(factory, target) {
|
|
99
|
+
var _a, _b, _c, _d;
|
|
100
|
+
const compiled = factory;
|
|
101
|
+
compiled.id = (_a = compiled.id) !== null && _a !== void 0 ? _a : nextId();
|
|
102
|
+
compiled.targetNodeId = (_b = compiled.targetNodeId) !== null && _b !== void 0 ? _b : nextId();
|
|
103
|
+
compiled.targetTagName = (_c = target.tagName) !== null && _c !== void 0 ? _c : null;
|
|
104
|
+
compiled.policy = (_d = compiled.policy) !== null && _d !== void 0 ? _d : DOM.policy;
|
|
105
|
+
this.addTarget(compiled.targetNodeId, target);
|
|
106
|
+
this.addBehavior(compiled.createBehavior());
|
|
107
|
+
},
|
|
108
|
+
addTarget(nodeId, target) {
|
|
109
|
+
targets[nodeId] = target;
|
|
110
|
+
},
|
|
111
|
+
addBehavior(behavior) {
|
|
112
|
+
behaviors.push(behavior);
|
|
113
|
+
if (isConnected) {
|
|
114
|
+
behavior.bind(this);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
onUnbind(unbindable) {
|
|
118
|
+
if (unbindables === null) {
|
|
119
|
+
unbindables = [];
|
|
120
|
+
}
|
|
121
|
+
unbindables.push(unbindable);
|
|
122
|
+
},
|
|
123
|
+
connectedCallback(controller) {
|
|
124
|
+
if (!isConnected) {
|
|
125
|
+
isConnected = true;
|
|
126
|
+
behaviors.forEach(x => x.bind(this));
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
disconnectedCallback(controller) {
|
|
130
|
+
if (isConnected) {
|
|
131
|
+
isConnected = false;
|
|
132
|
+
if (unbindables !== null) {
|
|
133
|
+
unbindables.forEach(x => x.unbind(this));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
});
|