Skip to content

Commit 166f461

Browse files
authored
Merge pull request #7926 from QwikDev/repl-fix
fix(repl): various
2 parents 66b9739 + 35bb7ff commit 166f461

File tree

8 files changed

+114
-24
lines changed

8 files changed

+114
-24
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { QwikSymbolEvent } from '@builder.io/qwik';
2+
import type { ReplEvent } from '../types';
3+
4+
(() => {
5+
const replId = location.pathname.split('/')[2];
6+
const origConsole: Record<string, any> = {};
7+
8+
const sendToServerWindow = (data: Omit<ReplEvent, 'start'>) => {
9+
try {
10+
parent.postMessage({
11+
type: 'event',
12+
replId,
13+
event: { ...data, start: performance.now() },
14+
});
15+
} catch {
16+
// ignore
17+
}
18+
};
19+
20+
const wrapConsole = (kind: 'log' | 'warn' | 'error' | 'debug') => {
21+
origConsole[kind] = console[kind];
22+
console[kind] = (...args: any[]) => {
23+
sendToServerWindow({
24+
kind: `console-${kind}` as any,
25+
scope: 'client',
26+
message: args.map((a) => String(a)),
27+
});
28+
origConsole[kind](...args);
29+
};
30+
};
31+
wrapConsole('log');
32+
wrapConsole('warn');
33+
wrapConsole('error');
34+
// wrapConsole('debug');
35+
36+
document.addEventListener('qsymbol', (ev) => {
37+
const customEv: QwikSymbolEvent = ev as any;
38+
const symbolName = customEv.detail?.symbol;
39+
sendToServerWindow({
40+
kind: 'symbol',
41+
scope: 'client',
42+
message: [symbolName],
43+
});
44+
});
45+
46+
document.addEventListener('qresume', () => {
47+
sendToServerWindow({
48+
kind: 'resume',
49+
scope: 'client',
50+
message: [''],
51+
});
52+
});
53+
54+
// Ensure all external links open in a new tab
55+
document.addEventListener(
56+
'click',
57+
(ev) => {
58+
try {
59+
if (ev.target && (ev.target as Element).tagName === 'A') {
60+
const anchor = ev.target as HTMLAnchorElement;
61+
const href = anchor.href;
62+
if (href && href !== '#') {
63+
const url = new URL(anchor.href, origin);
64+
if (url.origin !== origin) {
65+
anchor.setAttribute('target', '_blank');
66+
}
67+
}
68+
}
69+
} catch (e) {
70+
console.error('repl-request-handler', e);
71+
}
72+
},
73+
true
74+
);
75+
})();

packages/docs/src/repl/bundler/repl-ssr-worker.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SSR Worker - handles server-side rendering execution
22
// MUST be served from /repl/ so that its imports are intercepted by the REPL service worker
33
import type { QwikManifest } from '@builder.io/qwik/optimizer';
4+
// @ts-expect-error
5+
import listenerScript from './client-events-listener?compiled-string';
46

57
// Worker message types
68
interface MessageBase {
@@ -98,13 +100,11 @@ async function executeSSR(message: InitSSRMessage): Promise<{ html: string; even
98100
base: baseUrl,
99101
manifest,
100102
prefetchStrategy: null,
101-
}).catch((e: any) => {
102-
console.error('SSR failed', e);
103-
return {
104-
html: `<html><h1>SSR Error</h1><pre><code>${String(e).replaceAll('<', '&lt;')}</code></pre></html>`,
105-
};
106103
});
107104

105+
// Inject the event listener script
106+
ssrResult.html = ssrResult.html.replace('</body>', `<script>${listenerScript}</script></body>`);
107+
108108
// Restore console methods
109109
console.log = orig.log;
110110
console.warn = orig.warn;

packages/docs/src/repl/ui/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
useTask$,
77
useVisibleTask$,
88
$,
9+
useOnWindow,
910
} from '@builder.io/qwik';
1011
import { ReplInputPanel } from './repl-input-panel';
1112
import { ReplOutputPanel } from './repl-output-panel';
@@ -103,6 +104,21 @@ export const Repl = component$((props: ReplProps) => {
103104
store.instance?.markDirty();
104105
});
105106

107+
// Messages from ../bundler/client-events-listener.ts
108+
useOnWindow(
109+
'message',
110+
$((event: MessageEvent) => {
111+
if (
112+
event.data &&
113+
event.data.type === 'event' &&
114+
event.data.replId === store.replId &&
115+
event.data.event
116+
) {
117+
store.events.push(event.data.event);
118+
}
119+
})
120+
);
121+
106122
return (
107123
<>
108124
<ReplInputPanel

packages/docs/src/repl/ui/repl-console.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ export interface ReplConsoleProps {
77
export const ReplConsole = component$(({ store }: ReplConsoleProps) => {
88
return (
99
<div class="detail-logs">
10-
{store.events.filter(Boolean).map((ev) => (
11-
<ReplLog log={ev} key={ev.start} />
10+
{store.events.map((ev, i) => (
11+
<ReplLog log={ev} key={i} />
1212
))}
1313
</div>
1414
);

packages/docs/src/repl/ui/repl-instance.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,12 @@ export class ReplInstance {
142142
const status = fileContent === null ? 404 : error ? 500 : 200;
143143
const statusText =
144144
status === 200 ? 'OK' : status === 404 ? 'Not Found' : 'Internal Server Error';
145-
const headers: Record<string, string> =
146-
status === 200
147-
? {
148-
'Content-Type': this.getContentType(url),
149-
'Cache-Control': 'no-store, no-cache, max-age=0',
150-
}
151-
: {};
145+
const headers: Record<string, string> = {
146+
'Cache-Control': 'no-store, no-cache, max-age=0',
147+
};
148+
if (status === 200) {
149+
headers['Content-Type'] = this.getContentType(url);
150+
}
152151

153152
const message: ResponseMessage = {
154153
type: 'repl-response',
@@ -273,7 +272,7 @@ export class ReplInstance {
273272
};
274273

275274
ssrWorker.onerror = (error) => {
276-
resolve({ html: errorHtml(error, 'SSR ') });
275+
resolve({ html: errorHtml('Worker failed to load', 'SSR ') });
277276
ssrWorker.terminate();
278277
};
279278
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { component$ } from '@builder.io/qwik';
22
import { CodeBlock } from '../../components/code-block/code-block';
3+
import type { ReplAppInput, ReplStore } from '../types';
34
import { ReplOutputModules } from './repl-output-modules';
45
import { ReplOutputSymbols } from './repl-output-symbols';
56
import { ReplTabButton } from './repl-tab-button';
67
import { ReplTabButtons } from './repl-tab-buttons';
7-
import type { ReplAppInput, ReplStore } from '../types';
88

99
export const ReplOutputPanel = component$(({ input, store }: ReplOutputPanelProps) => {
1010
const diagnosticsLen = store.diagnostics.length + store.monacoDiagnostics.length;

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,10 @@ export const updateReplOutput = async (store: ReplStore, result: ReplResult) =>
3131
deepUpdate(store.clientBundles, result.clientBundles);
3232
deepUpdate(store.ssrModules, result.ssrModules);
3333

34-
if (
35-
result.events.length !== store.events.length ||
36-
result.events.some((ev, i) => ev?.start !== store.events[i]?.start)
37-
) {
38-
store.events = result.events;
39-
}
40-
4134
if (result.diagnostics.length === 0) {
4235
if (result.html && store.html !== result.html) {
4336
store.html = result.html;
37+
store.events = result.events;
4438
store.reload++;
4539
}
4640
}

scripts/compiled-string-plugin.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Plugin } from 'vite';
2+
import { minify } from 'terser';
23

34
const isCompiledStringId = (id: string) => /[?&]compiled-string/.test(id);
45

@@ -55,8 +56,13 @@ export function compiledStringPlugin(): Plugin {
5556
if (!code!) {
5657
throw new Error(`Unable to load code for ${originalId}`);
5758
}
59+
const minified = await minify(code);
60+
if (!minified.code) {
61+
throw new Error(`Unable to minify code for ${originalId}`);
62+
}
63+
const withoutExports = minified.code.replace('export{}', '').replace(/;+$/g, '');
5864
return {
59-
code: `export default ${JSON.stringify(code)};`,
65+
code: `export default ${JSON.stringify(withoutExports)};`,
6066
map: null,
6167
};
6268
}

0 commit comments

Comments
 (0)