Skip to content

Commit 4b2f175

Browse files
committed
feat(docs): add vnode tree & state to html tab
1 parent a6efe7c commit 4b2f175

File tree

11 files changed

+107
-30
lines changed

11 files changed

+107
-30
lines changed

packages/docs/src/components/code-block/code-block.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import styles from './code-block.css?inline';
1515
import { CopyCode } from '../copy-code/copy-code-block';
1616
interface CodeBlockProps {
1717
path?: string;
18-
language?: 'markup' | 'css' | 'javascript' | 'json' | 'jsx' | 'tsx';
18+
language?: 'markup' | 'css' | 'javascript' | 'json' | 'jsx' | 'tsx' | 'clike';
1919
code: string;
2020
pathInView$?: QRL<(name: string) => void>;
2121
observerRootId?: string;

packages/docs/src/repl/repl-output-panel.tsx

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,50 @@ import { ReplOutputSymbols } from './repl-output-symbols';
55
import { ReplTabButton } from './repl-tab-button';
66
import { ReplTabButtons } from './repl-tab-buttons';
77
import type { ReplAppInput, ReplStore } from './types';
8+
import { _deserialize, _getDomContainer } from '@qwik.dev/core/internal';
9+
import { dumpState, preprocessState } from '../../../qwik/src/core/shared/shared-serialization.ts';
10+
import { vnode_toString } from '../../../qwik/src/core/client/vnode.ts';
811

912
export const ReplOutputPanel = component$(({ input, store }: ReplOutputPanelProps) => {
1013
const diagnosticsLen = useComputed$(
1114
() => store.diagnostics.length + store.monacoDiagnostics.length
1215
);
1316

17+
const domContainerFromResultHtml = useComputed$(() => {
18+
const parser = new DOMParser();
19+
const doc = parser.parseFromString(store.htmlResult.rawHtml, 'text/html');
20+
return _getDomContainer(doc.documentElement);
21+
});
22+
23+
const parsedState = useComputed$(() => {
24+
const container = domContainerFromResultHtml.value;
25+
const doc = container.element;
26+
const qwikStates = doc.querySelectorAll('script[type="qwik/state"]');
27+
if (qwikStates.length !== 0) {
28+
const data = qwikStates[qwikStates.length - 1];
29+
const origState = JSON.parse(data?.textContent || '[]');
30+
preprocessState(origState, container as any);
31+
return origState
32+
? dumpState(origState, false, '', null)
33+
//remove first new line
34+
.replace(/\n/, '')
35+
: 'No state found';
36+
}
37+
return 'No state found';
38+
});
39+
40+
const vdomTree = useComputed$(() => {
41+
const container = domContainerFromResultHtml.value;
42+
return vnode_toString.call(
43+
container.rootVNode as any,
44+
Number.MAX_SAFE_INTEGER,
45+
'',
46+
true,
47+
false,
48+
false
49+
);
50+
});
51+
1452
return (
1553
<div class="repl-panel repl-output-panel">
1654
<ReplTabButtons>
@@ -103,8 +141,19 @@ export const ReplOutputPanel = component$(({ input, store }: ReplOutputPanelProp
103141
</div>
104142

105143
{store.selectedOutputPanel === 'html' ? (
106-
<div class="output-result output-html">
107-
<CodeBlock language="markup" code={store.html} />
144+
<div class="output-result output-html flex flex-col gap-2">
145+
<div>
146+
<span class="code-block-info">HTML</span>
147+
<CodeBlock language="markup" code={store.htmlResult.prettyHtml} />
148+
</div>
149+
<div>
150+
<span class="code-block-info">Parsed State</span>
151+
<CodeBlock language="clike" code={parsedState.value} />
152+
</div>
153+
<div>
154+
<span class="code-block-info">VNode Tree</span>
155+
<CodeBlock language="markup" code={vdomTree.value} />
156+
</div>
108157
</div>
109158
) : null}
110159

packages/docs/src/repl/repl-output-update.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ const matchByPath = (a: any, b: any) => a.path === b.path;
3030

3131
export const updateReplOutput = async (store: ReplStore, result: ReplResult) => {
3232
deepUpdate(store.diagnostics, result.diagnostics);
33+
if (store.htmlResult.rawHtml !== result.htmlResult.rawHtml) {
34+
store.htmlResult.rawHtml = result.htmlResult.rawHtml;
35+
store.htmlResult.prettyHtml = result.htmlResult.prettyHtml;
36+
}
3337

3438
if (result.diagnostics.length === 0) {
35-
if (store.html !== result.html) {
36-
store.html = result.html;
37-
}
38-
39+
deepUpdate(store.htmlResult, result.htmlResult);
3940
deepUpdate(store.transformedModules, result.transformedModules, matchByPath);
4041
deepUpdate(store.clientBundles, result.clientBundles, matchByPath);
4142
deepUpdate(store.ssrModules, result.ssrModules, matchByPath);

packages/docs/src/repl/repl.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@
199199
overflow: auto;
200200
}
201201

202+
.output-html .code-block-info {
203+
display: block;
204+
font-weight: bold;
205+
background-color: rgb(33 104 170 / 15%);
206+
padding: 5px 10px;
207+
overflow: hidden;
208+
text-overflow: ellipsis;
209+
}
210+
202211
.output-html pre {
203212
height: 100%;
204213
}

packages/docs/src/repl/repl.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ export const Repl = component$((props: ReplProps) => {
2727
clientId: Math.round(Math.random() * Number.MAX_SAFE_INTEGER)
2828
.toString(36)
2929
.toLowerCase(),
30-
html: '',
30+
htmlResult: {
31+
rawHtml: '',
32+
prettyHtml: '',
33+
},
3134
transformedModules: [],
3235
clientBundles: [],
3336
ssrModules: [],

packages/docs/src/repl/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface ReplInputOptions extends Omit<QwikRollupPluginOptions, 'srcDir'
2727

2828
export interface ReplStore {
2929
clientId: string;
30-
html: string;
30+
htmlResult: ReplHTMLResult;
3131
transformedModules: TransformModule[];
3232
clientBundles: ReplModuleOutput[];
3333
ssrModules: ReplModuleOutput[];
@@ -112,7 +112,7 @@ export interface ReplEvent {
112112
export interface ReplResult extends ReplMessageBase {
113113
type: 'result';
114114
buildId: number;
115-
html: string;
115+
htmlResult: ReplHTMLResult;
116116
transformedModules: TransformModule[];
117117
clientBundles: ReplModuleOutput[];
118118
ssrModules: ReplModuleOutput[];
@@ -121,9 +121,15 @@ export interface ReplResult extends ReplMessageBase {
121121
events: ReplEvent[];
122122
}
123123

124+
export interface ReplHTMLResult {
125+
rawHtml: string;
126+
prettyHtml: string;
127+
}
128+
124129
export type OutputPanel =
125130
| 'app'
126131
| 'html'
132+
| 'state'
127133
| 'symbols'
128134
| 'clientBundles'
129135
| 'serverModules'

packages/docs/src/repl/worker/app-ssr-html.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const appSsrHtml = async (options: ReplInputOptions, cache: Cache, result
8282
console.error = error;
8383
console.debug = debug;
8484

85-
result.html = ssrResult.html;
85+
result.htmlResult.rawHtml = ssrResult.html;
8686

8787
result.events.push({
8888
kind: 'pause',
@@ -94,12 +94,12 @@ export const appSsrHtml = async (options: ReplInputOptions, cache: Cache, result
9494

9595
if (options.buildMode !== 'production') {
9696
try {
97-
const html = await self.prettier?.format(result.html, {
97+
const html = await self.prettier?.format(result.htmlResult.rawHtml, {
9898
parser: 'html',
9999
plugins: self.prettierPlugins,
100100
});
101101
if (html) {
102-
result.html = html;
102+
result.htmlResult.prettyHtml = html;
103103
}
104104
} catch (e) {
105105
console.error(e);

packages/docs/src/repl/worker/app-update.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ export const appUpdate = async (
1515
type: 'result',
1616
clientId,
1717
buildId: options.buildId,
18-
html: '',
18+
htmlResult: {
19+
rawHtml: '',
20+
prettyHtml: '',
21+
},
1922
transformedModules: [],
2023
clientBundles: [],
2124
manifest: undefined,

packages/qwik/src/core/client/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export interface QDocument extends Document {
6969
* converted into separate text nodes.
7070
* - If Element: It means that the element tag attributes have not yet been read from the DOM.
7171
*
72-
* Inflation and materialization are not the same, they are two independent things.
72+
* Inflation and materialization are not the same, they are two independent things.-
7373
*
7474
* @internal
7575
*/

packages/qwik/src/core/client/vnode.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,7 +1723,8 @@ export function vnode_toString(
17231723
depth: number = 20,
17241724
offset: string = '',
17251725
materialize: boolean = false,
1726-
siblings = false
1726+
siblings = false,
1727+
colorize: boolean = true
17271728
): string {
17281729
let vnode = this;
17291730
if (depth === 0) {
@@ -1736,6 +1737,8 @@ export function vnode_toString(
17361737
return 'undefined';
17371738
}
17381739
const strings: string[] = [];
1740+
const NAME_COL_PREFIX = '\x1b[34m';
1741+
const NAME_COL_SUFFIX = '\x1b[0m';
17391742
do {
17401743
if (vnode_isTextVNode(vnode)) {
17411744
strings.push(qwikDebugToString(vnode_getText(vnode)));
@@ -1749,12 +1752,16 @@ export function vnode_toString(
17491752
}
17501753
});
17511754
const name =
1752-
VirtualTypeName[vnode_getAttr(vnode, DEBUG_TYPE) || VirtualType.Virtual] ||
1753-
VirtualTypeName[VirtualType.Virtual];
1755+
(colorize ? NAME_COL_PREFIX : '') +
1756+
(VirtualTypeName[vnode_getAttr(vnode, DEBUG_TYPE) || VirtualType.Virtual] ||
1757+
VirtualTypeName[VirtualType.Virtual]) +
1758+
(colorize ? NAME_COL_SUFFIX : '');
17541759
strings.push('<' + name + attrs.join('') + '>');
17551760
const child = vnode_getFirstChild(vnode);
17561761
child &&
1757-
strings.push(' ' + vnode_toString.call(child, depth - 1, offset + ' ', true, true));
1762+
strings.push(
1763+
' ' + vnode_toString.call(child, depth - 1, offset + ' ', true, true, colorize)
1764+
);
17581765
strings.push('</' + name + '>');
17591766
} else if (vnode_isElementVNode(vnode)) {
17601767
const tag = vnode_getElementName(vnode);
@@ -1782,7 +1789,9 @@ export function vnode_toString(
17821789
if (vnode_isMaterialized(vnode) || materialize) {
17831790
const child = vnode_getFirstChild(vnode);
17841791
child &&
1785-
strings.push(' ' + vnode_toString.call(child, depth - 1, offset + ' ', true, true));
1792+
strings.push(
1793+
' ' + vnode_toString.call(child, depth - 1, offset + ' ', true, true, colorize)
1794+
);
17861795
} else {
17871796
strings.push(' <!-- not materialized --!>');
17881797
}

0 commit comments

Comments
 (0)