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.
Files changed (87) hide show
  1. package/dist/ContentNode.d.ts +7 -0
  2. package/dist/ContentNode.d.ts.map +1 -1
  3. package/dist/Instance/Instance.d.ts.map +1 -1
  4. package/dist/Instance/Instance.js +7 -5
  5. package/dist/Instance/gotoRedirect.sxyt.d.ts +2 -0
  6. package/dist/Instance/gotoRedirect.sxyt.d.ts.map +1 -0
  7. package/dist/Instance/gotoRedirect.sxyt.js +148 -0
  8. package/dist/Servers/DevServer.d.ts +7 -0
  9. package/dist/Servers/DevServer.d.ts.map +1 -1
  10. package/dist/Servers/DevServer.js +38 -1
  11. package/dist/Servers/routing.d.ts.map +1 -1
  12. package/dist/Servers/routing.js +10 -1
  13. package/dist/build/compileJavascript.d.ts.map +1 -1
  14. package/dist/build/compileJavascript.js +86 -1
  15. package/dist/build/compileStyles.d.ts +1 -1
  16. package/dist/build/compileStyles.d.ts.map +1 -1
  17. package/dist/build/compileStyles.js +10 -4
  18. package/dist/build/compileUserApp.d.ts.map +1 -1
  19. package/dist/build/compileUserApp.js +7 -2
  20. package/dist/components/RawHtml.d.ts +36 -0
  21. package/dist/components/RawHtml.d.ts.map +1 -0
  22. package/dist/components/RawHtml.js +30 -0
  23. package/dist/components/RawHtml.sxyt.d.ts +2 -0
  24. package/dist/components/RawHtml.sxyt.d.ts.map +1 -0
  25. package/dist/components/RawHtml.sxyt.js +104 -0
  26. package/dist/dev/liveReloadHub.d.ts +6 -0
  27. package/dist/dev/liveReloadHub.d.ts.map +1 -0
  28. package/dist/dev/liveReloadHub.js +12 -0
  29. package/dist/helpers/getFetchCompatibleHeaders.d.ts.map +1 -1
  30. package/dist/hydrate/hydrate.d.ts.map +1 -1
  31. package/dist/hydrate/hydrate.js +11 -4
  32. package/dist/hydrate/reconcileHydratedAttributes.d.ts +24 -0
  33. package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +1 -0
  34. package/dist/hydrate/reconcileHydratedAttributes.js +73 -0
  35. package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts +2 -0
  36. package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts.map +1 -0
  37. package/dist/hydrate/reconcileHydratedAttributes.sxyt.js +173 -0
  38. package/dist/indexInner.d.ts +4 -0
  39. package/dist/indexInner.d.ts.map +1 -1
  40. package/dist/indexInner.js +3 -0
  41. package/dist/innerHtmlFromProps.d.ts +16 -0
  42. package/dist/innerHtmlFromProps.d.ts.map +1 -0
  43. package/dist/innerHtmlFromProps.js +31 -0
  44. package/dist/innerHtmlFromProps.sxyt.d.ts +2 -0
  45. package/dist/innerHtmlFromProps.sxyt.d.ts.map +1 -0
  46. package/dist/innerHtmlFromProps.sxyt.js +99 -0
  47. package/dist/postcssPlugins/TransformCssNames.d.ts +14 -1
  48. package/dist/postcssPlugins/TransformCssNames.d.ts.map +1 -1
  49. package/dist/postcssPlugins/TransformCssNames.js +97 -95
  50. package/dist/rawHtml/HtmlAllowList.d.ts +17 -0
  51. package/dist/rawHtml/HtmlAllowList.d.ts.map +1 -0
  52. package/dist/rawHtml/HtmlAllowList.js +1 -0
  53. package/dist/rawHtml/decodeHtmlEntities.d.ts +8 -0
  54. package/dist/rawHtml/decodeHtmlEntities.d.ts.map +1 -0
  55. package/dist/rawHtml/decodeHtmlEntities.js +105 -0
  56. package/dist/rawHtml/htmlParsingTags.d.ts +23 -0
  57. package/dist/rawHtml/htmlParsingTags.d.ts.map +1 -0
  58. package/dist/rawHtml/htmlParsingTags.js +55 -0
  59. package/dist/rawHtml/parseHtml.d.ts +35 -0
  60. package/dist/rawHtml/parseHtml.d.ts.map +1 -0
  61. package/dist/rawHtml/parseHtml.js +214 -0
  62. package/dist/rawHtml/parseHtml.sxyt.d.ts +2 -0
  63. package/dist/rawHtml/parseHtml.sxyt.d.ts.map +1 -0
  64. package/dist/rawHtml/parseHtml.sxyt.js +239 -0
  65. package/dist/rawHtml/safeHtmlAllowList.d.ts +18 -0
  66. package/dist/rawHtml/safeHtmlAllowList.d.ts.map +1 -0
  67. package/dist/rawHtml/safeHtmlAllowList.js +47 -0
  68. package/dist/rawHtml/tokenizeHtml.d.ts +25 -0
  69. package/dist/rawHtml/tokenizeHtml.d.ts.map +1 -0
  70. package/dist/rawHtml/tokenizeHtml.js +148 -0
  71. package/dist/render/processHead.d.ts.map +1 -1
  72. package/dist/render/processHead.js +10 -0
  73. package/dist/render/processProps.d.ts.map +1 -1
  74. package/dist/render/processProps.js +14 -2
  75. package/dist/render/processProps.sxyt.d.ts +2 -0
  76. package/dist/render/processProps.sxyt.d.ts.map +1 -0
  77. package/dist/render/processProps.sxyt.js +90 -0
  78. package/dist/render/renderingTreeToHtml.d.ts.map +1 -1
  79. package/dist/render/renderingTreeToHtml.js +8 -0
  80. package/dist/routing/RouteTypes.d.ts +1 -1
  81. package/dist/routing/RouteTypes.d.ts.map +1 -1
  82. package/dist/routing/routeSymbols.d.ts +9 -0
  83. package/dist/routing/routeSymbols.d.ts.map +1 -0
  84. package/dist/routing/routeSymbols.js +4 -0
  85. package/dist/update/integrateUpdateVdom.d.ts.map +1 -1
  86. package/dist/update/integrateUpdateVdom.js +28 -7
  87. 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,QAoGrB"}
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;AAuBjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,6BAA6B,iCA2ChE"}
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
- const omitProps = ['ref', 'onClick', 'onChange', 'onSubmit', 'onKeyDown', 'onKeyUp', 'onKeyPress', 'onMouseDown', 'onMouseUp', 'onMouseMove', 'onMouseEnter', 'onMouseLeave', 'onMouseOver', 'onMouseOut', '__zoneHtml', '__opaque'];
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=processProps.sxyt.d.ts.map
@@ -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;AAS5B,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,CA4IjB"}
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;AAC/C,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"}
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"}
@@ -0,0 +1,4 @@
1
+ export const $notFound = Symbol('notFound');
2
+ export const $404 = Symbol('404');
3
+ export const $default = Symbol('default');
4
+ export const $error = Symbol('error');
@@ -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;AAUhD,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;AA+fD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,mBAAmB,QAuC5E;AAgED,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,QAkK7E;AA6VD,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"}
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 renderedChildren = await createNode({
787
- tungstenElement: tungstenElement,
788
- instance,
789
- newNode: node[$props].children,
790
- domPointer: new DomPointer(htmlEl, htmlEl.childNodes.length),
791
- parentNode: node
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.2.24",
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 && rimraf dist",
138
- "build-once": "rimraf intermed && rimraf dist && pnpm build-ts-once && pnpm build-babel-once",
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",