@jxsuite/runtime 1.2.0 → 1.3.1
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/LICENSE +21 -0
- package/README.md +7 -7
- package/dist/runtime.js +38 -10
- package/dist/runtime.js.map +4 -4
- package/package.json +4 -4
- package/src/runtime.ts +58 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "JSON-native reactive web component runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"upgrade": "bunx npm-check-updates -u && bun install"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@jxsuite/schema": "^1.
|
|
34
|
-
"@vue/reactivity": "
|
|
33
|
+
"@jxsuite/schema": "^1.4.0",
|
|
34
|
+
"@vue/reactivity": "3.5.40"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@happy-dom/global-registrator": "^20.
|
|
37
|
+
"@happy-dom/global-registrator": "^20.11.1"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { evaluateExpression, isMutating } from "./expression.ts";
|
|
27
27
|
import type { DynamicClass, JxEventHandler, JxPath, JxRenderOptions, JxScope } from "./types.ts";
|
|
28
28
|
import {
|
|
29
|
+
bodyReturnsValue,
|
|
29
30
|
hasStructuredBody,
|
|
30
31
|
isExpressionDef,
|
|
31
32
|
isFunctionDef,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
isPrototypeDef,
|
|
36
37
|
isRef as isRefValue,
|
|
37
38
|
isServerFnDef,
|
|
39
|
+
isTemplateString,
|
|
38
40
|
paramNames,
|
|
39
41
|
} from "@jxsuite/schema/guards";
|
|
40
42
|
import { runStatements } from "./statements.ts";
|
|
@@ -68,6 +70,29 @@ import type { Ref } from "@vue/reactivity";
|
|
|
68
70
|
* @returns {Promise<JxScope>} Resolves with the live component scope (state reactive
|
|
69
71
|
* proxy)
|
|
70
72
|
*/
|
|
73
|
+
/** The document-schema version this runtime understands (the `/schema/vN` in a hosted `$schema`). */
|
|
74
|
+
export const SUPPORTED_SCHEMA_VERSION = 1;
|
|
75
|
+
let _schemaVersionWarned = false;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Warn once when a document declares a hosted `$schema` whose version differs from the one this
|
|
79
|
+
* runtime supports. A relative or non-hosted `$schema` (local dev schema paths) is ignored. This is
|
|
80
|
+
* a diagnostic only — there is no document-format migration story yet (see spec §3.2 / §21.4).
|
|
81
|
+
*/
|
|
82
|
+
function checkSchemaVersion($schema: unknown): void {
|
|
83
|
+
if (_schemaVersionWarned || typeof $schema !== "string") {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const match = $schema.match(/\/schema\/v(\d+)\b/);
|
|
87
|
+
if (match && Number(match[1]) !== SUPPORTED_SCHEMA_VERSION) {
|
|
88
|
+
_schemaVersionWarned = true;
|
|
89
|
+
console.warn(
|
|
90
|
+
`Jx: document $schema is version v${match[1]} but this runtime supports ` +
|
|
91
|
+
`v${SUPPORTED_SCHEMA_VERSION}. Behavior may be undefined; there is no format migrator yet.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
export async function Jx(
|
|
72
97
|
source: string | JxDocument,
|
|
73
98
|
target: HTMLElement = document.body,
|
|
@@ -75,6 +100,7 @@ export async function Jx(
|
|
|
75
100
|
) {
|
|
76
101
|
const base = typeof source === "string" ? new URL(source, location.href).href : location.href;
|
|
77
102
|
const doc = await resolve(source);
|
|
103
|
+
checkSchemaVersion(doc.$schema);
|
|
78
104
|
|
|
79
105
|
// Register custom elements declared in $elements (depth-first)
|
|
80
106
|
if (doc.$elements) {
|
|
@@ -615,9 +641,9 @@ async function resolveFunction(def: JxFunctionDef, state: JxScope, key: string,
|
|
|
615
641
|
let isComputed = false;
|
|
616
642
|
if (!hasParams) {
|
|
617
643
|
if (typeof def.body === "string") {
|
|
618
|
-
isComputed =
|
|
644
|
+
isComputed = bodyReturnsValue(def.body);
|
|
619
645
|
} else if (fn) {
|
|
620
|
-
isComputed = fn.length <= 1 &&
|
|
646
|
+
isComputed = fn.length <= 1 && bodyReturnsValue(fn.toString());
|
|
621
647
|
}
|
|
622
648
|
}
|
|
623
649
|
if (isComputed) {
|
|
@@ -690,6 +716,9 @@ export const RESERVED_KEYS = new Set([
|
|
|
690
716
|
* @param {JxRenderOptions} [options]
|
|
691
717
|
* @returns {HTMLElement | Text}
|
|
692
718
|
*/
|
|
719
|
+
/** Refs already warned about, so the §13 diagnostic fires once per distinct target. */
|
|
720
|
+
const warnedRefChildren = new Set<string>();
|
|
721
|
+
|
|
693
722
|
export function renderNode(
|
|
694
723
|
def: JxElement | string | number | boolean,
|
|
695
724
|
state: JxScope,
|
|
@@ -719,6 +748,22 @@ export function renderNode(
|
|
|
719
748
|
}
|
|
720
749
|
}
|
|
721
750
|
|
|
751
|
+
/*
|
|
752
|
+
* §13 diagnostic: a node-level external `$ref` (the withdrawn component-instance syntax) has no
|
|
753
|
+
* tagName and silently renders an empty <div>. Warn once per target instead of failing silently —
|
|
754
|
+
* the supported mechanism is $elements + a custom-element tag (see spec §13).
|
|
755
|
+
*/
|
|
756
|
+
if ("$ref" in def && !def.tagName) {
|
|
757
|
+
const refTarget = String((def as { $ref?: unknown }).$ref ?? "");
|
|
758
|
+
if (!warnedRefChildren.has(refTarget)) {
|
|
759
|
+
warnedRefChildren.add(refTarget);
|
|
760
|
+
console.warn(
|
|
761
|
+
`Jx: a $ref child ("${refTarget}") is not a supported component instance; register the ` +
|
|
762
|
+
`component in $elements and use its custom-element tag instead (spec §13).`,
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
722
767
|
// Custom element with $props: set JS properties on the element instance
|
|
723
768
|
const tagName = def.tagName ?? "div";
|
|
724
769
|
const isCustomEl = tagName.includes("-") && customElements.get(tagName);
|
|
@@ -728,6 +773,17 @@ export function renderNode(
|
|
|
728
773
|
}
|
|
729
774
|
|
|
730
775
|
if (def.$props) {
|
|
776
|
+
// A hyphenated tag carrying $props is a component instance whose definition has not finished
|
|
777
|
+
// Registering yet (async defineElement — e.g. the Studio desktop canvas, where the component
|
|
778
|
+
// Fetch can land after this node renders). Render via the property-first path anyway: it sets
|
|
779
|
+
// The instance props as JS properties on the bare element, which the eventual upgrade's
|
|
780
|
+
// ConnectedCallback reads back off `this` (see the def.state merge below), so the instance's
|
|
781
|
+
// Props win. The old branch stripped the props here, so a late upgrade painted the component's
|
|
782
|
+
// State DEFAULTS instead ("0"/"DESCRIPTION" for every instance). A non-custom tag can never
|
|
783
|
+
// Upgrade, so it keeps the historical mergeProps-into-scope behavior.
|
|
784
|
+
if (tagName.includes("-")) {
|
|
785
|
+
return renderCustomElementWithProps(def, localState, options, path);
|
|
786
|
+
}
|
|
731
787
|
const { $props: _$props, ...rest } = def;
|
|
732
788
|
return renderNode(rest, mergeProps(def, localState), options);
|
|
733
789
|
}
|
|
@@ -780,10 +836,6 @@ export function renderNode(
|
|
|
780
836
|
* @param {unknown} val
|
|
781
837
|
* @returns {boolean}
|
|
782
838
|
*/
|
|
783
|
-
function isTemplateString(val: unknown): val is string {
|
|
784
|
-
return typeof val === "string" && val.includes("${");
|
|
785
|
-
}
|
|
786
|
-
|
|
787
839
|
// ─── Property / style / attribute application ─────────────────────────────────
|
|
788
840
|
|
|
789
841
|
/**
|