1 | /**
|
2 | * @license
|
3 | * Copyright 2020 Google LLC
|
4 | * SPDX-License-Identifier: BSD-3-Clause
|
5 | */
|
6 | // Any new exports need to be added to the export statement in
|
7 | // `packages/lit/src/index.all.ts`.
|
8 | import { html as coreHtml, svg as coreSvg } from './lit-html.js';
|
9 | /**
|
10 | * Prevents JSON injection attacks.
|
11 | *
|
12 | * The goals of this brand:
|
13 | * 1) fast to check
|
14 | * 2) code is small on the wire
|
15 | * 3) multiple versions of Lit in a single page will all produce mutually
|
16 | * interoperable StaticValues
|
17 | * 4) normal JSON.parse (without an unusual reviver) can not produce a
|
18 | * StaticValue
|
19 | *
|
20 | * Symbols satisfy (1), (2), and (4). We use Symbol.for to satisfy (3), but
|
21 | * we don't care about the key, so we break ties via (2) and use the empty
|
22 | * string.
|
23 | */
|
24 | const brand = Symbol.for('');
|
25 | /** Safely extracts the string part of a StaticValue. */
|
26 | const unwrapStaticValue = (value) => {
|
27 | if (value?.r !== brand) {
|
28 | return undefined;
|
29 | }
|
30 | return value?.['_$litStatic$'];
|
31 | };
|
32 | /**
|
33 | * Wraps a string so that it behaves like part of the static template
|
34 | * strings instead of a dynamic value.
|
35 | *
|
36 | * Users must take care to ensure that adding the static string to the template
|
37 | * results in well-formed HTML, or else templates may break unexpectedly.
|
38 | *
|
39 | * Note that this function is unsafe to use on untrusted content, as it will be
|
40 | * directly parsed into HTML. Do not pass user input to this function
|
41 | * without sanitizing it.
|
42 | *
|
43 | * Static values can be changed, but they will cause a complete re-render
|
44 | * since they effectively create a new template.
|
45 | */
|
46 | export const unsafeStatic = (value) => ({
|
47 | ['_$litStatic$']: value,
|
48 | r: brand,
|
49 | });
|
50 | const textFromStatic = (value) => {
|
51 | if (value['_$litStatic$'] !== undefined) {
|
52 | return value['_$litStatic$'];
|
53 | }
|
54 | else {
|
55 | throw new Error(`Value passed to 'literal' function must be a 'literal' result: ${value}. Use 'unsafeStatic' to pass non-literal values, but
|
56 | take care to ensure page security.`);
|
57 | }
|
58 | };
|
59 | /**
|
60 | * Tags a string literal so that it behaves like part of the static template
|
61 | * strings instead of a dynamic value.
|
62 | *
|
63 | * The only values that may be used in template expressions are other tagged
|
64 | * `literal` results or `unsafeStatic` values (note that untrusted content
|
65 | * should never be passed to `unsafeStatic`).
|
66 | *
|
67 | * Users must take care to ensure that adding the static string to the template
|
68 | * results in well-formed HTML, or else templates may break unexpectedly.
|
69 | *
|
70 | * Static values can be changed, but they will cause a complete re-render since
|
71 | * they effectively create a new template.
|
72 | */
|
73 | export const literal = (strings, ...values) => ({
|
74 | ['_$litStatic$']: values.reduce((acc, v, idx) => acc + textFromStatic(v) + strings[idx + 1], strings[0]),
|
75 | r: brand,
|
76 | });
|
77 | const stringsCache = new Map();
|
78 | /**
|
79 | * Wraps a lit-html template tag (`html` or `svg`) to add static value support.
|
80 | */
|
81 | export const withStatic = (coreTag) => (strings, ...values) => {
|
82 | const l = values.length;
|
83 | let staticValue;
|
84 | let dynamicValue;
|
85 | const staticStrings = [];
|
86 | const dynamicValues = [];
|
87 | let i = 0;
|
88 | let hasStatics = false;
|
89 | let s;
|
90 | while (i < l) {
|
91 | s = strings[i];
|
92 | // Collect any unsafeStatic values, and their following template strings
|
93 | // so that we treat a run of template strings and unsafe static values as
|
94 | // a single template string.
|
95 | while (i < l &&
|
96 | ((dynamicValue = values[i]),
|
97 | (staticValue = unwrapStaticValue(dynamicValue))) !== undefined) {
|
98 | s += staticValue + strings[++i];
|
99 | hasStatics = true;
|
100 | }
|
101 | // If the last value is static, we don't need to push it.
|
102 | if (i !== l) {
|
103 | dynamicValues.push(dynamicValue);
|
104 | }
|
105 | staticStrings.push(s);
|
106 | i++;
|
107 | }
|
108 | // If the last value isn't static (which would have consumed the last
|
109 | // string), then we need to add the last string.
|
110 | if (i === l) {
|
111 | staticStrings.push(strings[l]);
|
112 | }
|
113 | if (hasStatics) {
|
114 | const key = staticStrings.join('$$lit$$');
|
115 | strings = stringsCache.get(key);
|
116 | if (strings === undefined) {
|
117 | // Beware: in general this pattern is unsafe, and doing so may bypass
|
118 | // lit's security checks and allow an attacker to execute arbitrary
|
119 | // code and inject arbitrary content.
|
120 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
121 | staticStrings.raw = staticStrings;
|
122 | stringsCache.set(key, (strings = staticStrings));
|
123 | }
|
124 | values = dynamicValues;
|
125 | }
|
126 | return coreTag(strings, ...values);
|
127 | };
|
128 | /**
|
129 | * Interprets a template literal as an HTML template that can efficiently
|
130 | * render to and update a container.
|
131 | *
|
132 | * Includes static value support from `lit-html/static.js`.
|
133 | */
|
134 | export const html = withStatic(coreHtml);
|
135 | /**
|
136 | * Interprets a template literal as an SVG template that can efficiently
|
137 | * render to and update a container.
|
138 | *
|
139 | * Includes static value support from `lit-html/static.js`.
|
140 | */
|
141 | export const svg = withStatic(coreSvg);
|
142 | //# sourceMappingURL=static.js.map |
\ | No newline at end of file |