diff --git a/packages/docs/src/components/repl/repl-output-panel.tsx b/packages/docs/src/components/repl/repl-output-panel.tsx
index daa5836db11..abd2c28d46e 100644
--- a/packages/docs/src/components/repl/repl-output-panel.tsx
+++ b/packages/docs/src/components/repl/repl-output-panel.tsx
@@ -1,10 +1,12 @@
import { CodeBlock } from '../code-block/code-block';
-import { ReplOutputModles } from './repl-output-modules';
+import { ReplOutputModules } from './repl-output-modules';
import { ReplTabButton } from './repl-tab-button';
import { ReplTabButtons } from './repl-tab-buttons';
import type { ReplStore } from './types';
export const ReplOutputPanel = ({ store }: ReplOutputPanelProps) => {
+ const diagnosticsLen = store.diagnostics.length + store.monacoDiagnostics.length;
+
return (
@@ -19,9 +21,9 @@ export const ReplOutputPanel = ({ store }: ReplOutputPanelProps) => {
{store.enableHtmlOutput ? (
{
- store.selectedOutputPanel = 'outputHtml';
+ store.selectedOutputPanel = 'html';
}}
/>
) : null}
@@ -46,16 +48,14 @@ export const ReplOutputPanel = ({ store }: ReplOutputPanelProps) => {
/>
) : null}
- {store.diagnostics.length > 0 ? (
- {
- store.selectedOutputPanel = 'diagnostics';
- }}
- />
- ) : null}
+ 0 ? ` (${diagnosticsLen})` : ``}`}
+ cssClass={{ 'repl-tab-diagnostics': true }}
+ isActive={store.selectedOutputPanel === 'diagnostics'}
+ onClick$={() => {
+ store.selectedOutputPanel = 'diagnostics';
+ }}
+ />
{
'output-app-active': store.selectedOutputPanel === 'app',
}}
>
-
+
- {store.selectedOutputPanel === 'outputHtml' ? (
+ {store.selectedOutputPanel === 'html' ? (
-
+
) : null}
{store.selectedOutputPanel === 'clientModules' ? (
-
+
) : null}
{store.selectedOutputPanel === 'serverModules' ? (
-
+
) : null}
{store.selectedOutputPanel === 'diagnostics' ? (
- {store.diagnostics.map((d) => (
+ {[...store.diagnostics, ...store.monacoDiagnostics].map((d) => (
{d.message}
))}
diff --git a/packages/docs/src/components/repl/repl-output-update.ts b/packages/docs/src/components/repl/repl-output-update.ts
new file mode 100644
index 00000000000..87b335c5d5a
--- /dev/null
+++ b/packages/docs/src/components/repl/repl-output-update.ts
@@ -0,0 +1,59 @@
+import type { ReplResult, ReplStore } from './types';
+
+export const updateReplOutput = async (store: ReplStore, result: ReplResult) => {
+ if (result.diagnostics.length === 0) {
+ store.html = result.html;
+ store.clientModules = result.clientModules;
+ store.ssrModules = result.ssrModules;
+
+ if (store.selectedOutputPanel === 'diagnostics' && store.monacoDiagnostics.length === 0) {
+ store.selectedOutputPanel = 'app';
+ }
+ }
+
+ store.diagnostics = result.diagnostics;
+ store.events = result.events;
+
+ if (!result.clientModules.some((m) => m.path === store.selectedClientModule)) {
+ if (result.clientModules.length > 0) {
+ store.selectedClientModule = result.clientModules[0].path;
+ } else {
+ store.selectedClientModule = '';
+ }
+ }
+
+ if (!result.ssrModules.some((m) => m.path === store.selectedSsrModule)) {
+ if (result.ssrModules.length > 0) {
+ store.selectedSsrModule = result.ssrModules[0].path;
+ } else {
+ store.selectedSsrModule = '';
+ }
+ }
+};
+
+const reapplyScripts = (elm: HTMLElement) => {
+ // adding a ${html || ''}`;
+};
diff --git a/packages/docs/src/components/repl/worker/ssr-html.ts b/packages/docs/src/components/repl/worker/ssr-html.ts
index 27ce46577ac..ceefc83b188 100644
--- a/packages/docs/src/components/repl/worker/ssr-html.ts
+++ b/packages/docs/src/components/repl/worker/ssr-html.ts
@@ -1,13 +1,19 @@
/* eslint-disable no-console */
import type { RenderToStringOptions, RenderToStringResult } from '@builder.io/qwik/server';
-import type { ReplInputOptions, ReplResult, ReplResultAttributes } from '../types';
+import type { ReplInputOptions, ReplResult } from '../types';
+import type { QwikReplContext } from './context';
import type { QwikWorkerGlobal } from './repl-service-worker';
-export const ssrHtml = async (options: ReplInputOptions, result: ReplResult) => {
+export const ssrHtml = async (
+ options: ReplInputOptions,
+ ctx: QwikReplContext,
+ result: ReplResult
+) => {
const ssrModule = result.ssrModules.find((m) => m.path.endsWith('.js'));
if (!ssrModule || typeof ssrModule.code !== 'string') {
return;
}
+ const start = performance.now();
const mod: any = { exports: {} };
const run = new Function('module', 'exports', 'require', ssrModule.code);
@@ -19,47 +25,93 @@ export const ssrHtml = async (options: ReplInputOptions, result: ReplResult) =>
}
console.time(`SSR Html`);
+
+ const log = console.log;
+ const warn = console.warn;
+ const error = console.error;
+ const debug = console.debug;
+
+ console.log = (...args) => {
+ result.events.push({
+ kind: 'console-log',
+ scope: 'ssr',
+ message: args.join(' '),
+ start: performance.now(),
+ });
+ log(...args);
+ };
+
+ console.warn = (...args) => {
+ result.events.push({
+ kind: 'console-warn',
+ scope: 'ssr',
+ message: args.join(' '),
+ start: performance.now(),
+ });
+ warn(...args);
+ };
+
+ console.error = (...args) => {
+ result.events.push({
+ kind: 'console-error',
+ scope: 'ssr',
+ message: args.join(' '),
+ start: performance.now(),
+ });
+ error(...args);
+ };
+
+ console.debug = (...args) => {
+ result.events.push({
+ kind: 'console-debug',
+ scope: 'ssr',
+ message: args.join(' '),
+ start: performance.now(),
+ });
+ debug(...args);
+ };
+
const ssrResult = await server.render({
- base: `/repl/${options.clientId}/build/`,
+ base: `/repl/${result.clientId}/build/`,
manifest: result.manifest,
});
- const doc = self.qwikServer?._createDocument({ html: ssrResult.html });
- if (doc) {
- const qwikLoader = doc.getElementById('qwikloader');
- if (qwikLoader) {
- qwikLoader.remove();
- result.qwikloader = qwikLoader.innerHTML;
- }
+ console.log = log;
+ console.warn = warn;
+ console.error = error;
+ console.debug = debug;
- getAttributes(doc.documentElement, result.docElementAttributes);
- getAttributes(doc.head, result.headAttributes);
- getAttributes(doc.body, result.bodyAttributes);
- result.bodyInnerHtml = doc.body.innerHTML;
+ result.html = ssrResult.html;
- if (options.buildMode !== 'production') {
- const outputHtml = self.prettier?.format(ssrResult.html, {
+ result.events.push({
+ kind: 'pause',
+ scope: 'build',
+ start,
+ end: performance.now(),
+ message: '',
+ });
+
+ if (options.buildMode !== 'production') {
+ try {
+ const html = self.prettier?.format(result.html, {
parser: 'html',
plugins: self.prettierPlugins,
});
- if (outputHtml) {
- result.outputHtml = outputHtml;
+ if (html) {
+ result.html = html;
}
- } else {
- result.outputHtml = ssrResult.html;
+ } catch (e) {
+ console.error(e);
}
}
+
+ ctx.html = result.html;
+
console.timeEnd(`SSR Html`);
};
const noopRequire = (path: string) => {
- console.error(`require() not available from REPL SSR, path: ${path}`);
-};
-
-const getAttributes = (elm: HTMLElement, attrs: ReplResultAttributes) => {
- for (let i = 0; i < elm.attributes.length; i++) {
- attrs[elm.attributes[i].nodeName] = elm.attributes[i].nodeValue!;
- }
+ console.debug(`require() not available from REPL SSR, path: ${path}`);
};
interface ServerModule {
diff --git a/packages/docs/src/components/repl/worker/update.ts b/packages/docs/src/components/repl/worker/update.ts
index 9f46c427ddc..66622b833fe 100644
--- a/packages/docs/src/components/repl/worker/update.ts
+++ b/packages/docs/src/components/repl/worker/update.ts
@@ -1,43 +1,37 @@
/* eslint-disable no-console */
import type { InputOptions, OutputAsset, OutputChunk } from 'rollup';
import type { Diagnostic, QwikRollupPluginOptions } from '@builder.io/qwik/optimizer';
-import type { ReplInputOptions, ReplModuleOutput, ReplResult } from '../types';
+import type { ReplInputOptions, ReplModuleInput, ReplModuleOutput, ReplResult } from '../types';
import { getCtx, QwikReplContext } from './context';
import { loadDependencies } from './dependencies';
import { ssrHtml } from './ssr-html';
import type { QwikWorkerGlobal } from './repl-service-worker';
-import { replResolver } from './repl-resolver';
-import { replMinify } from './repl-minify';
+import { replCss, replMinify, replResolver } from './repl-plugins';
+import { sendMessageToReplServer } from './repl-messenger';
-export const update = async (options: ReplInputOptions) => {
- console.time('Update');
+export const update = async (clientId: string, options: ReplInputOptions) => {
+ console.time(`Update (${options.buildId})`);
const result: ReplResult = {
type: 'result',
- clientId: options.clientId,
- outputHtml: '',
+ clientId,
+ buildId: options.buildId,
+ html: '',
clientModules: [],
manifest: undefined,
ssrModules: [],
diagnostics: [],
- docElementAttributes: {},
- headAttributes: {},
- bodyAttributes: {},
- bodyInnerHtml: '',
- qwikloader: '',
+ events: [],
};
try {
- const ctx = getCtx(options.clientId);
+ const ctx = getCtx(clientId, true)!;
await loadDependencies(options);
await bundleClient(options, ctx, result);
await bundleSSR(options, ctx, result);
-
- await ssrHtml(options, result);
-
- ctx.clientModules = result.clientModules;
+ await ssrHtml(options, ctx, result);
} catch (e: any) {
result.diagnostics.push({
scope: 'runtime',
@@ -51,9 +45,9 @@ export const update = async (options: ReplInputOptions) => {
console.error(e);
}
- await sendMessageToIframe(result);
+ await sendMessageToReplServer(result);
- console.timeEnd('Update');
+ console.timeEnd(`Update (${options.buildId})`);
};
const bundleClient = async (
@@ -61,13 +55,14 @@ const bundleClient = async (
ctx: QwikReplContext,
result: ReplResult
) => {
- console.time(`Bundle client`);
+ const start = performance.now();
+ console.time(`Bundle client (${options.buildId})`);
const qwikRollupClientOpts: QwikRollupPluginOptions = {
target: 'client',
buildMode: options.buildMode,
debug: options.debug,
- srcInputs: options.srcInputs,
+ srcInputs: getInputs(options),
entryStrategy: options.entryStrategy,
manifestOutput: (m) => {
result.manifest = m;
@@ -82,8 +77,9 @@ const bundleClient = async (
const rollupInputOpts: InputOptions = {
input: entry.path,
- cache: ctx.clientCache,
+ cache: ctx.rollupCache,
plugins: [
+ replCss(options),
self.qwikOptimizer?.qwikRollup(qwikRollupClientOpts),
replResolver(options, 'client'),
replMinify(options),
@@ -116,7 +112,7 @@ const bundleClient = async (
const bundle = await self.rollup?.rollup(rollupInputOpts);
if (bundle) {
- ctx.clientCache = bundle.cache;
+ ctx.rollupCache = bundle.cache;
const generated = await bundle.generate({
sourcemap: false,
@@ -125,19 +121,29 @@ const bundleClient = async (
result.clientModules = generated.output.map(getOutput).filter((f) => {
return !f.path.endsWith('app.js') && !f.path.endsWith('q-manifest.json');
});
+
+ ctx.clientModules = result.clientModules;
}
- console.timeEnd(`Bundle client`);
+ result.events.push({
+ kind: 'console-log',
+ scope: 'build',
+ start,
+ end: performance.now(),
+ message: 'Build Client',
+ });
+ console.timeEnd(`Bundle client (${options.buildId})`);
};
const bundleSSR = async (options: ReplInputOptions, ctx: QwikReplContext, result: ReplResult) => {
- console.time(`Bundle ssr`);
+ console.time(`Bundle SSR (${options.buildId})`);
+ const start = performance.now();
const qwikRollupSsrOpts: QwikRollupPluginOptions = {
target: 'ssr',
buildMode: options.buildMode,
debug: options.debug,
- srcInputs: options.srcInputs,
+ srcInputs: getInputs(options),
entryStrategy: options.entryStrategy,
manifestInput: result.manifest,
};
@@ -151,8 +157,12 @@ const bundleSSR = async (options: ReplInputOptions, ctx: QwikReplContext, result
const rollupInputOpts: InputOptions = {
input: entry.path,
- cache: ctx.ssrCache,
- plugins: [self.qwikOptimizer?.qwikRollup(qwikRollupSsrOpts), replResolver(options, 'ssr')],
+ cache: ctx.rollupCache,
+ plugins: [
+ replCss(options),
+ self.qwikOptimizer?.qwikRollup(qwikRollupSsrOpts),
+ replResolver(options, 'ssr'),
+ ],
onwarn(warning) {
const diagnostic: Diagnostic = {
scope: 'rollup-ssr',
@@ -181,7 +191,7 @@ const bundleSSR = async (options: ReplInputOptions, ctx: QwikReplContext, result
const bundle = await self.rollup?.rollup(rollupInputOpts);
if (bundle) {
- ctx.ssrCache = bundle.cache;
+ ctx.rollupCache = bundle.cache;
const generated = await bundle.generate({
format: 'cjs',
@@ -192,31 +202,23 @@ const bundleSSR = async (options: ReplInputOptions, ctx: QwikReplContext, result
result.ssrModules = generated.output.map(getOutput);
}
- console.timeEnd(`Bundle ssr`);
+ result.events.push({
+ kind: 'console-log',
+ scope: 'build',
+ start,
+ end: performance.now(),
+ message: 'Build SSR',
+ });
+ console.timeEnd(`Bundle SSR (${options.buildId})`);
};
-const sendMessageToIframe = async (result: ReplResult) => {
- const clients = await (self as any).clients.matchAll();
- clients.forEach((client: WindowClient) => {
- if (client.url) {
- const url = new URL(client.url);
- const clientId = url.hash.split('#')[1];
- if (clientId === result.clientId) {
- client.postMessage(result);
- }
- }
+const getInputs = (options: ReplInputOptions) => {
+ return options.srcInputs.filter((i) => {
+ return MODULE_EXTS.some((ext) => i.path.endsWith(ext));
});
};
-interface WindowClient {
- focused: boolean;
- frameType: 'nested';
- id: string;
- type: 'window';
- url: string;
- visibilityState: string;
- postMessage: (result: ReplResult) => void;
-}
+const MODULE_EXTS = ['.tsx', '.ts', '.js', '.jsx', '.mjs'];
const getOutput = (o: OutputChunk | OutputAsset) => {
const f: ReplModuleOutput = {
diff --git a/packages/docs/src/components/svgs/qwik-logo.tsx b/packages/docs/src/components/svgs/qwik-logo.tsx
index 0d54e23c3b2..3ea6c5b0eb0 100644
--- a/packages/docs/src/components/svgs/qwik-logo.tsx
+++ b/packages/docs/src/components/svgs/qwik-logo.tsx
@@ -5,65 +5,63 @@ interface QwikLogoProps {
export const QwikLogo = ({ width, height }: QwikLogoProps) => (
);
diff --git a/packages/docs/src/entry.cloudflare.tsx b/packages/docs/src/entry.cloudflare.tsx
index 02c33580e14..40a6806dac7 100644
--- a/packages/docs/src/entry.cloudflare.tsx
+++ b/packages/docs/src/entry.cloudflare.tsx
@@ -5,7 +5,15 @@ export const onRequestGet: PagesFunction = async ({ request, next, waitUntil })
const url = new URL(request.url);
if (url.hostname === 'qwik.builder.io' && url.pathname === '/') {
// temporarily redirect homepage to the overview page
- return Response.redirect('https://qwik.builder.io/docs/overview', 302);
+ return Response.redirect(new URL('/docs/overview', url), 302);
+ }
+
+ if (url.pathname === '/examples') {
+ return Response.redirect(new URL('/examples/introduction/hello-world', url));
+ }
+
+ if (url.pathname === '/tutorial') {
+ return Response.redirect(new URL('/tutorial/introduction/basics', url));
}
if (url.pathname === '/chat') {
@@ -13,7 +21,7 @@ export const onRequestGet: PagesFunction = async ({ request, next, waitUntil })
}
// Handle static assets
- if (/\.\w+$/.test(url.pathname) || url.pathname === '/repl/') {
+ if (/\.\w+$/.test(url.pathname) || url.pathname.endsWith('/repl-server')) {
return next(request);
}
diff --git a/packages/docs/src/layouts/examples/examples-data.ts b/packages/docs/src/layouts/examples/examples-data.ts
index 99900863b0d..8c58ceb923a 100644
--- a/packages/docs/src/layouts/examples/examples-data.ts
+++ b/packages/docs/src/layouts/examples/examples-data.ts
@@ -1,12 +1,19 @@
import type { ReplModuleInput } from '../../components/repl/types';
-export interface ExampleApp {
+export interface ExampleSection {
+ id: string;
title: string;
+ apps: ExampleApp[];
+}
+
+export interface ExampleApp {
id: string;
+ title: string;
description: string;
inputs: ReplModuleInput[];
}
// generated at build-time
-const apps: ExampleApp[] = [];
-export default apps;
+// see /docs/pages/examples/examples-menu.json
+const exampleSections: ExampleSection[] = [];
+export default exampleSections;
diff --git a/packages/docs/src/layouts/examples/examples.css b/packages/docs/src/layouts/examples/examples.css
index ad0c8d8aa1f..02035c10760 100644
--- a/packages/docs/src/layouts/examples/examples.css
+++ b/packages/docs/src/layouts/examples/examples.css
@@ -24,6 +24,16 @@
padding: 20px;
}
+.examples-menu-section {
+ margin: 0 0 20px 0;
+}
+
+.examples-menu-section h2 {
+ font-weight: 700;
+ margin: 0 0 5px 0;
+ color: #494949;
+}
+
.example-button {
display: block;
padding: 10px;
@@ -33,6 +43,7 @@
border: 1px solid #cccccc;
border-radius: 5px;
width: 100%;
+ font-size: 12px;
}
.example-button.selected {
@@ -43,7 +54,7 @@
background: #f3f3f3 !important;
}
-.example-button > h2 {
+.example-button h3 {
font-weight: 700;
}
@@ -53,7 +64,7 @@
grid-template-areas:
'repl-input-panel'
'repl-output-panel';
- height: calc(100vh - var(--header-height) - calc(var(--repl-tab-height) - 14px));
+ height: calc(100vh - var(--header-height));
}
.examples .repl .repl-detail-panel {
diff --git a/packages/docs/src/layouts/examples/examples.tsx b/packages/docs/src/layouts/examples/examples.tsx
index 21c36343e40..2795c7aa531 100644
--- a/packages/docs/src/layouts/examples/examples.tsx
+++ b/packages/docs/src/layouts/examples/examples.tsx
@@ -6,29 +6,27 @@ import {
useWatch$,
useStore,
} from '@builder.io/qwik';
-
import { Repl } from '../../components/repl/repl';
import styles from './examples.css?inline';
import { Header } from '../../components/header/header';
import { setHeadMeta, setHeadStyles } from '@builder.io/qwik-city';
-import exampleApps, { ExampleApp } from '@examples-data';
+import exampleSections, { ExampleApp } from '@examples-data';
-const Examples = component$(() => {
+const Examples = component$((props: ExamplesProp) => {
const hostElm = useHostElement();
const store = useStore
(() => {
+ // /examples/section/app-id
+ const app = getExampleApp(props.appId);
return {
- appId: 'hello-world',
- app: loadPlaygroundStore('hello-world')!,
+ appId: props.appId,
+ app,
};
});
useWatch$((track) => {
const appId = track(store, 'appId');
- const newApp = loadPlaygroundStore(appId);
- if (newApp) {
- store.app = newApp;
- }
+ store.app = getExampleApp(appId);
});
useWatch$(() => {
@@ -48,44 +46,66 @@ const Examples = component$(() => {
);
});
-export function loadPlaygroundStore(id: string): ExampleApp | undefined {
- return exampleApps.find((p) => p.id === id)!;
+export const getExampleApp = (id: string): ExampleApp | undefined => {
+ for (const exampleSection of exampleSections) {
+ for (const app of exampleSection.apps) {
+ if (app.id === id) {
+ return app;
+ }
+ }
+ }
+};
+
+interface ExamplesProp {
+ appId: string;
}
interface ExamplesStore {
appId: string;
- app: ExampleApp;
+ app: ExampleApp | undefined;
}
export default Examples;
diff --git a/packages/docs/src/layouts/playground/playground-data.ts b/packages/docs/src/layouts/playground/playground-data.ts
index 2f11948d8be..4677195366e 100644
--- a/packages/docs/src/layouts/playground/playground-data.ts
+++ b/packages/docs/src/layouts/playground/playground-data.ts
@@ -1,11 +1,9 @@
import type { ReplModuleInput } from '../../components/repl/types';
export interface PlaygroundApp {
- title: string;
- id: string;
inputs: ReplModuleInput[];
}
// generated at build-time
-const apps: PlaygroundApp[] = [];
-export default apps;
+const playgroundApp: PlaygroundApp = {} as any;
+export default playgroundApp;
diff --git a/packages/docs/src/layouts/playground/playground.tsx b/packages/docs/src/layouts/playground/playground.tsx
index 9c2778f96d7..0a378415923 100644
--- a/packages/docs/src/layouts/playground/playground.tsx
+++ b/packages/docs/src/layouts/playground/playground.tsx
@@ -6,14 +6,13 @@ import {
useScopedStyles$,
useWatch$,
useStore,
- useClientEffect$,
} from '@builder.io/qwik';
import type { TransformModuleInput } from '@builder.io/qwik/optimizer';
import { Repl } from '../../components/repl/repl';
import styles from './playground.css?inline';
import { Header } from '../../components/header/header';
import { setHeadMeta, setHeadStyles } from '@builder.io/qwik-city';
-import playgroundApps from '@playground-data';
+import playgroundApp from '@playground-data';
import { useLocation } from '../../utils/useLocation';
const Playground = component$(() => {
@@ -82,28 +81,17 @@ const Playground = component$(() => {
export function loadPlaygroundStore(hash: string) {
const playgroundStore: PlaygroundStore = {
- appId: 'hello-world',
- inputs: [],
+ inputs: playgroundApp.inputs,
version: '',
buildMode: 'development',
entryStrategy: 'hook',
colResizeActive: false,
colLeft: 50,
};
-
- let app = playgroundApps.find((p) => p.id === playgroundStore.appId)!;
- if (!app) {
- app = playgroundApps.find((p) => p.id === 'hello-world')!;
- }
-
- playgroundStore.appId = app.id;
- playgroundStore.inputs = app.inputs;
-
return playgroundStore;
}
interface PlaygroundStore {
- appId: string;
inputs: TransformModuleInput[];
version: string;
buildMode: 'development' | 'production';
diff --git a/packages/docs/src/layouts/tutorial/tutorial-content-header.tsx b/packages/docs/src/layouts/tutorial/tutorial-content-header.tsx
index 50cc44c808b..8557e0e6e59 100644
--- a/packages/docs/src/layouts/tutorial/tutorial-content-header.tsx
+++ b/packages/docs/src/layouts/tutorial/tutorial-content-header.tsx
@@ -25,7 +25,7 @@ export const TutorialContentHeader = ({ current }: TutorialContentHeaderProps) =
>
{tutorialSections.map((s) => (