tungsten 0.3.2 → 0.3.3
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/.build-complete +1 -0
- package/dist/Framework.d.ts +2 -0
- package/dist/Framework.d.ts.map +1 -1
- package/dist/Framework.js +68 -57
- package/dist/FrameworkConfig.d.ts +2 -0
- package/dist/FrameworkConfig.d.ts.map +1 -1
- package/dist/Instance/Instance.d.ts.map +1 -1
- package/dist/Instance/Instance.js +18 -1
- package/dist/Instance/InstanceConfig.d.ts +1 -0
- package/dist/Instance/InstanceConfig.d.ts.map +1 -1
- package/dist/PopMessage.type.d.ts +6 -0
- package/dist/PopMessage.type.d.ts.map +1 -0
- package/dist/PopMessage.type.js +1 -0
- package/dist/Servers/DevServer.d.ts +2 -0
- package/dist/Servers/DevServer.d.ts.map +1 -1
- package/dist/Servers/DevServer.js +84 -8
- package/dist/Servers/MatchedRouteInfo.d.ts +1 -1
- package/dist/Servers/MatchedRouteInfo.d.ts.map +1 -1
- package/dist/Servers/ProdServer.d.ts +1 -0
- package/dist/Servers/ProdServer.d.ts.map +1 -1
- package/dist/Servers/ProdServer.js +79 -5
- package/dist/Servers/hono/DevServerHono.js +25 -29
- package/dist/Servers/hono/ProdServerHono.js +49 -142
- package/dist/Servers/hono/TungstenServerHono.d.ts +1 -1
- package/dist/Servers/hono/TungstenServerHono.d.ts.map +1 -1
- package/dist/Servers/hono/TungstenServerHono.js +12 -29
- package/dist/Servers/routing.d.ts.map +1 -1
- package/dist/Servers/routing.js +10 -6
- package/dist/Servers/tungstenCorePrivateEnvSchema.d.ts +0 -3
- package/dist/Servers/tungstenCorePrivateEnvSchema.d.ts.map +1 -1
- package/dist/Servers/tungstenCorePrivateEnvSchema.js +3 -3
- package/dist/Servers/tungstenCorePublicEnvSchema.d.ts +8 -0
- package/dist/Servers/tungstenCorePublicEnvSchema.d.ts.map +1 -0
- package/dist/Servers/tungstenCorePublicEnvSchema.js +14 -0
- package/dist/Store/InternalStoreSchema.d.ts +2 -0
- package/dist/Store/InternalStoreSchema.d.ts.map +1 -1
- package/dist/Store/InternalStoreSchema.js +1 -0
- package/dist/api/Request.d.ts +1 -1
- package/dist/api/Request.d.ts.map +1 -1
- package/dist/api/Response.d.ts +1 -1
- package/dist/api/Response.d.ts.map +1 -1
- package/dist/babelPlugins/BabelTypes.d.ts +2 -2
- package/dist/babelPlugins/BabelTypes.d.ts.map +1 -1
- package/dist/babelPlugins/BabelTypes.js +0 -1
- package/dist/bin/commands/build.d.ts.map +1 -1
- package/dist/bin/commands/build.js +3 -0
- package/dist/bin/commands/dev.d.ts.map +1 -1
- package/dist/bin/commands/dev.js +15 -4
- package/dist/bin/commands/devmon.d.ts +2 -0
- package/dist/bin/commands/devmon.d.ts.map +1 -0
- package/dist/bin/commands/devmon.js +66 -0
- package/dist/bin/index.d.ts.map +1 -1
- package/dist/bin/index.js +6 -0
- package/dist/booleanAttributeValue.d.ts +21 -0
- package/dist/booleanAttributeValue.d.ts.map +1 -0
- package/dist/booleanAttributeValue.js +23 -0
- package/dist/build/compileJavascript.js +1 -1
- package/dist/build/compileUserApp.d.ts +2 -1
- package/dist/build/compileUserApp.d.ts.map +1 -1
- package/dist/build/compileUserApp.js +8 -7
- package/dist/build/createInstanceConfigFile.js +1 -1
- package/dist/compat/types.d.ts +1 -1
- package/dist/compat/types.d.ts.map +1 -1
- package/dist/components/PopMessages.d.ts +12 -0
- package/dist/components/PopMessages.d.ts.map +1 -0
- package/dist/components/PopMessages.js +45 -0
- package/dist/defaultConfig.d.ts +1 -0
- package/dist/defaultConfig.d.ts.map +1 -1
- package/dist/defaultConfig.js +2 -1
- package/dist/env/Interfaces.d.ts +2 -1
- package/dist/env/Interfaces.d.ts.map +1 -1
- package/dist/env/getEnvSchemas.d.ts +1 -1
- package/dist/env/getEnvSchemas.d.ts.map +1 -1
- package/dist/env/publicEnv.d.ts.map +1 -1
- package/dist/env/publicEnv.js +4 -1
- package/dist/frameworkConfigSchema.d.ts +4 -0
- package/dist/frameworkConfigSchema.d.ts.map +1 -1
- package/dist/frameworkConfigSchema.js +2 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useInstance.d.ts +3 -0
- package/dist/hooks/useInstance.d.ts.map +1 -0
- package/dist/hooks/useInstance.js +4 -0
- package/dist/hydrate/hydrate.d.ts +6 -4
- package/dist/hydrate/hydrate.d.ts.map +1 -1
- package/dist/hydrate/hydrate.js +51 -13
- package/dist/hydrate/reconcileHydratedAttributes.d.ts.map +1 -1
- package/dist/hydrate/reconcileHydratedAttributes.js +4 -3
- package/dist/hydrate/renderHydrationMismatchBanner.d.ts +7 -0
- package/dist/hydrate/renderHydrationMismatchBanner.d.ts.map +1 -0
- package/dist/hydrate/renderHydrationMismatchBanner.js +69 -0
- package/dist/indexInner.d.ts +8 -0
- package/dist/indexInner.d.ts.map +1 -1
- package/dist/indexInner.js +7 -1
- package/dist/makeInstanceConfigFromFrameworkConfig.d.ts.map +1 -1
- package/dist/makeInstanceConfigFromFrameworkConfig.js +2 -1
- package/dist/pop.d.ts +10 -0
- package/dist/pop.d.ts.map +1 -0
- package/dist/pop.js +49 -0
- package/dist/rawHtml/filterContentNode.d.ts +20 -0
- package/dist/rawHtml/filterContentNode.d.ts.map +1 -0
- package/dist/rawHtml/filterContentNode.js +96 -0
- package/dist/rawHtml/parseHtml.d.ts +14 -0
- package/dist/rawHtml/parseHtml.d.ts.map +1 -1
- package/dist/rawHtml/parseHtml.js +4 -4
- package/dist/rawHtml/sanitizeHtml.d.ts +17 -0
- package/dist/rawHtml/sanitizeHtml.d.ts.map +1 -0
- package/dist/rawHtml/sanitizeHtml.js +19 -0
- package/dist/render/HeadData.d.ts +1 -0
- package/dist/render/HeadData.d.ts.map +1 -1
- package/dist/render/contentNodeToHtml.d.ts +18 -0
- package/dist/render/contentNodeToHtml.d.ts.map +1 -0
- package/dist/render/contentNodeToHtml.js +66 -0
- package/dist/render/contentToHtml.d.ts.map +1 -1
- package/dist/render/contentToHtml.js +2 -1
- package/dist/render/processHead.d.ts.map +1 -1
- package/dist/render/processHead.js +8 -2
- package/dist/render/renderingTreeToHtml.d.ts.map +1 -1
- package/dist/render/renderingTreeToHtml.js +8 -1
- package/dist/routing/ErrorRouteType.d.ts +6 -0
- package/dist/routing/ErrorRouteType.d.ts.map +1 -0
- package/dist/routing/ErrorRouteType.js +1 -0
- package/dist/routing/RouteTypes.d.ts +2 -1
- package/dist/routing/RouteTypes.d.ts.map +1 -1
- package/dist/routing/Routes.d.ts +5 -0
- package/dist/routing/Routes.d.ts.map +1 -1
- package/dist/routing/TungstenErrorInfo.d.ts +6 -0
- package/dist/routing/TungstenErrorInfo.d.ts.map +1 -0
- package/dist/routing/TungstenErrorInfo.js +6 -0
- package/dist/style/styleContentSxy.js +21 -0
- package/dist/testing/hydrateTestApp.d.ts +38 -0
- package/dist/testing/hydrateTestApp.d.ts.map +1 -0
- package/dist/testing/hydrateTestApp.js +242 -0
- package/dist/testing/index.d.ts +4 -3
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +16 -2
- package/dist/timeProfiler.d.ts +6 -0
- package/dist/timeProfiler.d.ts.map +1 -0
- package/dist/timeProfiler.js +31 -0
- package/dist/update/integrateUpdateVdom.d.ts +11 -0
- package/dist/update/integrateUpdateVdom.d.ts.map +1 -1
- package/dist/update/integrateUpdateVdom.js +9 -3
- package/dist/warnOnFrameworkChange.d.ts.map +1 -1
- package/dist/warnOnFrameworkChange.js +8 -8
- package/package.json +18 -16
- package/dist/FunctionComponentStandIn.sxyt.d.ts +0 -2
- package/dist/FunctionComponentStandIn.sxyt.d.ts.map +0 -1
- package/dist/FunctionComponentStandIn.sxyt.js +0 -52
- package/dist/Instance/gotoRedirect.sxyt.d.ts +0 -2
- package/dist/Instance/gotoRedirect.sxyt.d.ts.map +0 -1
- package/dist/Instance/gotoRedirect.sxyt.js +0 -148
- package/dist/Instance/runTrackedWork.sxyt.d.ts +0 -2
- package/dist/Instance/runTrackedWork.sxyt.d.ts.map +0 -1
- package/dist/Instance/runTrackedWork.sxyt.js +0 -35
- package/dist/Servers/devErrorPageAutoRetry.sxyt.d.ts +0 -2
- package/dist/Servers/devErrorPageAutoRetry.sxyt.d.ts.map +0 -1
- package/dist/Servers/devErrorPageAutoRetry.sxyt.js +0 -117
- package/dist/_client/cjsHelpers.sxyt.d.ts +0 -2
- package/dist/_client/cjsHelpers.sxyt.d.ts.map +0 -1
- package/dist/_client/cjsHelpers.sxyt.js +0 -45
- package/dist/auth/passwordHashing.sxyt.d.ts +0 -2
- package/dist/auth/passwordHashing.sxyt.d.ts.map +0 -1
- package/dist/auth/passwordHashing.sxyt.js +0 -46
- package/dist/auth/ref/checkPasswordResetToken.sxyt.d.ts +0 -2
- package/dist/auth/ref/checkPasswordResetToken.sxyt.d.ts.map +0 -1
- package/dist/auth/ref/checkPasswordResetToken.sxyt.js +0 -40
- package/dist/auth/ref/createPasswordResetToken.sxyt.d.ts +0 -2
- package/dist/auth/ref/createPasswordResetToken.sxyt.d.ts.map +0 -1
- package/dist/auth/ref/createPasswordResetToken.sxyt.js +0 -10
- package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.d.ts +0 -2
- package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.d.ts.map +0 -1
- package/dist/babelPlugins/BabelAddDevCacheBust.sxyt.js +0 -138
- package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.d.ts +0 -2
- package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.d.ts.map +0 -1
- package/dist/babelPlugins/fixImportsInSassErrorMessage.sxyt.js +0 -108
- package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.d.ts +0 -2
- package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.d.ts.map +0 -1
- package/dist/babelPlugins/transformPackageImports/resolveBareImport.sxyt.js +0 -104
- package/dist/bin/commands/compile.sxyt.d.ts +0 -2
- package/dist/bin/commands/compile.sxyt.d.ts.map +0 -1
- package/dist/bin/commands/compile.sxyt.js +0 -255
- package/dist/build/modifySassImports.sxyt.d.ts +0 -2
- package/dist/build/modifySassImports.sxyt.d.ts.map +0 -1
- package/dist/build/modifySassImports.sxyt.js +0 -55
- package/dist/build/sassImportParser.sxyt.d.ts +0 -2
- package/dist/build/sassImportParser.sxyt.d.ts.map +0 -1
- package/dist/build/sassImportParser.sxyt.js +0 -110
- package/dist/build/scssImportParserChatgpt.sxyt.d.ts +0 -2
- package/dist/build/scssImportParserChatgpt.sxyt.d.ts.map +0 -1
- package/dist/build/scssImportParserChatgpt.sxyt.js +0 -120
- package/dist/clone.sxyt.d.ts +0 -2
- package/dist/clone.sxyt.d.ts.map +0 -1
- package/dist/clone.sxyt.js +0 -24
- package/dist/componentProxyHandlers.sxyt.d.ts +0 -2
- package/dist/componentProxyHandlers.sxyt.d.ts.map +0 -1
- package/dist/componentProxyHandlers.sxyt.js +0 -130
- package/dist/components/RawHtml.sxyt.d.ts +0 -2
- package/dist/components/RawHtml.sxyt.d.ts.map +0 -1
- package/dist/components/RawHtml.sxyt.js +0 -104
- package/dist/components/bindable/BindableSelect.sxyt.d.ts +0 -2
- package/dist/components/bindable/BindableSelect.sxyt.d.ts.map +0 -1
- package/dist/components/bindable/BindableSelect.sxyt.js +0 -186
- package/dist/env/injectEnv.sxyt.d.ts +0 -2
- package/dist/env/injectEnv.sxyt.d.ts.map +0 -1
- package/dist/env/injectEnv.sxyt.js +0 -20
- package/dist/env/parseEnv.sxyt.d.ts +0 -2
- package/dist/env/parseEnv.sxyt.d.ts.map +0 -1
- package/dist/env/parseEnv.sxyt.js +0 -11
- package/dist/findProjectDir.sxyt.d.ts +0 -2
- package/dist/findProjectDir.sxyt.d.ts.map +0 -1
- package/dist/findProjectDir.sxyt.js +0 -82
- package/dist/hmr/applyHmrModule.sxyt.d.ts +0 -2
- package/dist/hmr/applyHmrModule.sxyt.d.ts.map +0 -1
- package/dist/hmr/applyHmrModule.sxyt.js +0 -182
- package/dist/hmr/instanceRegistry.sxyt.d.ts +0 -2
- package/dist/hmr/instanceRegistry.sxyt.d.ts.map +0 -1
- package/dist/hmr/instanceRegistry.sxyt.js +0 -24
- package/dist/hmr/refreshStylesheets.sxyt.d.ts +0 -2
- package/dist/hmr/refreshStylesheets.sxyt.d.ts.map +0 -1
- package/dist/hmr/refreshStylesheets.sxyt.js +0 -46
- package/dist/hydrate/hydrate.sxyt.d.ts +0 -2
- package/dist/hydrate/hydrate.sxyt.d.ts.map +0 -1
- package/dist/hydrate/hydrate.sxyt.js +0 -302
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts +0 -2
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.d.ts.map +0 -1
- package/dist/hydrate/reconcileHydratedAttributes.sxyt.js +0 -190
- package/dist/importDevModuleWithRetry.sxyt.d.ts +0 -2
- package/dist/importDevModuleWithRetry.sxyt.d.ts.map +0 -1
- package/dist/importDevModuleWithRetry.sxyt.js +0 -104
- package/dist/innerHtmlFromProps.sxyt.d.ts +0 -2
- package/dist/innerHtmlFromProps.sxyt.d.ts.map +0 -1
- package/dist/innerHtmlFromProps.sxyt.js +0 -99
- package/dist/postcssPlugins/parseSelectors.sxyt.d.ts +0 -2
- package/dist/postcssPlugins/parseSelectors.sxyt.d.ts.map +0 -1
- package/dist/postcssPlugins/parseSelectors.sxyt.js +0 -62
- package/dist/postcssPlugins/parseSelectorsRich.sxyt.d.ts +0 -2
- package/dist/postcssPlugins/parseSelectorsRich.sxyt.d.ts.map +0 -1
- package/dist/postcssPlugins/parseSelectorsRich.sxyt.js +0 -155
- package/dist/rawHtml/parseHtml.sxyt.d.ts +0 -2
- package/dist/rawHtml/parseHtml.sxyt.d.ts.map +0 -1
- package/dist/rawHtml/parseHtml.sxyt.js +0 -239
- package/dist/render/getFunctionBodyAsString.sxyt.d.ts +0 -2
- package/dist/render/getFunctionBodyAsString.sxyt.d.ts.map +0 -1
- package/dist/render/getFunctionBodyAsString.sxyt.js +0 -37
- package/dist/render/processHead.sxyt.d.ts +0 -2
- package/dist/render/processHead.sxyt.d.ts.map +0 -1
- package/dist/render/processHead.sxyt.js +0 -93
- package/dist/render/processProps.sxyt.d.ts +0 -2
- package/dist/render/processProps.sxyt.d.ts.map +0 -1
- package/dist/render/processProps.sxyt.js +0 -90
- package/dist/routing/compareRequestToRegex.sxyt.d.ts +0 -2
- package/dist/routing/compareRequestToRegex.sxyt.d.ts.map +0 -1
- package/dist/routing/compareRequestToRegex.sxyt.js +0 -65
- package/dist/routing/getPathPriority.sxyt.d.ts +0 -2
- package/dist/routing/getPathPriority.sxyt.d.ts.map +0 -1
- package/dist/routing/getPathPriority.sxyt.js +0 -24
- package/dist/routing/matchPath.sxyt.d.ts +0 -2
- package/dist/routing/matchPath.sxyt.d.ts.map +0 -1
- package/dist/routing/matchPath.sxyt.js +0 -41
- package/dist/routing/patternToRegex.sxyt.d.ts +0 -2
- package/dist/routing/patternToRegex.sxyt.d.ts.map +0 -1
- package/dist/routing/patternToRegex.sxyt.js +0 -69
- package/dist/routing/splitRequest.sxyt.d.ts +0 -2
- package/dist/routing/splitRequest.sxyt.d.ts.map +0 -1
- package/dist/routing/splitRequest.sxyt.js +0 -47
- package/dist/style/jssToCss.sxyt.d.ts +0 -2
- package/dist/style/jssToCss.sxyt.d.ts.map +0 -1
- package/dist/style/jssToCss.sxyt.js +0 -17
- package/dist/testing/createTestApp.sxyt.d.ts +0 -2
- package/dist/testing/createTestApp.sxyt.d.ts.map +0 -1
- package/dist/testing/createTestApp.sxyt.js +0 -470
- package/dist/testing/ensureTestEnvLoaded.sxyt.d.ts +0 -2
- package/dist/testing/ensureTestEnvLoaded.sxyt.d.ts.map +0 -1
- package/dist/testing/ensureTestEnvLoaded.sxyt.js +0 -20
- package/dist/update/hotSwap.sxyt.d.ts +0 -2
- package/dist/update/hotSwap.sxyt.d.ts.map +0 -1
- package/dist/update/hotSwap.sxyt.js +0 -252
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ContentNodeIsComponent } from "../ContentNode.js";
|
|
2
|
+
import { $props, $type } from "../contentNodeSymbols.js";
|
|
3
|
+
import { StringIsHtmlEvent } from "../htmlEvents.js";
|
|
4
|
+
import { blockedUrl, makeAllowCheck, removalFor, urlAttributes } from "./parseHtml.js";
|
|
5
|
+
// The NodeProps equivalent of parseHtml's class->className, style->css
|
|
6
|
+
// rename: the allow list is written in terms of the HTML attribute name, but
|
|
7
|
+
// a content tree already carries Tungsten's prop names for these two.
|
|
8
|
+
const attributeCheckName = {
|
|
9
|
+
className: 'class',
|
|
10
|
+
css: 'style'
|
|
11
|
+
};
|
|
12
|
+
function filterProps(tag, props, allow) {
|
|
13
|
+
const filtered = {};
|
|
14
|
+
for (const key in props) {
|
|
15
|
+
if (key === 'children') continue;
|
|
16
|
+
const value = props[key];
|
|
17
|
+
// Tungsten reads these as event listeners, not attributes - same rule
|
|
18
|
+
// parseHtml applies to on* HTML attributes, expressed for prop names.
|
|
19
|
+
if (StringIsHtmlEvent(key)) continue;
|
|
20
|
+
// Raw markup that bypasses tree filtering entirely: parseHtml never
|
|
21
|
+
// produces this prop, so a tree in its shape should not carry it either.
|
|
22
|
+
if (key === 'dangerouslySetInnerHTML') continue;
|
|
23
|
+
if (key === 'ref') continue;
|
|
24
|
+
const checkName = (attributeCheckName[key] ?? key).toLowerCase();
|
|
25
|
+
if (!allow.attribute(tag, checkName)) continue;
|
|
26
|
+
if (checkName in urlAttributes && typeof value === 'string' && blockedUrl(checkName, value)) continue;
|
|
27
|
+
filtered[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return filtered;
|
|
30
|
+
}
|
|
31
|
+
// Matches toContentNode() in parseHtml.ts: zero children omits the prop,
|
|
32
|
+
// one is the value directly (not wrapped in an array), two or more is an
|
|
33
|
+
// array. Without this, an element that loses every child to filtering ends
|
|
34
|
+
// up with an empty-array `children` instead of no `children` prop at all -
|
|
35
|
+
// a shape parseHtml itself would never produce.
|
|
36
|
+
function normalizeChildren(children) {
|
|
37
|
+
if (!Array.isArray(children)) return children;
|
|
38
|
+
if (children.length === 0) return null;
|
|
39
|
+
if (children.length === 1) return children[0] ?? null;
|
|
40
|
+
return children;
|
|
41
|
+
}
|
|
42
|
+
function filter(node, allow) {
|
|
43
|
+
if (node === undefined || node === null || node === true || node === false) return node;
|
|
44
|
+
if (typeof node === 'string' || typeof node === 'number') return node;
|
|
45
|
+
if (Array.isArray(node)) {
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const child of node) {
|
|
48
|
+
const filteredChild = filter(child, allow);
|
|
49
|
+
// Non-rendered values, whether the child already was one or
|
|
50
|
+
// filtering made it one (an unwrapped or dropped element that
|
|
51
|
+
// left nothing behind), contribute nothing to the array - never
|
|
52
|
+
// a stray null placeholder in a dropped element's place.
|
|
53
|
+
if (filteredChild === undefined || filteredChild === null || filteredChild === true || filteredChild === false) continue;
|
|
54
|
+
if (Array.isArray(filteredChild)) out.push(...filteredChild);else out.push(filteredChild);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
// Not authored markup - parseHtml never produces a component node, so a
|
|
59
|
+
// tree in its shape should not carry one either, but this is defensive
|
|
60
|
+
// rather than reachable: nothing that round-trips through storage (JSON)
|
|
61
|
+
// can carry a class or function reference.
|
|
62
|
+
if (ContentNodeIsComponent(node)) return node;
|
|
63
|
+
const tag = node[$type];
|
|
64
|
+
const removal = removalFor(tag, true, allow);
|
|
65
|
+
if (removal === 'drop') return null;
|
|
66
|
+
const children = normalizeChildren(filter(node[$props].children ?? null, allow));
|
|
67
|
+
if (removal === 'unwrap') return children ?? [];
|
|
68
|
+
return {
|
|
69
|
+
[$type]: tag,
|
|
70
|
+
[$props]: {
|
|
71
|
+
...filterProps(tag, node[$props], allow),
|
|
72
|
+
...(children === null ? {} : {
|
|
73
|
+
children
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Applies an allow list to a content tree that already exists, rather than
|
|
80
|
+
* one being built - the same decisions `parseHtml(html, { allowList })` makes
|
|
81
|
+
* while parsing, for a tree that did not come from parsing HTML at all (one
|
|
82
|
+
* rebuilt from storage, for instance) and so never had the chance to.
|
|
83
|
+
*
|
|
84
|
+
* A disallowed element is unwrapped, keeping its children in its place, so
|
|
85
|
+
* filtering a tag never costs the reader the words inside it - the same rule
|
|
86
|
+
* `parseHtml` applies. An element holding program text rather than prose
|
|
87
|
+
* (`<script>`, and anything on the same footing as `<script>` even under a
|
|
88
|
+
* permissive allow list) is dropped whole instead. `on*` props, `ref`, and
|
|
89
|
+
* `dangerouslySetInnerHTML` are always stripped - the tree-shape equivalent
|
|
90
|
+
* of what `parseHtml` refuses to produce in the first place - and a `href`/
|
|
91
|
+
* `src`/... value using an executable scheme (`javascript:`, `vbscript:`) or,
|
|
92
|
+
* on a navigating attribute, `data:` is dropped the same way.
|
|
93
|
+
*/
|
|
94
|
+
export function filterContentNode(node, allowList) {
|
|
95
|
+
return filter(node, makeAllowCheck(allowList));
|
|
96
|
+
}
|
|
@@ -18,6 +18,20 @@ export type ParseHtmlOptions = {
|
|
|
18
18
|
*/
|
|
19
19
|
allowList?: HtmlAllowList | undefined;
|
|
20
20
|
};
|
|
21
|
+
export type AllowCheck = {
|
|
22
|
+
tag: (name: string) => boolean;
|
|
23
|
+
attribute: (tag: string, name: string) => boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function makeAllowCheck(allowList: HtmlAllowList | undefined): AllowCheck;
|
|
26
|
+
export declare const urlAttributes: Record<string, true>;
|
|
27
|
+
export declare function blockedUrl(attributeName: string, value: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether an element survives, and if not, how it goes.
|
|
30
|
+
*
|
|
31
|
+
* Elements holding program text rather than prose are dropped whole — there is
|
|
32
|
+
* no reader's content inside them to preserve. Everything else is unwrapped.
|
|
33
|
+
*/
|
|
34
|
+
export declare function removalFor(name: string, sanitize: boolean, allow: AllowCheck): 'drop' | 'unwrap' | undefined;
|
|
21
35
|
/**
|
|
22
36
|
* Parses raw HTML into a Tungsten content tree.
|
|
23
37
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/parseHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,mBAAmB,CAAA;AAKhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CACxC,CAAA;
|
|
1
|
+
{"version":3,"file":"parseHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/parseHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,mBAAmB,CAAA;AAKhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,MAAM,gBAAgB,GAAG;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACrB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CACpD,CAAA;AAID,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,UAAU,CAe/E;AAiBD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAE9C,CAAA;AA2BD,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAOxE;AAgCD;;;;;GAKG;AACH,wBAAgB,UAAU,CACtB,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,GACnD,MAAM,GAAG,QAAQ,GAAG,SAAS,CAK/B;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,WAAW,CAqGnF"}
|
|
@@ -6,7 +6,7 @@ const allowEverything = {
|
|
|
6
6
|
tag: () => true,
|
|
7
7
|
attribute: () => true
|
|
8
8
|
};
|
|
9
|
-
function makeAllowCheck(allowList) {
|
|
9
|
+
export function makeAllowCheck(allowList) {
|
|
10
10
|
if (allowList === undefined) return allowEverything;
|
|
11
11
|
const tags = new Set(allowList.tags);
|
|
12
12
|
const attributes = new Set(allowList.attributes);
|
|
@@ -21,7 +21,7 @@ function makeAllowCheck(allowList) {
|
|
|
21
21
|
}
|
|
22
22
|
// REGEX: on followed by at least one letter, e.g. onclick, onerror
|
|
23
23
|
const eventAttribute = /^on[a-z]/i;
|
|
24
|
-
const urlAttributes = {
|
|
24
|
+
export const urlAttributes = {
|
|
25
25
|
href: true,
|
|
26
26
|
src: true,
|
|
27
27
|
action: true,
|
|
@@ -56,7 +56,7 @@ function urlScheme(url) {
|
|
|
56
56
|
}
|
|
57
57
|
return '';
|
|
58
58
|
}
|
|
59
|
-
function blockedUrl(attributeName, value) {
|
|
59
|
+
export function blockedUrl(attributeName, value) {
|
|
60
60
|
const scheme = urlScheme(value);
|
|
61
61
|
if (scheme in executableSchemes) return true;
|
|
62
62
|
// data: URLs can carry a whole HTML document, so they are barred from
|
|
@@ -87,7 +87,7 @@ function attributesToProps(tag, attributes, sanitize, allow) {
|
|
|
87
87
|
* Elements holding program text rather than prose are dropped whole — there is
|
|
88
88
|
* no reader's content inside them to preserve. Everything else is unwrapped.
|
|
89
89
|
*/
|
|
90
|
-
function removalFor(name, sanitize, allow) {
|
|
90
|
+
export function removalFor(name, sanitize, allow) {
|
|
91
91
|
if (sanitize && name === 'script') return 'drop';
|
|
92
92
|
if (allow.tag(name)) return undefined;
|
|
93
93
|
return name in htmlRawTextTags ? 'drop' : 'unwrap';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HtmlAllowList } from './HtmlAllowList.js';
|
|
2
|
+
export type SanitizeHtmlOptions = {
|
|
3
|
+
/** Defaults to `safeHtmlAllowList`. Pass your own list to allow more or less. */
|
|
4
|
+
allowList?: HtmlAllowList;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Cleans untrusted HTML and returns it as a string, synchronously.
|
|
8
|
+
*
|
|
9
|
+
* `parseHtml()` already does the cleaning - this only adds the return trip,
|
|
10
|
+
* for a caller that needs the result as a string rather than a content tree:
|
|
11
|
+
* a paste handler cleaning a clipboard payload before it is inserted, or an
|
|
12
|
+
* editor cleaning its content before it is stored. Both happen at a rate
|
|
13
|
+
* `renderingNodeToHtml` (the async, component-aware server renderer) is the
|
|
14
|
+
* wrong shape for.
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeHtml(html: string, options?: SanitizeHtmlOptions): string;
|
|
17
|
+
//# sourceMappingURL=sanitizeHtml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../../src/rawHtml/sanitizeHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,MAAM,mBAAmB,GAAG;IAC9B,iFAAiF;IACjF,SAAS,CAAC,EAAE,aAAa,CAAA;CAC5B,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAGpF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { contentNodeToHtml } from "../render/contentNodeToHtml.js";
|
|
2
|
+
import { parseHtml } from "./parseHtml.js";
|
|
3
|
+
import { safeHtmlAllowList } from "./safeHtmlAllowList.js";
|
|
4
|
+
/**
|
|
5
|
+
* Cleans untrusted HTML and returns it as a string, synchronously.
|
|
6
|
+
*
|
|
7
|
+
* `parseHtml()` already does the cleaning - this only adds the return trip,
|
|
8
|
+
* for a caller that needs the result as a string rather than a content tree:
|
|
9
|
+
* a paste handler cleaning a clipboard payload before it is inserted, or an
|
|
10
|
+
* editor cleaning its content before it is stored. Both happen at a rate
|
|
11
|
+
* `renderingNodeToHtml` (the async, component-aware server renderer) is the
|
|
12
|
+
* wrong shape for.
|
|
13
|
+
*/
|
|
14
|
+
export function sanitizeHtml(html, options = {}) {
|
|
15
|
+
const allowList = options.allowList ?? safeHtmlAllowList;
|
|
16
|
+
return contentNodeToHtml(parseHtml(html, {
|
|
17
|
+
allowList
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadData.d.ts","sourceRoot":"","sources":["../../src/render/HeadData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5C,MAAM,MAAM,QAAQ,GAAG;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,mBAAmB,EAAE,aAAa,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,OAAO,GAAG,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"HeadData.d.ts","sourceRoot":"","sources":["../../src/render/HeadData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5C,MAAM,MAAM,QAAQ,GAAG;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,mBAAmB,EAAE,aAAa,CAAC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,OAAO,GAAG,QAAQ,CAAA;IACvB,GAAG,EAAE,OAAO,CAAA;CACf,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ContentNode } from '../ContentNode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Serialises a content tree to HTML, synchronously.
|
|
4
|
+
*
|
|
5
|
+
* `renderingNodeToHtml` (the server's real renderer) covers the general case -
|
|
6
|
+
* components, async data, `<head>` - and is async and internal because of it.
|
|
7
|
+
* This covers the subset that never needs any of that: plain HTML elements,
|
|
8
|
+
* text and arrays, the exact shape `parseHtml()` produces. Given a component
|
|
9
|
+
* node it throws rather than silently rendering nothing, since a caller
|
|
10
|
+
* reaching for the synchronous path has nothing that could resolve one.
|
|
11
|
+
*
|
|
12
|
+
* Attribute values are escaped, unlike `renderingNodeToHtml`'s - that renderer
|
|
13
|
+
* trusts values from application-authored JSX; this one exists specifically to
|
|
14
|
+
* round-trip untrusted markup back out as a string, so a value carrying `"`
|
|
15
|
+
* must not be able to break out of its attribute.
|
|
16
|
+
*/
|
|
17
|
+
export declare function contentNodeToHtml(node: ContentNode): string;
|
|
18
|
+
//# sourceMappingURL=contentNodeToHtml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentNodeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentNodeToHtml.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAapD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA+C3D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ContentNodeIsComponent } from "../ContentNode.js";
|
|
2
|
+
import { booleanAttributeValue } from "../booleanAttributeValue.js";
|
|
3
|
+
import { $props, $type } from "../contentNodeSymbols.js";
|
|
4
|
+
import { htmlEntities } from "../htmlEntities.js";
|
|
5
|
+
import { innerHtmlFromProps } from "../innerHtmlFromProps.js";
|
|
6
|
+
import { throwError } from "../throwError.js";
|
|
7
|
+
import { validChildren } from "../validChildren.js";
|
|
8
|
+
import { htmlMonoTags } from "./htmlMonoTags.js";
|
|
9
|
+
import { htmlSelfClosableTags } from "./htmlSelfClosableTags.js";
|
|
10
|
+
import { htmlVoidTags } from "./htmlVoidTags.js";
|
|
11
|
+
import { processProps } from "./processProps.js";
|
|
12
|
+
/**
|
|
13
|
+
* Serialises a content tree to HTML, synchronously.
|
|
14
|
+
*
|
|
15
|
+
* `renderingNodeToHtml` (the server's real renderer) covers the general case -
|
|
16
|
+
* components, async data, `<head>` - and is async and internal because of it.
|
|
17
|
+
* This covers the subset that never needs any of that: plain HTML elements,
|
|
18
|
+
* text and arrays, the exact shape `parseHtml()` produces. Given a component
|
|
19
|
+
* node it throws rather than silently rendering nothing, since a caller
|
|
20
|
+
* reaching for the synchronous path has nothing that could resolve one.
|
|
21
|
+
*
|
|
22
|
+
* Attribute values are escaped, unlike `renderingNodeToHtml`'s - that renderer
|
|
23
|
+
* trusts values from application-authored JSX; this one exists specifically to
|
|
24
|
+
* round-trip untrusted markup back out as a string, so a value carrying `"`
|
|
25
|
+
* must not be able to break out of its attribute.
|
|
26
|
+
*/
|
|
27
|
+
export function contentNodeToHtml(node) {
|
|
28
|
+
if (node === undefined || node === null || node === false || node === true) return '';
|
|
29
|
+
if (Array.isArray(node)) return node.map(contentNodeToHtml).join('');
|
|
30
|
+
if (typeof node === 'string' || typeof node === 'number') return htmlEntities(node.toString());
|
|
31
|
+
if (ContentNodeIsComponent(node)) {
|
|
32
|
+
throwError('contentNodeToHtml', `component nodes are not supported - encountered one of type ` + `${String(typeof node[$type] === 'string' ? node[$type] : node[$type].name)}.`);
|
|
33
|
+
}
|
|
34
|
+
const type = node[$type];
|
|
35
|
+
const {
|
|
36
|
+
children,
|
|
37
|
+
...restProps
|
|
38
|
+
} = node[$props];
|
|
39
|
+
const attribProps = processProps(restProps);
|
|
40
|
+
let attributes = '';
|
|
41
|
+
for (const key in attribProps) {
|
|
42
|
+
const value = attribProps[key];
|
|
43
|
+
if (typeof value === 'boolean') {
|
|
44
|
+
const attrValue = booleanAttributeValue(key, value);
|
|
45
|
+
if (attrValue !== null) attributes += attrValue === '' ? ` ${key}` : ` ${key}="${attrValue}"`;
|
|
46
|
+
} else if (value !== undefined && value !== null) {
|
|
47
|
+
attributes += ` ${key}="${htmlEntities(String(value))}"`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const innerHtml = innerHtmlFromProps(type, node[$props], validChildren(children));
|
|
51
|
+
if (innerHtml !== null) {
|
|
52
|
+
if (type in htmlVoidTags) {
|
|
53
|
+
throwError('contentNodeToHtml', `<${type}> is an HTML void type (cannot have children), but the node has dangerouslySetInnerHTML.`);
|
|
54
|
+
}
|
|
55
|
+
return `<${type}${attributes}>${innerHtml}</${type}>`;
|
|
56
|
+
}
|
|
57
|
+
if (validChildren(children)) {
|
|
58
|
+
if (type in htmlVoidTags) {
|
|
59
|
+
throwError('contentNodeToHtml', `<${type}> is an HTML void type (cannot have children), but the node has children.`);
|
|
60
|
+
}
|
|
61
|
+
return `<${type}${attributes}>${contentNodeToHtml(children)}</${type}>`;
|
|
62
|
+
}
|
|
63
|
+
if (type in htmlMonoTags) return `<${type}${attributes}>`;
|
|
64
|
+
if (type in htmlSelfClosableTags) return `<${type}${attributes} />`;
|
|
65
|
+
return `<${type}${attributes}></${type}>`;
|
|
66
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contentToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentToHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAMvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAGrE,wBAAsB,aAAa,CAC/B,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EACpE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EACxC,MAAM,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EACnE,iBAAiB,GAAE,sBAAkC,EAAE,GAAG,UAAQ,GACnE,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"contentToHtml.d.ts","sourceRoot":"","sources":["../../src/render/contentToHtml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,eAAe,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAMvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAGrE,wBAAsB,aAAa,CAC/B,eAAe,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EACpE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EACxC,MAAM,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,YAAY,EACnE,iBAAiB,GAAE,sBAAkC,EAAE,GAAG,UAAQ,GACnE,OAAO,CAAC,MAAM,CAAC,CAsDjB"}
|
|
@@ -23,7 +23,8 @@ export async function contentToHtml(compilationInfo, clientImportAliases, conten
|
|
|
23
23
|
dev,
|
|
24
24
|
tungstenPublicEnv,
|
|
25
25
|
siteBasePath: siteBasePathOverride,
|
|
26
|
-
hmr: config.hmr
|
|
26
|
+
hmr: config.hmr,
|
|
27
|
+
csr: config.csr
|
|
27
28
|
};
|
|
28
29
|
//console.log('rendering tree', inspect(tree, { depth: 99 } ))
|
|
29
30
|
let html = await renderingNodeToHtml(tree, false, headData, null);
|
|
@@ -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;AAS7E,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;AAS7E,wBAAgB,WAAW,CACvB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,QAAQ,QA6MrB"}
|
|
@@ -86,10 +86,16 @@ export function processHead(headNode, headData) {
|
|
|
86
86
|
// Tier 1 (hmr: 'reload', or nothing left for Tier 2 to do): always full-reload. A
|
|
87
87
|
// simple, independent branch from Tier 2's logic - restored as an explicit config value
|
|
88
88
|
// so it's available as a one-line escape hatch if Tier 2 itself is misbehaving.
|
|
89
|
-
|
|
89
|
+
// The server sends its bootId as the first event on every connection (see
|
|
90
|
+
// handleLiveReloadStream). If sessionStorage already holds a different one, this
|
|
91
|
+
// connection is a reconnect to a process that restarted since the page loaded (e.g. via
|
|
92
|
+
// tungsten devmon on a framework rebuild) - EventSource retries silently on its own, so
|
|
93
|
+
// this is the only reliable signal that the page is now stale and needs reloading.
|
|
94
|
+
const bootIdReloadSnippet = `es.addEventListener('boot',function(ev){` + `var bootId=JSON.parse(ev.data).bootId;` + `var key='__tungsten_boot_id__';` + `var prev=sessionStorage.getItem(key);` + `sessionStorage.setItem(key,bootId);` + `if(prev!==null&&prev!==bootId){location.reload()}` + `});`;
|
|
95
|
+
const tier1OnlyScript = `(function(){` + `var es=new EventSource('${siteBasePath}/__tungsten_live_reload__');` + bootIdReloadSnippet + `es.addEventListener('css',function(){location.reload()});` + `es.addEventListener('js',function(){location.reload()});` + `})();`;
|
|
90
96
|
// Tier 2: attempt an in-place component/CSS hot-swap before falling back to a full
|
|
91
97
|
// reload. See tungsten/docs/hmr.md.
|
|
92
|
-
const tier2Script = `(function(){` + `var es=new EventSource('${siteBasePath}/__tungsten_live_reload__');` + `function fullReload(reason){console.info('[tungsten-hmr] full reload:',reason);location.reload()}` + `function loadHmr(){return import('${frameworkDestLocation}/hmr/applyHmrModule.js')}`
|
|
98
|
+
const tier2Script = `(function(){` + `var es=new EventSource('${siteBasePath}/__tungsten_live_reload__');` + `function fullReload(reason){console.info('[tungsten-hmr] full reload:',reason);location.reload()}` + `es.addEventListener('boot',function(ev){` + `var bootId=JSON.parse(ev.data).bootId;` + `var key='__tungsten_boot_id__';` + `var prev=sessionStorage.getItem(key);` + `sessionStorage.setItem(key,bootId);` + `if(prev!==null&&prev!==bootId){fullReload('dev server restarted')}` + `});` + `function loadHmr(){return import('${frameworkDestLocation}/hmr/applyHmrModule.js')}`
|
|
93
99
|
// CSS has no state to preserve, so re-applying it is always safe: refreshStylesheets()
|
|
94
100
|
// inserts the new link and only removes the old one once it's loaded, so there's no
|
|
95
101
|
// gap where nothing is applied (a plain href swap can produce a visible flash).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderingTreeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/renderingTreeToHtml.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"renderingTreeToHtml.d.ts","sourceRoot":"","sources":["../../src/render/renderingTreeToHtml.ts"],"names":[],"mappings":"AAGA,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,CA8JjB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { reduce as objectReduce } from 'sxy-lib/objects.js';
|
|
2
|
+
import { booleanAttributeValue } from "../booleanAttributeValue.js";
|
|
2
3
|
import { processProps } from "./processProps.js";
|
|
3
4
|
import { htmlVoidTags } from "./htmlVoidTags.js";
|
|
4
5
|
import { validRenderingChildren } from "../validChildren.js";
|
|
@@ -77,7 +78,9 @@ export async function renderingNodeToHtml(node, noEscape, headData, lastSelect)
|
|
|
77
78
|
const attribs = objectReduce(processProps(attribProps), (string, key, value) => {
|
|
78
79
|
//debug('reducing attr', key, value)
|
|
79
80
|
if (typeof value === 'boolean') {
|
|
80
|
-
|
|
81
|
+
const attrValue = booleanAttributeValue(key, value);
|
|
82
|
+
if (attrValue === null) return string;
|
|
83
|
+
return attrValue === '' ? string + ` ${key}` : string + ` ${key}="${attrValue}"`;
|
|
81
84
|
} else if (value === undefined || value === null) {
|
|
82
85
|
return string;
|
|
83
86
|
} else {
|
|
@@ -86,6 +89,10 @@ export async function renderingNodeToHtml(node, noEscape, headData, lastSelect)
|
|
|
86
89
|
}, '');
|
|
87
90
|
// : ''
|
|
88
91
|
debug('attribs', attribs);
|
|
92
|
+
// CSR: body's own attributes render, its children don't - see FrameworkConfig.ts.
|
|
93
|
+
if (type === 'body' && headData.csr) {
|
|
94
|
+
return html + `<${type}${attribs}></${type}>`;
|
|
95
|
+
}
|
|
89
96
|
if (typeof node[$props].__zoneHtml === 'string') {
|
|
90
97
|
return html + `<${type}${attribs}>${node[$props].__zoneHtml}</${type}>`;
|
|
91
98
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RouteContentFunction } from './RouteContentFunction.js';
|
|
2
|
+
import type { TungstenErrorInfo } from './TungstenErrorInfo.js';
|
|
3
|
+
export type ErrorRouteType = {
|
|
4
|
+
content: RouteContentFunction<object, TungstenErrorInfo>;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=ErrorRouteType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorRouteType.d.ts","sourceRoot":"","sources":["../../src/routing/ErrorRouteType.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAM/D,MAAM,MAAM,cAAc,GAAG;IACzB,OAAO,EAAE,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CAC3D,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Routes } from './Routes.js';
|
|
2
2
|
export type Path = `/${string}`;
|
|
3
|
-
export type
|
|
3
|
+
export type SpecialRoute = 'default' | '404' | 'error';
|
|
4
|
+
export type RouteKey = Path | SpecialRoute;
|
|
4
5
|
export declare function StringIsValidRouteKey(string: string, routes: Routes): string is RouteKey;
|
|
5
6
|
export declare function StringIsPath(string: string): string is Path;
|
|
6
7
|
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,
|
|
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,YAAY,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAA;AACtD,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,YAAY,CAAA;AAE1C,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"}
|
package/dist/routing/Routes.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Path } from './RouteTypes.js';
|
|
2
2
|
import type { RouteType } from './RouteType.js';
|
|
3
|
+
import type { ErrorRouteType } from './ErrorRouteType.js';
|
|
3
4
|
export type MinimumRoutes = {
|
|
4
5
|
default: RouteType<any, any>;
|
|
5
6
|
} | {
|
|
@@ -7,6 +8,8 @@ export type MinimumRoutes = {
|
|
|
7
8
|
};
|
|
8
9
|
export type Routes = {
|
|
9
10
|
[key in Path]: RouteType<any, any>;
|
|
11
|
+
} & {
|
|
12
|
+
'error'?: ErrorRouteType;
|
|
10
13
|
} & ({
|
|
11
14
|
'default': RouteType<any, any>;
|
|
12
15
|
} | {
|
|
@@ -14,6 +17,8 @@ export type Routes = {
|
|
|
14
17
|
});
|
|
15
18
|
export type CustomRoutes<Route extends RouteType<any, any>> = {
|
|
16
19
|
[key in Path]: Route;
|
|
20
|
+
} & {
|
|
21
|
+
error: ErrorRouteType;
|
|
17
22
|
} & ({
|
|
18
23
|
'default': Route;
|
|
19
24
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Routes.d.ts","sourceRoot":"","sources":["../../src/routing/Routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"Routes.d.ts","sourceRoot":"","sources":["../../src/routing/Routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD,MAAM,MAAM,aAAa,GACnB;IAAE,OAAO,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,GAChC;IAAE,KAAK,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,CAAA;AAEpC,MAAM,MAAM,MAAM,GAAG;KAAG,GAAG,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;CAAE,GACzD;IAAE,OAAO,CAAC,EAAE,cAAc,CAAA;CAAE,GAC5B,CACE;IAAE,SAAS,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,GAChC;IAAE,KAAK,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,CACnC,CAAA;AAED,MAAM,MAAM,YAAY,CAAC,KAAK,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI;KAAG,GAAG,IAAI,IAAI,GAAG,KAAK;CAAE,GACpF;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GACzB,CACE;IAAE,SAAS,EAAE,KAAK,CAAA;CAAE,GAClB;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CACrB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TungstenErrorInfo.d.ts","sourceRoot":"","sources":["../../src/routing/TungstenErrorInfo.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAEvE"}
|
|
@@ -121,6 +121,27 @@ function processItem(element, styles, usage) {
|
|
|
121
121
|
}
|
|
122
122
|
newClassName = newClassName.trim();
|
|
123
123
|
if (newClassName !== '') element[$props].className = newClassName;
|
|
124
|
+
// Any other *ClassName prop (e.g. messageClassName, handed to a child component to apply
|
|
125
|
+
// to one of its own elements) gets the same local-stylesheet lookup applied in place, so a
|
|
126
|
+
// classname threaded down through props can still be scoped against this component's own
|
|
127
|
+
// stylesheet, even though the child's own render tree is outside this walk - see
|
|
128
|
+
// styleContentSxy() above.
|
|
129
|
+
for (const key in element[$props]) {
|
|
130
|
+
if (key === 'className' || !key.endsWith('ClassName')) continue;
|
|
131
|
+
const value = element[$props][key];
|
|
132
|
+
if (value === undefined || value === null) continue;
|
|
133
|
+
if (typeof value !== 'string') {
|
|
134
|
+
throw new TungstenError(`styleContentSxy() error: ${ContentNodeElementIsHtml(element) ? element[$type] : getComponentName(element)}` + ` ${key} prop should be a string. Received ${typeof value}`);
|
|
135
|
+
}
|
|
136
|
+
let newValue = value;
|
|
137
|
+
for (const cn of value.split(' ')) {
|
|
138
|
+
if (cn in styles.classes) {
|
|
139
|
+
newValue += ` ${styles.classes[cn]}`;
|
|
140
|
+
usage?.usedClasses.add(cn);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (newValue !== value) element[$props][key] = newValue;
|
|
144
|
+
}
|
|
124
145
|
debug('element after mods', tagName, element);
|
|
125
146
|
// apply this to both fragment and elements
|
|
126
147
|
if (element[$props].children !== undefined) {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ConstructPackage, TungstenBaseComponent, TungstenComponentPropsBase } from '../Components.jsx';
|
|
2
|
+
import type { TaggedAttributeMismatch } from '../hydrate/renderHydrationMismatchBanner.js';
|
|
3
|
+
import type { TestAppOptions } from './index.js';
|
|
4
|
+
export type HydrateTestAppOptions<Props extends object> = TestAppOptions<Props> & {
|
|
5
|
+
serverProps?: Props;
|
|
6
|
+
csr?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type HydrateTestAppArgs<Props extends object> = object extends Props ? [options?: HydrateTestAppOptions<Props>] : keyof Props extends never ? [options?: HydrateTestAppOptions<Props>] : [options: HydrateTestAppOptions<Props> & {
|
|
9
|
+
props: Props;
|
|
10
|
+
}];
|
|
11
|
+
export type HydrateTestApp<Component extends TungstenBaseComponent> = {
|
|
12
|
+
readonly document: Document;
|
|
13
|
+
readonly container: HTMLElement;
|
|
14
|
+
readonly component: Component;
|
|
15
|
+
readonly html: string;
|
|
16
|
+
readonly mismatches: readonly TaggedAttributeMismatch[];
|
|
17
|
+
resolved(): Promise<void>;
|
|
18
|
+
setProps(props: Component['props']): Promise<void>;
|
|
19
|
+
unmount(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The hydration round trip createTestApp() deliberately doesn't cover: render a component to
|
|
23
|
+
* HTML the way a real server would, parse it with a real HTML parser, hydrate a fresh client
|
|
24
|
+
* instance against the result, and hand back the live, hydrated component - still fully usable
|
|
25
|
+
* via setProps()/resolved(), exactly like createTestApp()'s result.
|
|
26
|
+
*
|
|
27
|
+
* By default `props` renders on both sides, so a passing test proves an ordinary page hydrates
|
|
28
|
+
* clean (`mismatches` empty). Pass `serverProps` to diverge the two passes on purpose and assert
|
|
29
|
+
* on the resulting `mismatches` - the case a one-off "does the reconciler still agree with real
|
|
30
|
+
* served pages" script never could, because nothing fed it markup that didn't already match.
|
|
31
|
+
*
|
|
32
|
+
* A structural mismatch (a different tag, a different number of children) is not something
|
|
33
|
+
* reconcileHydratedAttributes() can fix - hydrateRootComponent() throws, exactly as the real
|
|
34
|
+
* client hydrator does, so a caller reproducing that class of bug should assert on the rejection.
|
|
35
|
+
*/
|
|
36
|
+
export declare function hydrateTestApp<Props extends TungstenComponentPropsBase, Component extends TungstenBaseComponent<any, any>, RouteData = undefined>(RootComponent: new (pkg: ConstructPackage<NoInfer<Props>, RouteData>) => Component, ...args: HydrateTestAppArgs<Props>): Promise<HydrateTestApp<Component>>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=hydrateTestApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrateTestApp.d.ts","sourceRoot":"","sources":["../../src/testing/hydrateTestApp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,gBAAgB,EAChB,qBAAqB,EACrB,0BAA0B,EAC7B,MAAM,mBAAmB,CAAA;AAO1B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAA;AAa1F,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,YAAY,CAAA;AAEnE,MAAM,MAAM,qBAAqB,CAAC,KAAK,SAAS,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,GAAG;IAM9E,WAAW,CAAC,EAAE,KAAK,CAAA;IAEnB,GAAG,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,KAAK,kBAAkB,CAAC,KAAK,SAAS,MAAM,IAAI,MAAM,SAAS,KAAK,GAC9D,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC,GACxC,MAAM,KAAK,SAAS,KAAK,GACrB,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC,GACxC,CAAC,OAAO,EAAE,qBAAqB,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC,CAAA;AAEpE,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,qBAAqB,IAAI;IAClE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAG7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAIrB,QAAQ,CAAC,UAAU,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAEvD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B,CAAA;AAkGD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAChC,KAAK,SAAS,0BAA0B,EACxC,SAAS,SAAS,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,EACjD,SAAS,GAAG,SAAS,EAErB,aAAa,EAAE,KAAK,GAAG,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,KAAK,SAAS,EAClF,GAAG,IAAI,EAAE,kBAAkB,CAAC,KAAK,CAAC,GACnC,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CA6HpC"}
|