mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
$global hydration (#2093)
This commit is contained in:
parent
54575cf53a
commit
688982e757
68
.sizes.json
68
.sizes.json
@ -7,81 +7,81 @@
|
||||
{
|
||||
"name": "*",
|
||||
"total": {
|
||||
"min": 13105,
|
||||
"gzip": 5618,
|
||||
"brotli": 5130
|
||||
"min": 12923,
|
||||
"gzip": 5520,
|
||||
"brotli": 5018
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "counter",
|
||||
"user": {
|
||||
"min": 351,
|
||||
"gzip": 276,
|
||||
"gzip": 274,
|
||||
"brotli": 234
|
||||
},
|
||||
"runtime": {
|
||||
"min": 4083,
|
||||
"gzip": 1894,
|
||||
"brotli": 1681
|
||||
"min": 4123,
|
||||
"gzip": 1905,
|
||||
"brotli": 1705
|
||||
},
|
||||
"total": {
|
||||
"min": 4434,
|
||||
"gzip": 2170,
|
||||
"brotli": 1915
|
||||
"min": 4474,
|
||||
"gzip": 2179,
|
||||
"brotli": 1939
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "counter 💧",
|
||||
"user": {
|
||||
"min": 204,
|
||||
"gzip": 179,
|
||||
"brotli": 151
|
||||
"gzip": 178,
|
||||
"brotli": 154
|
||||
},
|
||||
"runtime": {
|
||||
"min": 2612,
|
||||
"gzip": 1350,
|
||||
"brotli": 1210
|
||||
"min": 2664,
|
||||
"gzip": 1370,
|
||||
"brotli": 1223
|
||||
},
|
||||
"total": {
|
||||
"min": 2816,
|
||||
"gzip": 1529,
|
||||
"brotli": 1361
|
||||
"min": 2868,
|
||||
"gzip": 1548,
|
||||
"brotli": 1377
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "comments",
|
||||
"user": {
|
||||
"min": 1216,
|
||||
"gzip": 701,
|
||||
"brotli": 636
|
||||
"gzip": 708,
|
||||
"brotli": 638
|
||||
},
|
||||
"runtime": {
|
||||
"min": 7506,
|
||||
"gzip": 3457,
|
||||
"brotli": 3116
|
||||
"min": 7536,
|
||||
"gzip": 3491,
|
||||
"brotli": 3142
|
||||
},
|
||||
"total": {
|
||||
"min": 8722,
|
||||
"gzip": 4158,
|
||||
"brotli": 3752
|
||||
"min": 8752,
|
||||
"gzip": 4199,
|
||||
"brotli": 3780
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "comments 💧",
|
||||
"user": {
|
||||
"min": 988,
|
||||
"gzip": 587,
|
||||
"brotli": 544
|
||||
"gzip": 591,
|
||||
"brotli": 554
|
||||
},
|
||||
"runtime": {
|
||||
"min": 7999,
|
||||
"gzip": 3683,
|
||||
"brotli": 3342
|
||||
"min": 8047,
|
||||
"gzip": 3690,
|
||||
"brotli": 3345
|
||||
},
|
||||
"total": {
|
||||
"min": 8987,
|
||||
"gzip": 4270,
|
||||
"brotli": 3886
|
||||
"min": 9035,
|
||||
"gzip": 4281,
|
||||
"brotli": 3899
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -4,8 +4,6 @@ export type Renderer = (...args: unknown[]) => unknown;
|
||||
|
||||
export type CommentWalker = TreeWalker & Record<string, Comment>;
|
||||
|
||||
export type ScopeContext = Record<string, [Scope, number | string]>;
|
||||
|
||||
export type Scope<
|
||||
T extends { [x: string | number]: unknown } = {
|
||||
[x: string | number]: unknown;
|
||||
@ -18,7 +16,7 @@ export type Scope<
|
||||
___client: boolean;
|
||||
___bound: Map<unknown, unknown> | undefined;
|
||||
___renderer: ClientRenderer | undefined;
|
||||
___context: ScopeContext | undefined;
|
||||
$global: Record<string, unknown>;
|
||||
_: Scope | undefined;
|
||||
[x: string | number]: any;
|
||||
} & T;
|
||||
@ -47,12 +45,9 @@ export const enum AccessorChars {
|
||||
TAG_VARIABLE = "/",
|
||||
COND_SCOPE = "!",
|
||||
LOOP_SCOPE_ARRAY = "!",
|
||||
COND_CONTEXT = "^",
|
||||
LOOP_CONTEXT = "^",
|
||||
COND_RENDERER = "(",
|
||||
LOOP_SCOPE_MAP = "(",
|
||||
LOOP_VALUE = ")",
|
||||
CONTEXT_VALUE = ":",
|
||||
PREVIOUS_ATTRIBUTES = "~",
|
||||
}
|
||||
|
||||
|
||||
@ -83,11 +83,7 @@ export function setConditionalRenderer<ChildScope extends Scope>(
|
||||
|
||||
if (newRenderer) {
|
||||
newScope = scope[nodeAccessor + AccessorChars.COND_SCOPE] =
|
||||
createScopeWithRenderer(
|
||||
newRenderer,
|
||||
(scope[nodeAccessor + AccessorChars.COND_CONTEXT] ||= scope.___context),
|
||||
scope,
|
||||
) as ChildScope;
|
||||
createScopeWithRenderer(newRenderer, scope.$global, scope) as ChildScope;
|
||||
prevScope = prevScope || getEmptyScope(scope[nodeAccessor] as Comment);
|
||||
} else {
|
||||
newScope = getEmptyScope(scope[nodeAccessor] as Comment) as ChildScope;
|
||||
@ -132,11 +128,7 @@ export function setConditionalRendererOnlyChild(
|
||||
|
||||
if (newRenderer) {
|
||||
const newScope = (scope[nodeAccessor + AccessorChars.COND_SCOPE] =
|
||||
createScopeWithRenderer(
|
||||
newRenderer,
|
||||
(scope[nodeAccessor + AccessorChars.COND_CONTEXT] ||= scope.___context),
|
||||
scope,
|
||||
));
|
||||
createScopeWithRenderer(newRenderer, scope.$global, scope));
|
||||
(newRenderer.___fragment ?? defaultFragment).___insertBefore(
|
||||
newScope,
|
||||
referenceNode,
|
||||
@ -243,12 +235,7 @@ function loop(
|
||||
let childScope = oldMap.get(key);
|
||||
const isNew = !childScope;
|
||||
if (!childScope) {
|
||||
childScope = createScopeWithRenderer(
|
||||
renderer,
|
||||
(scope[nodeAccessor + AccessorChars.LOOP_CONTEXT] ||=
|
||||
scope.___context),
|
||||
scope,
|
||||
);
|
||||
childScope = createScopeWithRenderer(renderer, scope.$global, scope);
|
||||
// TODO: once we can track moves
|
||||
// needsReconciliation = true;
|
||||
} else {
|
||||
|
||||
@ -35,7 +35,6 @@ export type { Scope } from "../common/types";
|
||||
|
||||
export {
|
||||
createRenderer,
|
||||
initContextProvider,
|
||||
dynamicTagAttrs,
|
||||
createScopeWithRenderer,
|
||||
} from "./renderer";
|
||||
@ -48,7 +47,6 @@ export {
|
||||
intersection,
|
||||
closure,
|
||||
dynamicClosure,
|
||||
contextClosure,
|
||||
dynamicSubscribers,
|
||||
childClosures,
|
||||
setTagVar,
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import {
|
||||
type Accessor,
|
||||
AccessorChars,
|
||||
type Scope,
|
||||
type ScopeContext,
|
||||
} from "../common/types";
|
||||
import { type Accessor, AccessorChars, type Scope } from "../common/types";
|
||||
import { setConditionalRendererOnlyChild } from "./control-flow";
|
||||
import { attrs } from "./dom";
|
||||
import { type DOMFragment, defaultFragment } from "./fragment";
|
||||
import { type DOMFragment } from "./fragment";
|
||||
import { bindRenderer, createScope } from "./scope";
|
||||
import type { IntersectionSignal, ValueSignal } from "./signals";
|
||||
import { WalkCodes, trimWalkString, walk } from "./walker";
|
||||
@ -41,10 +36,10 @@ type SetupFn = (scope: Scope) => void;
|
||||
|
||||
export function createScopeWithRenderer(
|
||||
renderer: RendererOrElementName,
|
||||
context: ScopeContext,
|
||||
$global: Scope["___global"],
|
||||
ownerScope?: Scope,
|
||||
) {
|
||||
const newScope = createScope(context as ScopeContext);
|
||||
const newScope = createScope($global);
|
||||
newScope._ = renderer.___owner || ownerScope;
|
||||
newScope.___renderer = renderer as Renderer;
|
||||
initRenderer(renderer, newScope);
|
||||
@ -56,34 +51,6 @@ export function createScopeWithRenderer(
|
||||
return newScope;
|
||||
}
|
||||
|
||||
export function initContextProvider(
|
||||
scope: Scope,
|
||||
scopeAccessor: number,
|
||||
valueAccessor: number,
|
||||
contextKey: string,
|
||||
renderer: Renderer,
|
||||
) {
|
||||
const node: Node = scope[scopeAccessor];
|
||||
const newScope = createScopeWithRenderer(
|
||||
renderer,
|
||||
{
|
||||
...scope.___context,
|
||||
[contextKey]: [scope, valueAccessor],
|
||||
},
|
||||
scope,
|
||||
);
|
||||
|
||||
(renderer.___fragment ?? defaultFragment).___insertBefore(
|
||||
newScope,
|
||||
node.parentNode!,
|
||||
node.nextSibling,
|
||||
);
|
||||
|
||||
for (const signal of renderer.___closureSignals) {
|
||||
signal(newScope, true);
|
||||
}
|
||||
}
|
||||
|
||||
export function initRenderer(renderer: RendererOrElementName, scope: Scope) {
|
||||
const dom =
|
||||
typeof renderer === "string"
|
||||
|
||||
@ -16,7 +16,7 @@ export function register<T>(id: string, obj: T): T {
|
||||
return obj;
|
||||
}
|
||||
|
||||
export const scopeLookup = {} as Record<number, Scope>;
|
||||
export const scopeLookup = {} as Record<number | string, Scope>;
|
||||
|
||||
export function init(
|
||||
runtimeId = ResumeSymbols.DEFAULT_RUNTIME_ID /* [a-zA-Z0-9]+ */,
|
||||
@ -46,50 +46,56 @@ export function init(
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(window, resumeVar, {
|
||||
get() {
|
||||
return fakeArray;
|
||||
},
|
||||
});
|
||||
|
||||
if (initialHydration) {
|
||||
for (let i = 0; i < initialHydration.length; i += 2) {
|
||||
resume(initialHydration[i], initialHydration[i + 1]);
|
||||
}
|
||||
} else {
|
||||
(window as any)[resumeVar] = fakeArray;
|
||||
}
|
||||
|
||||
function resume(
|
||||
scopesFn: (
|
||||
b: typeof bind,
|
||||
s: typeof scopeLookup,
|
||||
...rest: unknown[]
|
||||
) => Record<string, Scope>,
|
||||
scopesFn:
|
||||
| null
|
||||
| ((
|
||||
b: typeof bind,
|
||||
s: typeof scopeLookup,
|
||||
...rest: unknown[]
|
||||
) => Record<string, Scope>),
|
||||
calls: Array<string | number>,
|
||||
) {
|
||||
// TODO: Can be refactored/removed when adding runtimeId and componentIdPrefix
|
||||
/**
|
||||
* Necessary for injecting content into an existing document (e.g. microframe)
|
||||
*/
|
||||
if (doc.readyState !== "loading") {
|
||||
walker.currentNode = doc;
|
||||
}
|
||||
|
||||
const scopes = scopesFn?.(bind, scopeLookup);
|
||||
if (scopesFn) {
|
||||
const scopes = scopesFn(bind, scopeLookup);
|
||||
scopeLookup.$global ||= scopes.$global || {};
|
||||
|
||||
/**
|
||||
* Loop over all the new hydration scopes and see if a previous walk
|
||||
* had to create a dummy scope to store Nodes of interest.
|
||||
* If so merge them and set/replace the scope in the scopeLookup.
|
||||
*/
|
||||
for (const scopeIdAsString in scopes) {
|
||||
const scopeId = parseInt(scopeIdAsString);
|
||||
const scope = scopes[scopeId];
|
||||
const storedScope = scopeLookup[scopeId];
|
||||
|
||||
if (storedScope !== scope) {
|
||||
scopeLookup[scopeId] = Object.assign(scope, storedScope);
|
||||
/**
|
||||
* Loop over all the new hydration scopes and see if a previous walk
|
||||
* had to create a dummy scope to store Nodes of interest.
|
||||
* If so merge them and set/replace the scope in the scopeLookup.
|
||||
*/
|
||||
for (const scopeIdAsString in scopes) {
|
||||
if (scopeIdAsString === "$global") continue;
|
||||
const scopeId = parseInt(scopeIdAsString);
|
||||
const scope = scopes[scopeId];
|
||||
const storedScope = scopeLookup[scopeId];
|
||||
scope.$global = scopes.$global;
|
||||
if (storedScope !== scope) {
|
||||
scopeLookup[scopeId] = Object.assign(scope, storedScope) as Scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((currentNode = walker.nextNode() as ChildNode)) {
|
||||
const nodeValue = currentNode.nodeValue;
|
||||
if (nodeValue?.startsWith(`${runtimeId}`)) {
|
||||
const nodeValue = currentNode.nodeValue!;
|
||||
if (nodeValue.startsWith(runtimeId)) {
|
||||
const token = nodeValue[runtimeLength];
|
||||
const scopeId = parseInt(nodeValue.slice(runtimeLength + 1));
|
||||
const scope = getScope(scopeId);
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import type { Scope, ScopeContext } from "../common/types";
|
||||
import type { Scope } from "../common/types";
|
||||
import { queueEffect } from "./queue";
|
||||
import type { Renderer } from "./renderer";
|
||||
|
||||
let debugID = 0;
|
||||
|
||||
export function createScope(context?: ScopeContext): Scope {
|
||||
export function createScope($global: Scope["$global"]): Scope {
|
||||
const scope = {} as Scope;
|
||||
if (MARKO_DEBUG) {
|
||||
scope.___debugId = debugID++;
|
||||
}
|
||||
scope.___client = true;
|
||||
scope.___context = context;
|
||||
scope.$global = $global;
|
||||
return scope;
|
||||
}
|
||||
|
||||
const emptyScope = createScope();
|
||||
const emptyScope = createScope({});
|
||||
export function getEmptyScope(marker?: Comment) {
|
||||
emptyScope.___startNode = emptyScope.___endNode = marker;
|
||||
return emptyScope;
|
||||
|
||||
@ -202,24 +202,6 @@ export function dynamicClosure<T>(
|
||||
});
|
||||
}
|
||||
|
||||
export function contextClosure<T>(
|
||||
valueAccessor: Accessor,
|
||||
contextKey: string,
|
||||
fn: ValueSignal<T>,
|
||||
intersection?: IntersectionSignal,
|
||||
valueWithIntersection?: ValueSignal,
|
||||
) {
|
||||
// TODO: might be viable as a reliable way to get a unique id
|
||||
// const dirtyAccessor = valueAccessor - 2;
|
||||
return dynamicClosure(
|
||||
(scope) => scope.___context![contextKey][1],
|
||||
value(valueAccessor, fn),
|
||||
(scope) => scope.___context![contextKey][0],
|
||||
intersection,
|
||||
valueWithIntersection,
|
||||
);
|
||||
}
|
||||
|
||||
export function childClosures(
|
||||
closureSignals: IntersectionSignal[],
|
||||
childAccessor: Accessor,
|
||||
|
||||
@ -22,14 +22,15 @@ export class ClientTemplate implements Template {
|
||||
}
|
||||
|
||||
mount(
|
||||
input: Input,
|
||||
templateInput: Input & { $global?: Record<string, unknown> } = {},
|
||||
reference: ParentNode & Node,
|
||||
position?: InsertPosition,
|
||||
): TemplateInstance {
|
||||
let scope!: Scope, dom!: Node;
|
||||
const { $global = {}, ...input } = templateInput;
|
||||
const attrs = this._.___attrs;
|
||||
const effects = prepare(() => {
|
||||
scope = createScope();
|
||||
scope = createScope($global);
|
||||
dom = initRenderer(this._, scope);
|
||||
if (attrs) {
|
||||
attrs(scope, input);
|
||||
|
||||
@ -107,7 +107,7 @@ function walkInternal(
|
||||
MARKO_DEBUG
|
||||
? getDebugKey(currentScopeIndex++, "#childScope")
|
||||
: currentScopeIndex++
|
||||
] = createScope(scope.___context)),
|
||||
] = createScope(scope.$global)),
|
||||
currentWalkIndex,
|
||||
)!;
|
||||
} else if (value === WalkCodes.EndChild) {
|
||||
|
||||
@ -49,7 +49,7 @@ export function createRenderFn(renderer: Renderer) {
|
||||
return (
|
||||
stream: Writable,
|
||||
input: Input = {},
|
||||
global: Record<string, unknown> = {},
|
||||
$global?: Record<string, unknown>,
|
||||
streamState: Partial<StreamData> = {},
|
||||
) => {
|
||||
let remainingChildren = 1;
|
||||
@ -67,7 +67,7 @@ export function createRenderFn(renderer: Renderer) {
|
||||
};
|
||||
|
||||
$_buffer = createInitialBuffer(stream);
|
||||
streamState.global = global;
|
||||
streamState.global = $global;
|
||||
$_streamData = createStreamState(streamState);
|
||||
|
||||
$_buffer.onReject = reject;
|
||||
@ -473,7 +473,9 @@ export function writeScope(
|
||||
scope = Object.assign(assignTo, scope);
|
||||
}
|
||||
}
|
||||
$_buffer!.scopes = $_buffer!.scopes || {};
|
||||
$_buffer!.scopes ??= {
|
||||
$global: getFilteredGlobals($_streamData!.global) as any,
|
||||
};
|
||||
$_buffer!.scopes[scopeId] = scope;
|
||||
$_streamData!.scopeLookup.set(scopeId, scope);
|
||||
}
|
||||
@ -524,3 +526,44 @@ function getResumeScript(
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function getFilteredGlobals($global: Record<string, unknown>) {
|
||||
if (!$global) return undefined;
|
||||
|
||||
const serializedGlobals = $global.serializedGlobals as
|
||||
| string[]
|
||||
| Record<string, boolean>
|
||||
| undefined;
|
||||
|
||||
if (!serializedGlobals) return undefined;
|
||||
|
||||
let filtered: undefined | Record<string, unknown>;
|
||||
|
||||
if (Array.isArray(serializedGlobals)) {
|
||||
for (const key of serializedGlobals) {
|
||||
const value = $global[key];
|
||||
if (value !== undefined) {
|
||||
if (filtered) {
|
||||
filtered[key] = value;
|
||||
} else {
|
||||
filtered = { [key]: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const key in serializedGlobals) {
|
||||
if (serializedGlobals[key]) {
|
||||
const value = $global[key];
|
||||
if (value !== undefined) {
|
||||
if (filtered) {
|
||||
filtered[key] = value;
|
||||
} else {
|
||||
filtered = { [key]: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@ -35,7 +35,6 @@ type Result = {
|
||||
};
|
||||
|
||||
type TestConfig = {
|
||||
context?: Record<string, unknown>;
|
||||
steps?: unknown[] | (() => Promise<unknown[]>);
|
||||
skip_dom?: boolean;
|
||||
skip_html?: boolean;
|
||||
@ -134,14 +133,12 @@ describe("translator-interop", () => {
|
||||
}),
|
||||
});
|
||||
const document = browser.window.document;
|
||||
const [input = {}] = (
|
||||
const [input] = (
|
||||
typeof config.steps === "function"
|
||||
? await config.steps()
|
||||
: config.steps || []
|
||||
) as [Input];
|
||||
|
||||
input.$global = config.context;
|
||||
|
||||
document.open();
|
||||
|
||||
const tracker = createMutationTracker(browser.window, document);
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
# Render {"$global":{"x":1,"serializedGlobals":["x"]}}
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
@ -0,0 +1,73 @@
|
||||
# Render {"$global":{"x":1,"serializedGlobals":["x"]}}
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted div0
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted div0/span0
|
||||
removed #text after div0/span0
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted div0/#text0
|
||||
removed span after div0/#text0
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted div0/span0
|
||||
removed #text after div0/span0
|
||||
```
|
||||
@ -0,0 +1,23 @@
|
||||
import { data as _data, on as _on, queueSource as _queueSource, createRenderer as _createRenderer, register as _register, conditional as _conditional, queueEffect as _queueEffect, value as _value, createTemplate as _createTemplate } from "@marko/runtime-tags/src/dom";
|
||||
const _setup$ifBody = _scope => {
|
||||
_data(_scope["#text/0"], _scope.$global.x);
|
||||
};
|
||||
const _ifBody = _register("packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_1_renderer", /* @__PURE__ */_createRenderer("<span> </span>", /* next(1), get */"D ", _setup$ifBody));
|
||||
const _if = /* @__PURE__ */_conditional("#text/0");
|
||||
const _show_effect = _register("packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show", _scope => _on(_scope["#button/1"], "click", function () {
|
||||
const {
|
||||
show
|
||||
} = _scope;
|
||||
_queueSource(_scope, _show, !show);
|
||||
}));
|
||||
const _show = /* @__PURE__ */_value("show", (_scope, show) => {
|
||||
_queueEffect(_scope, _show_effect);
|
||||
_if(_scope, show ? _ifBody : null);
|
||||
});
|
||||
const _setup = _scope => {
|
||||
_show(_scope, false);
|
||||
};
|
||||
export const template = "<div><!><button>Toggle</button></div>";
|
||||
export const walks = /* next(1), replace, over(1), get, out(1) */"D%b l";
|
||||
export const setup = _setup;
|
||||
export default /* @__PURE__ */_createTemplate( /* @__PURE__ */_createRenderer(template, walks, setup), "packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko");
|
||||
@ -0,0 +1,24 @@
|
||||
import { write as _write, $_streamData as _$_streamData, escapeXML as _escapeXML, markResumeNode as _markResumeNode, serializedScope as _serializedScope, writeScope as _writeScope, nextScopeId as _nextScopeId, createRenderer as _createRenderer, register as _register, markResumeControlSingleNodeEnd as _markResumeControlSingleNodeEnd, writeEffect as _writeEffect, createTemplate as _createTemplate } from "@marko/runtime-tags/src/html";
|
||||
const _renderer = /* @__PURE__ */_createRenderer((input, _tagVar) => {
|
||||
const _scope0_id = _nextScopeId();
|
||||
const show = false;
|
||||
_write("<div>");
|
||||
let _ifScopeId, _scope1_, _ifRenderer;
|
||||
if (show) {
|
||||
const _scope1_id = _nextScopeId();
|
||||
_write(`<span>${_escapeXML(_$_streamData.global.x)}${_markResumeNode(_scope1_id, "#text/0")}</span>`);
|
||||
_writeScope(_scope1_id, _scope1_ = {
|
||||
"_": _serializedScope(_scope0_id)
|
||||
});
|
||||
_register(_ifRenderer = /* @__PURE__ */_createRenderer(() => {}), "packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_1_renderer");
|
||||
_ifScopeId = _scope1_id;
|
||||
}
|
||||
_write(`${_markResumeControlSingleNodeEnd(_scope0_id, "#text/0", _ifScopeId)}<button>Toggle</button>${_markResumeNode(_scope0_id, "#button/1")}</div>`);
|
||||
_writeEffect(_scope0_id, "packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show");
|
||||
_writeScope(_scope0_id, {
|
||||
"show": show,
|
||||
"#text/0!": _scope1_,
|
||||
"#text/0(": _ifRenderer
|
||||
});
|
||||
});
|
||||
export default /* @__PURE__ */_createTemplate(_renderer, "packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko");
|
||||
@ -0,0 +1,50 @@
|
||||
# Render {"$global":{"x":1,"serializedGlobals":["x"]}}
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
@ -0,0 +1,111 @@
|
||||
# Render {"$global":{"x":1,"serializedGlobals":["x"]}}
|
||||
```html
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div>
|
||||
<!--M|0 #text/0 -->
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
<!--M*0 #button/1-->
|
||||
</div>
|
||||
<script>
|
||||
(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
<!--M*0 #button/1-->
|
||||
</div>
|
||||
<script>
|
||||
(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted #document/html0/body1/div0/span0
|
||||
removed #comment after #document/html0/body1/div0/span0
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div>
|
||||
<!--M|0 #text/0 -->
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
<!--M*0 #button/1-->
|
||||
</div>
|
||||
<script>
|
||||
(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted #document/html0/body1/div0/#comment0
|
||||
removed span after #document/html0/body1/div0/#comment0
|
||||
```
|
||||
|
||||
|
||||
# Render
|
||||
container.querySelector("button").click()
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div>
|
||||
<span>
|
||||
1
|
||||
</span>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
<!--M*0 #button/1-->
|
||||
</div>
|
||||
<script>
|
||||
(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted #document/html0/body1/div0/span0
|
||||
removed #comment after #document/html0/body1/div0/span0
|
||||
```
|
||||
@ -0,0 +1,8 @@
|
||||
# Render "End"
|
||||
```html
|
||||
<div>
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
@ -0,0 +1,36 @@
|
||||
# Write
|
||||
<div><!M|0 #text/0 ><button>Toggle</button><!M*0 #button/1></div><script>(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])</script>
|
||||
|
||||
|
||||
# Render "End"
|
||||
```html
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<div>
|
||||
<!--M|0 #text/0 -->
|
||||
<button>
|
||||
Toggle
|
||||
</button>
|
||||
<!--M*0 #button/1-->
|
||||
</div>
|
||||
<script>
|
||||
(M$h=[]).push((b,s)=>({0:{show:!1},$global:{x:1}}),[0,"packages/translator-tags/src/__tests__/fixtures/dollar-global-client/template.marko_0_show",])
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
# Mutations
|
||||
```
|
||||
inserted #document/html0
|
||||
inserted #document/html0/head0
|
||||
inserted #document/html0/body1
|
||||
inserted #document/html0/body1/div0
|
||||
inserted #document/html0/body1/div0/#comment0
|
||||
inserted #document/html0/body1/div0/button1
|
||||
inserted #document/html0/body1/div0/button1/#text0
|
||||
inserted #document/html0/body1/div0/#comment2
|
||||
inserted #document/html0/body1/script1
|
||||
inserted #document/html0/body1/script1/#text0
|
||||
```
|
||||
@ -0,0 +1,9 @@
|
||||
<div>
|
||||
<let/show=false/>
|
||||
<if=show>
|
||||
<span>${$global.x}</span>
|
||||
</if>
|
||||
<button onClick() { show = !show; }>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
@ -0,0 +1,10 @@
|
||||
export const steps = [
|
||||
{ $global: { x: 1, serializedGlobals: ["x"] } },
|
||||
click,
|
||||
click,
|
||||
click,
|
||||
];
|
||||
|
||||
function click(container: Element) {
|
||||
container.querySelector("button")!.click();
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { $_streamData as _$_streamData, data as _data, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/src/dom";
|
||||
import { data as _data, createRenderer as _createRenderer, createTemplate as _createTemplate } from "@marko/runtime-tags/src/dom";
|
||||
const _setup = _scope => {
|
||||
_data(_scope["#text/0"], _$_streamData.global.x);
|
||||
_data(_scope["#text/0"], _scope.$global.x);
|
||||
};
|
||||
export const template = "<div><span> </span></div>";
|
||||
export const walks = /* next(2), get, out(2) */"E m";
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export const steps = [{ $global: { x: 1 } }];
|
||||
export const skip_csr = true;
|
||||
export const skip_resume = true;
|
||||
export const context = { x: 1 };
|
||||
|
||||
@ -21,7 +21,6 @@ const reorderRuntimeString = String(reorderRuntime).replace(
|
||||
);
|
||||
|
||||
type TestConfig = {
|
||||
context?: Record<string, unknown>;
|
||||
steps?: unknown[] | (() => Promise<unknown[]>);
|
||||
skip_dom?: boolean;
|
||||
skip_html?: boolean;
|
||||
@ -167,8 +166,6 @@ describe("translator-tags", () => {
|
||||
: config.steps || []
|
||||
) as [Input];
|
||||
|
||||
input.$global = config.context;
|
||||
|
||||
document.open();
|
||||
|
||||
const tracker = createMutationTracker(browser.window, document);
|
||||
|
||||
@ -27,7 +27,6 @@ const pureFunctions: Array<keyof typeof import("@marko/runtime-tags/src/dom")> =
|
||||
"intersection",
|
||||
"closure",
|
||||
"dynamicClosure",
|
||||
"contextClosure",
|
||||
"loopOf",
|
||||
"loopIn",
|
||||
"loopTo",
|
||||
|
||||
@ -66,12 +66,9 @@ const enum AccessorChars {
|
||||
TAG_VARIABLE = "/",
|
||||
COND_SCOPE = "!",
|
||||
LOOP_SCOPE_ARRAY = "!",
|
||||
COND_CONTEXT = "^",
|
||||
LOOP_CONTEXT = "^",
|
||||
COND_RENDERER = "(",
|
||||
LOOP_SCOPE_MAP = "(",
|
||||
LOOP_VALUE = ")",
|
||||
CONTEXT_VALUE = ":",
|
||||
}
|
||||
|
||||
const [getSignals] = createSectionState<Map<unknown, Signal>>(
|
||||
@ -236,59 +233,6 @@ export function initValue(
|
||||
return signal;
|
||||
}
|
||||
|
||||
export function initContextProvider(
|
||||
templateId: string,
|
||||
reserve: Reserve,
|
||||
providers: References,
|
||||
compute: t.Expression,
|
||||
renderer: t.Identifier,
|
||||
) {
|
||||
const section = reserve.section;
|
||||
const scopeAccessor = getScopeAccessorLiteral(reserve);
|
||||
const valueAccessor = t.stringLiteral(
|
||||
`${reserve.id}${AccessorChars.CONTEXT_VALUE}`,
|
||||
);
|
||||
|
||||
const signal = initValue(reserve, valueAccessor);
|
||||
addValue(section, providers, signal, compute);
|
||||
signal.hasDynamicSubscribers = true;
|
||||
signal.hasDownstreamIntersections = () => true;
|
||||
|
||||
addStatement(
|
||||
"render",
|
||||
reserve.section,
|
||||
undefined,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"initContextProvider",
|
||||
scopeIdentifier,
|
||||
scopeAccessor,
|
||||
valueAccessor,
|
||||
t.stringLiteral(templateId),
|
||||
renderer,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
export function initContextConsumer(templateId: string, reserve: Reserve) {
|
||||
const section = reserve.section;
|
||||
const signal = getSignal(section, reserve);
|
||||
getClosures(section).push(signal.identifier);
|
||||
signal.build = () => {
|
||||
return callRuntime(
|
||||
"contextClosure",
|
||||
getScopeAccessorLiteral(reserve),
|
||||
t.stringLiteral(templateId),
|
||||
getSignalFn(signal, [scopeIdentifier, t.identifier(reserve.name)]),
|
||||
);
|
||||
};
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
export function getSignalFn(
|
||||
signal: Signal,
|
||||
params: Array<t.Identifier | t.Pattern>,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { types as t } from "@marko/compiler";
|
||||
import isStatic from "../util/is-static";
|
||||
import { isOutputHTML } from "../util/marko-config";
|
||||
import { importRuntime } from "../util/runtime";
|
||||
import { currentProgramPath } from "./program";
|
||||
import { currentProgramPath, scopeIdentifier } from "./program";
|
||||
|
||||
const globalImportIdentifier = new WeakMap<
|
||||
t.NodePath<t.Program>,
|
||||
@ -50,18 +51,24 @@ export default {
|
||||
switch (name) {
|
||||
case "$global":
|
||||
{
|
||||
let streamDataIdentifier =
|
||||
globalImportIdentifier.get(currentProgramPath);
|
||||
if (!streamDataIdentifier) {
|
||||
streamDataIdentifier = importRuntime("$_streamData");
|
||||
globalImportIdentifier.set(
|
||||
currentProgramPath,
|
||||
streamDataIdentifier,
|
||||
if (isOutputHTML()) {
|
||||
let streamDataIdentifier =
|
||||
globalImportIdentifier.get(currentProgramPath);
|
||||
if (!streamDataIdentifier) {
|
||||
streamDataIdentifier = importRuntime("$_streamData");
|
||||
globalImportIdentifier.set(
|
||||
currentProgramPath,
|
||||
streamDataIdentifier,
|
||||
);
|
||||
}
|
||||
identifier.replaceWith(
|
||||
t.memberExpression(streamDataIdentifier, t.identifier("global")),
|
||||
);
|
||||
} else {
|
||||
identifier.replaceWith(
|
||||
t.memberExpression(scopeIdentifier, t.identifier("$global")),
|
||||
);
|
||||
}
|
||||
identifier.replaceWith(
|
||||
t.memberExpression(streamDataIdentifier, t.identifier("global")),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user