tungsten 0.2.24 → 0.3.0
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/ContentNode.d.ts +7 -0
- package/dist/ContentNode.d.ts.map +1 -1
- package/dist/Instance/Instance.d.ts.map +1 -1
- package/dist/Instance/Instance.js +7 -5
- package/dist/Instance/gotoRedirect.sxyt.d.ts +2 -0
- package/dist/Instance/gotoRedirect.sxyt.d.ts.map +1 -0
- package/dist/Instance/gotoRedirect.sxyt.js +148 -0
- package/dist/Servers/DevServer.d.ts +7 -0
- package/dist/Servers/DevServer.d.ts.map +1 -1
- package/dist/Servers/DevServer.js +38 -1
- package/dist/Servers/routing.d.ts.map +1 -1
- package/dist/Servers/routing.js +10 -1
- package/dist/build/compileJavascript.d.ts.map +1 -1
- package/dist/build/compileJavascript.js +86 -1
- package/dist/build/compileStyles.d.ts +1 -1
- package/dist/build/compileStyles.d.ts.map +1 -1
- package/dist/build/compileStyles.js +10 -4
- package/dist/build/compileUserApp.d.ts.map +1 -1
- package/dist/build/compileUserApp.js +7 -2
- package/dist/components/RawHtml.d.ts +36 -0
- package/dist/components/RawHtml.d.ts.map +1 -0
- package/dist/components/RawHtml.js +30 -0
- package/dist/components/RawHtml.sxyt.d.ts +2 -0
- package/dist/components/RawHtml.sxyt.d.ts.map +1 -0
- package/dist/components/RawHtml.sxyt.js +104 -0
- package/dist/dev/liveReloadHub.d.ts +6 -0
- package/dist/dev/liveReloadHub.d.ts.map +1 -0
- package/dist/dev/liveReloadHub.js +12 -0
- package/dist/helpers/getFetchCompatibleHeaders.d.ts.map +1 -1
- package/dist/hydrate/hydrate.d.ts.map +1 -1
- package/dist/hydrate/hydrate.js +11 -4
- package/dist/hydrate/reconcileHydratedAttributes.d.ts +24 -0
- package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +1 -0
- package/dist/hydrate/reconcileHydratedAttributes.js +73 -0
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts +2 -0
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts.map +1 -0
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.js +173 -0
- package/dist/indexInner.d.ts +4 -0
- package/dist/indexInner.d.ts.map +1 -1
- package/dist/indexInner.js +3 -0
- package/dist/innerHtmlFromProps.d.ts +16 -0
- package/dist/innerHtmlFromProps.d.ts.map +1 -0
- package/dist/innerHtmlFromProps.js +31 -0
- package/dist/innerHtmlFromProps.sxyt.d.ts +2 -0
- package/dist/innerHtmlFromProps.sxyt.d.ts.map +1 -0
- package/dist/innerHtmlFromProps.sxyt.js +99 -0
- package/dist/postcssPlugins/TransformCssNames.d.ts +14 -1
- package/dist/postcssPlugins/TransformCssNames.d.ts.map +1 -1
- package/dist/postcssPlugins/TransformCssNames.js +97 -95
- package/dist/rawHtml/HtmlAllowList.d.ts +17 -0
- package/dist/rawHtml/HtmlAllowList.d.ts.map +1 -0
- package/dist/rawHtml/HtmlAllowList.js +1 -0
- package/dist/rawHtml/decodeHtmlEntities.d.ts +8 -0
- package/dist/rawHtml/decodeHtmlEntities.d.ts.map +1 -0
- package/dist/rawHtml/decodeHtmlEntities.js +105 -0
- package/dist/rawHtml/htmlParsingTags.d.ts +23 -0
- package/dist/rawHtml/htmlParsingTags.d.ts.map +1 -0
- package/dist/rawHtml/htmlParsingTags.js +55 -0
- package/dist/rawHtml/parseHtml.d.ts +35 -0
- package/dist/rawHtml/parseHtml.d.ts.map +1 -0
- package/dist/rawHtml/parseHtml.js +214 -0
- package/dist/rawHtml/parseHtml.sxyt.d.ts +2 -0
- package/dist/rawHtml/parseHtml.sxyt.d.ts.map +1 -0
- package/dist/rawHtml/parseHtml.sxyt.js +239 -0
- package/dist/rawHtml/safeHtmlAllowList.d.ts +18 -0
- package/dist/rawHtml/safeHtmlAllowList.d.ts.map +1 -0
- package/dist/rawHtml/safeHtmlAllowList.js +47 -0
- package/dist/rawHtml/tokenizeHtml.d.ts +25 -0
- package/dist/rawHtml/tokenizeHtml.d.ts.map +1 -0
- package/dist/rawHtml/tokenizeHtml.js +148 -0
- package/dist/render/processHead.d.ts.map +1 -1
- package/dist/render/processHead.js +10 -0
- package/dist/render/processProps.d.ts.map +1 -1
- package/dist/render/processProps.js +14 -2
- package/dist/render/processProps.sxyt.d.ts +2 -0
- package/dist/render/processProps.sxyt.d.ts.map +1 -0
- package/dist/render/processProps.sxyt.js +90 -0
- package/dist/render/renderingTreeToHtml.d.ts.map +1 -1
- package/dist/render/renderingTreeToHtml.js +8 -0
- package/dist/routing/RouteTypes.d.ts +1 -1
- package/dist/routing/RouteTypes.d.ts.map +1 -1
- package/dist/routing/routeSymbols.d.ts +9 -0
- package/dist/routing/routeSymbols.d.ts.map +1 -0
- package/dist/routing/routeSymbols.js +4 -0
- package/dist/update/integrateUpdateVdom.d.ts.map +1 -1
- package/dist/update/integrateUpdateVdom.js +28 -7
- package/package.json +3 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { decodeHtmlEntities } from "./decodeHtmlEntities.js";
|
|
2
|
+
import { htmlRawTextTags } from "./htmlParsingTags.js";
|
|
3
|
+
// REGEX: < then optional / then a letter -- the only thing that starts a tag.
|
|
4
|
+
// Anything else after a < is literal text, which is how browsers treat it too.
|
|
5
|
+
const tagStart = /<\/?[a-zA-Z]/;
|
|
6
|
+
// REGEX:
|
|
7
|
+
// optional whitespace, then an attribute name (anything but whitespace, /, >, =),
|
|
8
|
+
// then optionally = and a value: "double quoted", 'single quoted', or bare.
|
|
9
|
+
const attributePattern = /\s*([^\s/>=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]*)))?/g;
|
|
10
|
+
/**
|
|
11
|
+
* Index of the `>` that ends a tag, or -1.
|
|
12
|
+
*
|
|
13
|
+
* A `>` inside a quoted attribute value is ordinary text, so scanning for the
|
|
14
|
+
* first one would cut tags such as `<a title="a > b">` short and build a tree
|
|
15
|
+
* the browser does not agree with.
|
|
16
|
+
*/
|
|
17
|
+
function findTagEnd(source) {
|
|
18
|
+
let quote = '';
|
|
19
|
+
for (let i = 1; i < source.length; i++) {
|
|
20
|
+
const character = source[i];
|
|
21
|
+
if (quote !== '') {
|
|
22
|
+
if (character === quote) quote = '';
|
|
23
|
+
} else if (character === '"' || character === '\'') {
|
|
24
|
+
quote = character;
|
|
25
|
+
} else if (character === '>') {
|
|
26
|
+
return i;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return -1;
|
|
30
|
+
}
|
|
31
|
+
function parseAttributes(source) {
|
|
32
|
+
const attributes = {};
|
|
33
|
+
attributePattern.lastIndex = 0;
|
|
34
|
+
let match;
|
|
35
|
+
while ((match = attributePattern.exec(source)) !== null) {
|
|
36
|
+
// A zero-length match cannot advance lastIndex, so bail rather than spin.
|
|
37
|
+
if (match[0] === '') break;
|
|
38
|
+
const name = match[1];
|
|
39
|
+
if (name === undefined || name === '') continue;
|
|
40
|
+
const value = match[2] ?? match[3] ?? match[4];
|
|
41
|
+
attributes[name] = value === undefined ? true : decodeHtmlEntities(value);
|
|
42
|
+
}
|
|
43
|
+
return attributes;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Splits raw markup into text, start and end tokens.
|
|
47
|
+
*
|
|
48
|
+
* Never throws. Anything that is not well-formed markup — a stray `<`, an
|
|
49
|
+
* unterminated tag, a comment that is never closed — is emitted as text or
|
|
50
|
+
* skipped, matching what a browser does with the same input.
|
|
51
|
+
*
|
|
52
|
+
* Comments and doctypes are dropped: Tungsten uses comment nodes as its own
|
|
53
|
+
* component markers, so carrying authored ones through would confuse hydration.
|
|
54
|
+
*/
|
|
55
|
+
export function tokenizeHtml(html) {
|
|
56
|
+
const tokens = [];
|
|
57
|
+
let rest = html;
|
|
58
|
+
const pushText = text => {
|
|
59
|
+
if (text !== '') tokens.push({
|
|
60
|
+
kind: 'text',
|
|
61
|
+
text: decodeHtmlEntities(text)
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
while (rest !== '') {
|
|
65
|
+
const next = rest.search(tagStart);
|
|
66
|
+
// no more tags: the remainder is all text
|
|
67
|
+
if (next === -1) {
|
|
68
|
+
const comment = rest.indexOf('<!');
|
|
69
|
+
if (comment === -1) {
|
|
70
|
+
pushText(rest);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
pushText(rest.slice(0, comment));
|
|
74
|
+
rest = rest.slice(comment);
|
|
75
|
+
} else if (next > 0) {
|
|
76
|
+
const markup = rest.indexOf('<!');
|
|
77
|
+
if (markup !== -1 && markup < next) {
|
|
78
|
+
pushText(rest.slice(0, markup));
|
|
79
|
+
rest = rest.slice(markup);
|
|
80
|
+
} else {
|
|
81
|
+
pushText(rest.slice(0, next));
|
|
82
|
+
rest = rest.slice(next);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// comment or doctype - skip it
|
|
86
|
+
if (rest.startsWith('<!')) {
|
|
87
|
+
if (rest.startsWith('<!--')) {
|
|
88
|
+
const end = rest.indexOf('-->', 4);
|
|
89
|
+
// never closed: the rest of the document is inside the comment
|
|
90
|
+
if (end === -1) break;
|
|
91
|
+
rest = rest.slice(end + 3);
|
|
92
|
+
} else {
|
|
93
|
+
const end = rest.indexOf('>');
|
|
94
|
+
if (end === -1) break;
|
|
95
|
+
rest = rest.slice(end + 1);
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (!rest.startsWith('<')) continue;
|
|
100
|
+
const end = findTagEnd(rest);
|
|
101
|
+
// unterminated tag: treat what is left as text, as a browser does
|
|
102
|
+
if (end === -1) {
|
|
103
|
+
pushText(rest);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
const inner = rest.slice(1, end);
|
|
107
|
+
rest = rest.slice(end + 1);
|
|
108
|
+
if (inner.startsWith('/')) {
|
|
109
|
+
tokens.push({
|
|
110
|
+
kind: 'close',
|
|
111
|
+
name: inner.slice(1).trim().toLowerCase()
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const selfClosing = inner.endsWith('/');
|
|
116
|
+
const body = selfClosing ? inner.slice(0, -1) : inner;
|
|
117
|
+
// REGEX: leading run of tag-name characters
|
|
118
|
+
const nameMatch = /^[^\s/>]+/.exec(body);
|
|
119
|
+
const name = (nameMatch?.[0] ?? '').toLowerCase();
|
|
120
|
+
if (name === '') continue;
|
|
121
|
+
tokens.push({
|
|
122
|
+
kind: 'open',
|
|
123
|
+
name,
|
|
124
|
+
attributes: parseAttributes(body.slice(name.length)),
|
|
125
|
+
selfClosing
|
|
126
|
+
});
|
|
127
|
+
// raw-text elements hold text, not markup, up to their closing tag
|
|
128
|
+
if (name in htmlRawTextTags && !selfClosing) {
|
|
129
|
+
const closeTag = `</${name}`;
|
|
130
|
+
const closeAt = rest.toLowerCase().indexOf(closeTag);
|
|
131
|
+
const text = closeAt === -1 ? rest : rest.slice(0, closeAt);
|
|
132
|
+
if (text !== '') {
|
|
133
|
+
// script and style hold literal program text; textarea and
|
|
134
|
+
// title hold ordinary text, so their entities are decoded.
|
|
135
|
+
tokens.push({
|
|
136
|
+
kind: 'text',
|
|
137
|
+
text: htmlRawTextTags[name] === 'escapable' ? decodeHtmlEntities(text) : text
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (closeAt === -1) {
|
|
141
|
+
rest = '';
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
rest = rest.slice(closeAt);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return tokens;
|
|
148
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processHead.d.ts","sourceRoot":"","sources":["../../src/render/processHead.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAK7C,OAAO,EAAE,oBAAoB,EAAqB,MAAM,qBAAqB,CAAA;AAO7E,wBAAgB,WAAW,CACvB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"processHead.d.ts","sourceRoot":"","sources":["../../src/render/processHead.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAK7C,OAAO,EAAE,oBAAoB,EAAqB,MAAM,qBAAqB,CAAA;AAO7E,wBAAgB,WAAW,CACvB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,QAAQ,QAmHrB"}
|
|
@@ -59,6 +59,16 @@ export function processHead(headNode, headData) {
|
|
|
59
59
|
};
|
|
60
60
|
cjsHelpersScriptNode[$props].type = 'module';
|
|
61
61
|
headNode[$props].children.unshift(cjsHelpersScriptNode);
|
|
62
|
+
// auto-reload the page whenever the dev build finishes recompiling.
|
|
63
|
+
// this is a plain full-page reload signal, not module-level hot patching.
|
|
64
|
+
const liveReloadScriptNode = {
|
|
65
|
+
[$type]: 'script',
|
|
66
|
+
[$props]: {
|
|
67
|
+
'data--t-internal': undefined,
|
|
68
|
+
children: `(function(){` + `var es=new EventSource('${siteBasePath}/__tungsten_live_reload__');` + `es.addEventListener('js',function(){location.reload()});` + `es.addEventListener('css',function(){location.reload()});` + `})();`
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
headNode[$props].children.unshift(liveReloadScriptNode);
|
|
62
72
|
}
|
|
63
73
|
const {
|
|
64
74
|
content: _,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processProps.d.ts","sourceRoot":"","sources":["../../src/render/processProps.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"processProps.d.ts","sourceRoot":"","sources":["../../src/render/processProps.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAA;AAoBjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,6BAA6B,iCA8ChE"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { objects } from 'sxy-lib';
|
|
2
2
|
import { jssToCss } from "../style/jssToCss.js";
|
|
3
|
-
|
|
3
|
+
import { StringIsHtmlEvent } from "../htmlEvents.js";
|
|
4
|
+
// Props that are instructions to Tungsten rather than HTML attributes.
|
|
5
|
+
//
|
|
6
|
+
// Event handlers are not listed here: they are recognised with
|
|
7
|
+
// StringIsHtmlEvent, the same test the client uses. This list previously named
|
|
8
|
+
// a dozen of them by hand and so let the rest — onInput, onFocus, onBlur,
|
|
9
|
+
// onScroll and the rest of htmlEvents — through into the markup as attributes
|
|
10
|
+
// holding a stringified function, and left the server and client disagreeing
|
|
11
|
+
// about which props are attributes at all.
|
|
12
|
+
const omitProps = ['ref', '__zoneHtml', '__opaque', 'dangerouslySetInnerHTML'];
|
|
4
13
|
//// !!!! We need to handle bind here
|
|
5
14
|
export function processProps(props) {
|
|
6
15
|
props = objects.filter(props, key => {
|
|
7
|
-
return !omitProps.includes(key);
|
|
16
|
+
return !omitProps.includes(key) && !StringIsHtmlEvent(key);
|
|
8
17
|
});
|
|
9
18
|
// if ('className' in props) {
|
|
10
19
|
// if ('class' in props) {
|
|
@@ -36,6 +45,9 @@ export function processProps(props) {
|
|
|
36
45
|
} else {
|
|
37
46
|
props.style = props.css;
|
|
38
47
|
}
|
|
48
|
+
// css is Tungsten's raw-string form of style, not an HTML attribute.
|
|
49
|
+
// It cannot go in omitProps, which is applied before it is read here.
|
|
50
|
+
delete props.css;
|
|
39
51
|
}
|
|
40
52
|
//delete props.bind
|
|
41
53
|
return props;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processProps.sxyt.d.ts","sourceRoot":"","sources":["../../src/render/processProps.sxyt.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe } from 'sxy-test-runner';
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { processProps } from "./processProps.js";
|
|
4
|
+
import { htmlEvents } from "../htmlEvents.js";
|
|
5
|
+
describe('processProps()', ({
|
|
6
|
+
it
|
|
7
|
+
}) => {
|
|
8
|
+
it('keeps ordinary attributes', () => {
|
|
9
|
+
expect(processProps({
|
|
10
|
+
name: 'a',
|
|
11
|
+
title: 't',
|
|
12
|
+
'data-x': '1'
|
|
13
|
+
})).to.deep.equal({
|
|
14
|
+
name: 'a',
|
|
15
|
+
title: 't',
|
|
16
|
+
'data-x': '1'
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
// The list of handlers used to be written out by hand and missed most of
|
|
20
|
+
// them, so anything not named leaked into the markup as an attribute
|
|
21
|
+
// holding a stringified function.
|
|
22
|
+
it('strips every event handler Tungsten recognises, not a hand-picked few', () => {
|
|
23
|
+
const handlers = Object.fromEntries(Object.keys(htmlEvents).map(name => [name, () => undefined]));
|
|
24
|
+
expect(processProps({
|
|
25
|
+
...handlers,
|
|
26
|
+
name: 'a'
|
|
27
|
+
})).to.deep.equal({
|
|
28
|
+
name: 'a'
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
it('strips the handlers that were missing from the old list', () => {
|
|
32
|
+
const kept = processProps({
|
|
33
|
+
onInput: () => undefined,
|
|
34
|
+
onFocus: () => undefined,
|
|
35
|
+
onBlur: () => undefined,
|
|
36
|
+
onScroll: () => undefined,
|
|
37
|
+
onPaste: () => undefined,
|
|
38
|
+
name: 'a'
|
|
39
|
+
});
|
|
40
|
+
expect(Object.keys(kept)).to.deep.equal(['name']);
|
|
41
|
+
});
|
|
42
|
+
it('strips props that are instructions to Tungsten rather than attributes', () => {
|
|
43
|
+
const kept = processProps({
|
|
44
|
+
ref: {
|
|
45
|
+
current: null
|
|
46
|
+
},
|
|
47
|
+
__zoneHtml: '<b>x</b>',
|
|
48
|
+
__opaque: true,
|
|
49
|
+
dangerouslySetInnerHTML: {
|
|
50
|
+
__html: '<b>x</b>'
|
|
51
|
+
},
|
|
52
|
+
name: 'a'
|
|
53
|
+
});
|
|
54
|
+
expect(Object.keys(kept)).to.deep.equal(['name']);
|
|
55
|
+
});
|
|
56
|
+
it('renames className to class', () => {
|
|
57
|
+
expect(processProps({
|
|
58
|
+
className: 'a b'
|
|
59
|
+
})).to.deep.equal({
|
|
60
|
+
class: 'a b'
|
|
61
|
+
});
|
|
62
|
+
// a raw class prop is dropped; className is the one that reaches HTML
|
|
63
|
+
expect(processProps({
|
|
64
|
+
class: 'ignored'
|
|
65
|
+
})).to.deep.equal({});
|
|
66
|
+
});
|
|
67
|
+
it('turns a style object into a css string', () => {
|
|
68
|
+
expect(processProps({
|
|
69
|
+
style: {
|
|
70
|
+
backgroundColor: 'red'
|
|
71
|
+
}
|
|
72
|
+
}).style).to.equal('background-color: red;');
|
|
73
|
+
});
|
|
74
|
+
it('folds css into style and does not leave css behind as an attribute', () => {
|
|
75
|
+
const kept = processProps({
|
|
76
|
+
css: 'color: red'
|
|
77
|
+
});
|
|
78
|
+
expect(kept.style).to.equal('color: red');
|
|
79
|
+
expect('css' in kept).to.equal(false);
|
|
80
|
+
});
|
|
81
|
+
it('does not mutate the props it is given', () => {
|
|
82
|
+
const props = {
|
|
83
|
+
className: 'a',
|
|
84
|
+
onClick: () => undefined,
|
|
85
|
+
name: 'n'
|
|
86
|
+
};
|
|
87
|
+
processProps(props);
|
|
88
|
+
expect(Object.keys(props).sort()).to.deep.equal(['className', 'name', 'onClick']);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderingTreeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/renderingTreeToHtml.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAY7C,OAAO,EACH,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAG7C,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"renderingTreeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/renderingTreeToHtml.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAY7C,OAAO,EACH,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAG7C,MAAM,qBAAqB,CAAA;AAU5B,wBAAsB,mBAAmB,CACrC,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAC1D,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACrC,OAAO,CAAC,MAAM,CAAC,CAwJjB"}
|
|
@@ -13,6 +13,7 @@ import { printComponentNamesInSource } from "./printComponentNameInSource.js";
|
|
|
13
13
|
import { RenderingNodeElementIsComponent, RenderingNodeElementIsHtml } from "../RenderingNode.js";
|
|
14
14
|
import { processHead } from "./processHead.js";
|
|
15
15
|
import { processRenderingNodeHtml } from "./processRenderingNodeHtml.js";
|
|
16
|
+
import { innerHtmlFromProps } from "../innerHtmlFromProps.js";
|
|
16
17
|
//// Maybe we should build the full tree before rendering
|
|
17
18
|
//// may need some merging with componentTreeToHtml
|
|
18
19
|
export async function renderingNodeToHtml(node, noEscape, headData, lastSelect) {
|
|
@@ -93,6 +94,13 @@ export async function renderingNodeToHtml(node, noEscape, headData, lastSelect)
|
|
|
93
94
|
...attribProps
|
|
94
95
|
} = node[$props]);
|
|
95
96
|
debug('validChildren(children)', validRenderingChildren(children));
|
|
97
|
+
const innerHtml = innerHtmlFromProps(type, node[$props], validRenderingChildren(children));
|
|
98
|
+
if (innerHtml !== null) {
|
|
99
|
+
if (type in htmlVoidTags) {
|
|
100
|
+
throw new TungstenError(`nodeToHtml() error: node type ${type} is an HTML void type` + ` (cannot have children), but the node has dangerouslySetInnerHTML.`);
|
|
101
|
+
}
|
|
102
|
+
return html + `<${type}${attribs}>${innerHtml}</${type}>`;
|
|
103
|
+
}
|
|
96
104
|
if (validRenderingChildren(children)) {
|
|
97
105
|
if (type in htmlVoidTags) {
|
|
98
106
|
throw new TungstenError(`nodeToHtml() error: node ${JSON.stringify(node)} type ${type} is an HTML void type` + ` (cannot have children), but the node has children.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Routes } from './Routes.js';
|
|
2
2
|
export type Path = `/${string}`;
|
|
3
|
-
export type RouteKey = Path | 'default' | '404';
|
|
3
|
+
export type RouteKey = Path | 'default' | '404' | 'error';
|
|
4
4
|
export declare function StringIsValidRouteKey(string: string, routes: Routes): string is RouteKey;
|
|
5
5
|
export declare function StringIsPath(string: string): string is Path;
|
|
6
6
|
export declare function RouteKeyIsPath(path: RouteKey): path is Path;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteTypes.d.ts","sourceRoot":"","sources":["../../src/routing/RouteTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,IAAI,GAAG,IAAI,MAAM,EAAE,CAAA;AAC/B,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"RouteTypes.d.ts","sourceRoot":"","sources":["../../src/routing/RouteTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,IAAI,GAAG,IAAI,MAAM,EAAE,CAAA;AAC/B,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAA;AACzD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,QAAQ,CAExF;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,IAAI,CAE3D;AACD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,IAAI,IAAI,CAE3D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const $notFound: unique symbol;
|
|
2
|
+
export type $NotFound = typeof $notFound;
|
|
3
|
+
export declare const $404: unique symbol;
|
|
4
|
+
export type $404 = typeof $404;
|
|
5
|
+
export declare const $default: unique symbol;
|
|
6
|
+
export type $Default = typeof $default;
|
|
7
|
+
export declare const $error: unique symbol;
|
|
8
|
+
export type $Error = typeof $error;
|
|
9
|
+
//# sourceMappingURL=routeSymbols.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routeSymbols.d.ts","sourceRoot":"","sources":["../../src/routing/routeSymbols.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,SAAS,eAAqB,CAAA;AAC3C,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAA;AAExC,eAAO,MAAM,IAAI,eAAgB,CAAA;AACjC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAA;AAE9B,eAAO,MAAM,QAAQ,eAAoB,CAAA;AACzC,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAA;AAEtC,eAAO,MAAM,MAAM,eAAkB,CAAA;AACrC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrateUpdateVdom.d.ts","sourceRoot":"","sources":["../../src/update/integrateUpdateVdom.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EACH,WAAW,EAAwC,eAAe,EAElE,uBAAuB,EAG1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAKlD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAA;AAE1D,OAAO,EACe,mBAAmB,EAIT,4BAA4B,EAI3D,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"integrateUpdateVdom.d.ts","sourceRoot":"","sources":["../../src/update/integrateUpdateVdom.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EACH,WAAW,EAAwC,eAAe,EAElE,uBAAuB,EAG1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAKlD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,OAAO,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAA;AAE1D,OAAO,EACe,mBAAmB,EAIT,4BAA4B,EAI3D,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAYhD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAGlE,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG;IAC5C,wBAAwB,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,KAAK,GAAG,CAAA;IACpE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACzB,CAAA;AAYD,wBAAsB,0BAA0B,CAC5C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,GAC/C,OAAO,CAAC,YAAY,CAAC,CA0GvB;AA8gBD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,mBAAmB,QAuC5E;AAgED,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,QAkK7E;AA0WD,KAAK,2BAA2B,GAAG;IAC/B,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,uBAAuB,CAAC;IAE9B,UAAU,EAAE,UAAU,CAAA;CACzB,CAAA;AACD,wBAAsB,uBAAuB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,2BAA2B,GAC3G,OAAO,CAAC,4BAA4B,CAAC,CAU1C;AAED,MAAM,MAAM,mBAAmB,CAC3B,aAAa,SAAS,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,IACzD;IACA,SAAS,EAAE,YAAY,CAAC,aAAa,CAAC,CAAA;IACtC,cAAc,EAAE,OAAO,CAAA;IACvB,cAAc,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,wBAAsB,2BAA2B,CAC7C,aAAa,SAAS,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,EAEzD,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,EAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC/C,SAAS,EAAE,OAAO,EAClB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,WAAW,GACvB,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAgD7C;AAED,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,mBAAmB,CAAC,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/D,IAAI,CAON"}
|
|
@@ -17,6 +17,8 @@ import { $key, $props, $routeData, $routeParams, $type } from "../contentNodeSym
|
|
|
17
17
|
import { printComponentNamesInSource } from "../render/printComponentNameInSource.js";
|
|
18
18
|
import { bindableHtmlElementsNumber, bindableHtmlElementsString } from "../components/bindable/bindableHtmlElements.js";
|
|
19
19
|
import { checkedValueToBoolean } from "../checkedValueToBoolean.js";
|
|
20
|
+
import { innerHtmlFromProps } from "../innerHtmlFromProps.js";
|
|
21
|
+
import { validChildren } from "../validChildren.js";
|
|
20
22
|
import { setCurrentComponent } from 'tungsten/hooks/setCurrentComponent.js';
|
|
21
23
|
import { clearCurrentComponent } from 'tungsten/hooks/clearCurrentComponent.js';
|
|
22
24
|
import { isDev } from "../isDev.js";
|
|
@@ -401,6 +403,15 @@ async function updateHtmlElement(tungstenElement, instance, newNode, oldNode, do
|
|
|
401
403
|
//console.log(newNode[$type], 'children', newChildren)
|
|
402
404
|
const oldChildren = oldNode.renderedChildren;
|
|
403
405
|
//console.log('oldChildren', oldChildren)
|
|
406
|
+
const newInnerHtml = innerHtmlFromProps(newNode[$type], newNode[$props], validChildren(newChildren));
|
|
407
|
+
if (newInnerHtml !== null) {
|
|
408
|
+
// Replacing the markup wholesale is the only option: Tungsten holds the
|
|
409
|
+
// subtree as one opaque string and cannot diff it. Guarded so an update
|
|
410
|
+
// for an unrelated reason does not discard DOM state under the host.
|
|
411
|
+
const oldInnerHtml = innerHtmlFromProps(oldNode[$type], oldNode[$props], false);
|
|
412
|
+
if (newInnerHtml !== oldInnerHtml) domNode.innerHTML = newInnerHtml;
|
|
413
|
+
return makeRenderedContentNodeHtml(newNode, domNode, oldChildren);
|
|
414
|
+
}
|
|
404
415
|
const renderedChildren = newNode[$props].__opaque === true ? oldChildren : await applyUpdate({
|
|
405
416
|
tungstenElement: tungstenElement,
|
|
406
417
|
instance,
|
|
@@ -783,13 +794,22 @@ async function createHtmlElement(tungstenElement, instance, node, domPointer, pa
|
|
|
783
794
|
//!!!! node.domElement = el
|
|
784
795
|
if (IsHTMLElement(domPointer.parent)) debug('***', node[$type], 'html created', htmlEl);
|
|
785
796
|
// children
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
797
|
+
const innerHtml = innerHtmlFromProps(node[$type], node[$props], validChildren(node[$props].children));
|
|
798
|
+
let renderedChildren;
|
|
799
|
+
if (innerHtml !== null) {
|
|
800
|
+
// The raw markup owns the whole subtree, so there is nothing to create
|
|
801
|
+
// and nothing to record as rendered children.
|
|
802
|
+
htmlEl.innerHTML = innerHtml;
|
|
803
|
+
renderedChildren = makeRenderedContentNodeNonRendered();
|
|
804
|
+
} else {
|
|
805
|
+
renderedChildren = await createNode({
|
|
806
|
+
tungstenElement: tungstenElement,
|
|
807
|
+
instance,
|
|
808
|
+
newNode: node[$props].children,
|
|
809
|
+
domPointer: new DomPointer(htmlEl, htmlEl.childNodes.length),
|
|
810
|
+
parentNode: node
|
|
811
|
+
});
|
|
812
|
+
}
|
|
793
813
|
if (node[$type] === 'select') {
|
|
794
814
|
if (node[$props].value !== undefined && node[$props].value !== null) {
|
|
795
815
|
htmlEl.value = String(node[$props].value);
|
|
@@ -930,6 +950,7 @@ function processAttributes(node) {
|
|
|
930
950
|
delete attributes.ref;
|
|
931
951
|
delete attributes.__zoneHtml;
|
|
932
952
|
delete attributes.__opaque;
|
|
953
|
+
delete attributes.dangerouslySetInnerHTML;
|
|
933
954
|
delete attributes.class;
|
|
934
955
|
if (attributes.className !== undefined) attributes.class = attributes.className;
|
|
935
956
|
delete attributes.className;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tungsten",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -134,8 +134,8 @@
|
|
|
134
134
|
"scripts": {
|
|
135
135
|
"__build": "pnpm clean && pnpm build-prep && pnpm build-watch",
|
|
136
136
|
"build": "run-s -c clean build-prep build-watch",
|
|
137
|
-
"clean": "rimraf intermed
|
|
138
|
-
"build-once": "rimraf intermed
|
|
137
|
+
"clean": "rimraf intermed dist",
|
|
138
|
+
"build-once": "rimraf intermed dist && pnpm build-ts-once && pnpm build-babel-once",
|
|
139
139
|
"build-quick": "pnpm build-watch",
|
|
140
140
|
"build-prep": "pnpm build-ts-once",
|
|
141
141
|
"build-watch": "run-p build-ts build-babel",
|