dalila 1.10.3 → 1.10.4
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/dist/core/persist.js +10 -6
- package/dist/runtime/html-sinks.js +79 -5
- package/package.json +1 -1
- package/scripts/dev-server.cjs +22 -10
package/dist/core/persist.js
CHANGED
|
@@ -492,6 +492,9 @@ function escapeInlineScriptContent(script) {
|
|
|
492
492
|
}
|
|
493
493
|
});
|
|
494
494
|
}
|
|
495
|
+
function stringifyInlineScriptLiteral(value) {
|
|
496
|
+
return escapeInlineScriptContent(JSON.stringify(value));
|
|
497
|
+
}
|
|
495
498
|
/**
|
|
496
499
|
* Generate a minimal inline script to prevent FOUC.
|
|
497
500
|
*
|
|
@@ -499,13 +502,14 @@ function escapeInlineScriptContent(script) {
|
|
|
499
502
|
*/
|
|
500
503
|
export function createPreloadScript(options) {
|
|
501
504
|
const { storageKey, defaultValue, target = 'documentElement', attribute = 'data-theme', storageType = 'localStorage', } = options;
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
const
|
|
505
|
+
const safeTarget = target === 'body' ? 'body' : 'documentElement';
|
|
506
|
+
const safeStorageType = storageType === 'sessionStorage' ? 'sessionStorage' : 'localStorage';
|
|
507
|
+
const k = stringifyInlineScriptLiteral(storageKey);
|
|
508
|
+
const d = stringifyInlineScriptLiteral(defaultValue);
|
|
509
|
+
const a = stringifyInlineScriptLiteral(attribute);
|
|
506
510
|
// Still minified
|
|
507
|
-
const script = `(function(){try{var s=${
|
|
508
|
-
return
|
|
511
|
+
const script = `(function(){try{var s=${safeStorageType}.getItem(${k});var v=s==null?${d}:JSON.parse(s);document.${safeTarget}.setAttribute(${a},v)}catch(e){document.${safeTarget}.setAttribute(${a},${d})}})();`;
|
|
512
|
+
return script;
|
|
509
513
|
}
|
|
510
514
|
export function createThemeScript(storageKey, defaultTheme = 'light') {
|
|
511
515
|
return createPreloadScript({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const TRUSTED_POLICY_CACHE_KEY = Symbol.for('dalila.runtime.trustedTypesPolicies');
|
|
2
2
|
const TRUSTED_POLICY_PARSE_SUFFIX = '--dalila-parse';
|
|
3
|
-
const EXECUTABLE_HTML_EVENT_ATTR_PATTERN = /<[^>]+\son[a-z0-9:_-]+\s*=/i;
|
|
4
3
|
const EXECUTABLE_HTML_URL_ATTR_NAMES = new Set([
|
|
5
4
|
'href',
|
|
6
5
|
'src',
|
|
@@ -9,7 +8,6 @@ const EXECUTABLE_HTML_URL_ATTR_NAMES = new Set([
|
|
|
9
8
|
'action',
|
|
10
9
|
'poster',
|
|
11
10
|
]);
|
|
12
|
-
const EXECUTABLE_DATA_URL_PATTERN = /^data:(?:text\/html|application\/xhtml\+xml|image\/svg\+xml)\b/i;
|
|
13
11
|
function getTrustedPolicyCache() {
|
|
14
12
|
const host = globalThis;
|
|
15
13
|
if (host[TRUSTED_POLICY_CACHE_KEY] instanceof Map) {
|
|
@@ -42,7 +40,7 @@ function isHtmlAttributeNameChar(code) {
|
|
|
42
40
|
&& code !== 0x60;
|
|
43
41
|
}
|
|
44
42
|
function isTagBoundaryChar(char) {
|
|
45
|
-
return !char || /
|
|
43
|
+
return !char || char === '/' || char === '>' || isHtmlWhitespaceCode(char.charCodeAt(0));
|
|
46
44
|
}
|
|
47
45
|
function getPreviousNonWhitespaceChar(value, end, start = 0) {
|
|
48
46
|
for (let index = end - 1; index >= start; index -= 1) {
|
|
@@ -82,7 +80,83 @@ function hasExecutableHtmlScriptTag(value) {
|
|
|
82
80
|
function hasExecutableProtocol(value) {
|
|
83
81
|
return value.startsWith('javascript:')
|
|
84
82
|
|| value.startsWith('vbscript:')
|
|
85
|
-
||
|
|
83
|
+
|| value.startsWith('data:');
|
|
84
|
+
}
|
|
85
|
+
function hasExecutableHtmlEventAttribute(value) {
|
|
86
|
+
let index = 0;
|
|
87
|
+
while (index < value.length) {
|
|
88
|
+
const tagStart = value.indexOf('<', index);
|
|
89
|
+
if (tagStart === -1)
|
|
90
|
+
return false;
|
|
91
|
+
let cursor = tagStart + 1;
|
|
92
|
+
const firstCode = value.charCodeAt(cursor);
|
|
93
|
+
if (Number.isNaN(firstCode)
|
|
94
|
+
|| value[cursor] === '/'
|
|
95
|
+
|| value[cursor] === '!'
|
|
96
|
+
|| value[cursor] === '?') {
|
|
97
|
+
index = cursor;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
while (cursor < value.length
|
|
101
|
+
&& !isHtmlWhitespaceCode(value.charCodeAt(cursor))
|
|
102
|
+
&& value[cursor] !== '>') {
|
|
103
|
+
cursor += 1;
|
|
104
|
+
}
|
|
105
|
+
while (cursor < value.length && value[cursor] !== '>') {
|
|
106
|
+
while (cursor < value.length && isHtmlWhitespaceCode(value.charCodeAt(cursor))) {
|
|
107
|
+
cursor += 1;
|
|
108
|
+
}
|
|
109
|
+
if (cursor >= value.length || value[cursor] === '>') {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
if (value[cursor] === '/') {
|
|
113
|
+
cursor += 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const nameStart = cursor;
|
|
117
|
+
while (cursor < value.length && isHtmlAttributeNameChar(value.charCodeAt(cursor))) {
|
|
118
|
+
cursor += 1;
|
|
119
|
+
}
|
|
120
|
+
if (cursor === nameStart) {
|
|
121
|
+
cursor += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const attrName = value.slice(nameStart, cursor).toLowerCase();
|
|
125
|
+
while (cursor < value.length && isHtmlWhitespaceCode(value.charCodeAt(cursor))) {
|
|
126
|
+
cursor += 1;
|
|
127
|
+
}
|
|
128
|
+
if (value[cursor] !== '=') {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (attrName.startsWith('on')) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
cursor += 1;
|
|
135
|
+
while (cursor < value.length && isHtmlWhitespaceCode(value.charCodeAt(cursor))) {
|
|
136
|
+
cursor += 1;
|
|
137
|
+
}
|
|
138
|
+
if (cursor >= value.length) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
const quote = value[cursor];
|
|
142
|
+
if (quote === '"' || quote === '\'') {
|
|
143
|
+
cursor += 1;
|
|
144
|
+
const closingQuoteIndex = value.indexOf(quote, cursor);
|
|
145
|
+
if (closingQuoteIndex === -1) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
cursor = closingQuoteIndex + 1;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
while (cursor < value.length
|
|
152
|
+
&& !isHtmlWhitespaceCode(value.charCodeAt(cursor))
|
|
153
|
+
&& value[cursor] !== '>') {
|
|
154
|
+
cursor += 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
index = cursor + 1;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
86
160
|
}
|
|
87
161
|
function hasExecutableHtmlUrlAttribute(value) {
|
|
88
162
|
let index = 0;
|
|
@@ -194,7 +268,7 @@ export function hasExecutableHtmlSinkPattern(value) {
|
|
|
194
268
|
if (!value)
|
|
195
269
|
return false;
|
|
196
270
|
return hasExecutableHtmlScriptTag(value)
|
|
197
|
-
||
|
|
271
|
+
|| hasExecutableHtmlEventAttribute(value)
|
|
198
272
|
|| hasExecutableHtmlUrlAttribute(value);
|
|
199
273
|
}
|
|
200
274
|
function getTrustedTypesApi() {
|
package/package.json
CHANGED
package/scripts/dev-server.cjs
CHANGED
|
@@ -116,19 +116,27 @@ function createForbiddenPathError() {
|
|
|
116
116
|
return error;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
function
|
|
119
|
+
function toServedRelativePath(candidatePath) {
|
|
120
|
+
if (typeof candidatePath !== 'string' || candidatePath.length === 0) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
120
124
|
const normalizedPath = path.resolve(candidatePath);
|
|
121
125
|
const relativePath = path.relative(rootDirAbs, normalizedPath);
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
if (relativePath === '') {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
if (typeof candidatePath !== 'string' || candidatePath.length === 0) {
|
|
130
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
127
131
|
return null;
|
|
128
132
|
}
|
|
129
133
|
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
return relativePath;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeServedPath(candidatePath) {
|
|
138
|
+
const relativePath = toServedRelativePath(candidatePath);
|
|
139
|
+
return relativePath == null ? null : path.join(rootDirAbs, relativePath);
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
function statServedPath(targetPath) {
|
|
@@ -219,6 +227,10 @@ function stringifyInlineScriptPayload(value, indent = 0) {
|
|
|
219
227
|
.join('\n');
|
|
220
228
|
}
|
|
221
229
|
|
|
230
|
+
function stringifyInlineScriptLiteral(value) {
|
|
231
|
+
return escapeInlineScriptContent(JSON.stringify(value));
|
|
232
|
+
}
|
|
233
|
+
|
|
222
234
|
function normalizePreloadStorageType(storageType) {
|
|
223
235
|
return storageType === 'sessionStorage' ? 'sessionStorage' : 'localStorage';
|
|
224
236
|
}
|
|
@@ -763,14 +775,14 @@ function findTypeScriptFiles(dir, files = []) {
|
|
|
763
775
|
*/
|
|
764
776
|
function generatePreloadScript(name, defaultValue, storageType = 'localStorage') {
|
|
765
777
|
const safeStorageType = normalizePreloadStorageType(storageType);
|
|
766
|
-
const payload =
|
|
778
|
+
const payload = stringifyInlineScriptLiteral({
|
|
767
779
|
key: name,
|
|
768
780
|
defaultValue,
|
|
769
781
|
storageType: safeStorageType,
|
|
770
782
|
});
|
|
771
|
-
const fallbackValue =
|
|
783
|
+
const fallbackValue = stringifyInlineScriptLiteral(defaultValue);
|
|
772
784
|
const script = `(function(){try{var p=${payload};var s=window[p.storageType];var v=s.getItem(p.key);document.documentElement.setAttribute('data-theme',v==null?p.defaultValue:JSON.parse(v))}catch(e){document.documentElement.setAttribute('data-theme',${fallbackValue})}})();`;
|
|
773
|
-
return
|
|
785
|
+
return script;
|
|
774
786
|
}
|
|
775
787
|
|
|
776
788
|
function renderPreloadScriptTags(baseDir) {
|