diff --git a/packages/docs/src/entry.ssr.tsx b/packages/docs/src/entry.ssr.tsx
index 74385c84d9c..7840ad731d3 100644
--- a/packages/docs/src/entry.ssr.tsx
+++ b/packages/docs/src/entry.ssr.tsx
@@ -5,9 +5,6 @@ import { Root } from './root';
export function render(opts: RenderToStringOptions) {
return renderToString(, {
manifest,
- qwikLoader: {
- include: 'body',
- },
...opts,
});
}
diff --git a/packages/qwik/src/server/api.md b/packages/qwik/src/server/api.md
index b425fda5d9f..22999df59ba 100644
--- a/packages/qwik/src/server/api.md
+++ b/packages/qwik/src/server/api.md
@@ -131,7 +131,7 @@ export interface RenderToStringOptions extends SerializeDocumentOptions {
prefetchStrategy?: PrefetchStrategy | null;
qwikLoader?: {
events?: string[];
- include?: boolean | 'body' | 'head';
+ include?: boolean | 'top' | 'bottom';
};
snapshot?: boolean;
}
diff --git a/packages/qwik/src/server/prefetch-implementation.ts b/packages/qwik/src/server/prefetch-implementation.ts
index d491d32c1dc..94a999011dd 100644
--- a/packages/qwik/src/server/prefetch-implementation.ts
+++ b/packages/qwik/src/server/prefetch-implementation.ts
@@ -7,6 +7,7 @@ import type {
export function applyPrefetchImplementation(
doc: QwikDocument,
+ parentElm: Element,
opts: RenderToStringOptions,
prefetchResources: PrefetchResource[]
) {
@@ -19,21 +20,22 @@ export function applyPrefetchImplementation(
prefetchImpl === 'link-preload-html' ||
prefetchImpl === 'link-modulepreload-html'
) {
- linkHtmlImplementation(doc, prefetchResources, prefetchImpl);
+ linkHtmlImplementation(doc, parentElm, prefetchResources, prefetchImpl);
} else if (
prefetchImpl === 'link-prefetch' ||
prefetchImpl === 'link-preload' ||
prefetchImpl === 'link-modulepreload'
) {
- linkJsImplementation(doc, prefetchResources, prefetchImpl);
+ linkJsImplementation(doc, parentElm, prefetchResources, prefetchImpl);
} else if (prefetchImpl === 'worker-fetch') {
- workerFetchImplementation(doc, prefetchResources);
+ workerFetchImplementation(doc, parentElm, prefetchResources);
}
}
}
function linkHtmlImplementation(
doc: QwikDocument,
+ parentElm: Element,
prefetchResources: PrefetchResource[],
prefetchImpl: PrefetchImplementation
) {
@@ -57,12 +59,13 @@ function linkHtmlImplementation(
}
}
- doc.body.appendChild(linkElm);
+ parentElm.appendChild(linkElm);
}
}
function linkJsImplementation(
doc: QwikDocument,
+ parentElm: Element,
prefetchResources: PrefetchResource[],
prefetchImpl: PrefetchImplementation
) {
@@ -103,7 +106,7 @@ function linkJsImplementation(
const script = doc.createElement('script');
script.setAttribute('type', 'module');
script.innerHTML = s;
- doc.body.appendChild(script);
+ parentElm.appendChild(script);
}
function workerFetchScript() {
@@ -122,14 +125,18 @@ function workerFetchScript() {
return s;
}
-function workerFetchImplementation(doc: QwikDocument, prefetchResources: PrefetchResource[]) {
+function workerFetchImplementation(
+ doc: QwikDocument,
+ parentElm: Element,
+ prefetchResources: PrefetchResource[]
+) {
let s = `const u=${JSON.stringify(flattenPrefetchResources(prefetchResources))};`;
s += workerFetchScript();
const script = doc.createElement('script');
script.setAttribute('type', 'module');
script.innerHTML = s;
- doc.body.appendChild(script);
+ parentElm.appendChild(script);
}
export function flattenPrefetchResources(prefetchResources: PrefetchResource[]) {
diff --git a/packages/qwik/src/server/render.ts b/packages/qwik/src/server/render.ts
index 840844d4a54..6c054699ce3 100644
--- a/packages/qwik/src/server/render.ts
+++ b/packages/qwik/src/server/render.ts
@@ -11,6 +11,7 @@ import { getPrefetchResources } from './prefetch-strategy';
import { _createDocument } from './document';
import type { SymbolMapper } from '../optimizer/src/types';
import { getCanonicalSymbol } from '../core/import/qrl-class';
+import { isDocument } from '../core/util/element';
/**
* Creates a server-side `document`, renders to root node to the document,
@@ -19,43 +20,45 @@ import { getCanonicalSymbol } from '../core/import/qrl-class';
*/
export async function renderToString(rootNode: any, opts: RenderToStringOptions = {}) {
const createDocTimer = createTimer();
- const doc = _createDocument(opts);
+ const doc = _createDocument(opts) as Document;
const createDocTime = createDocTimer();
-
const renderDocTimer = createTimer();
- let rootEl: Element | Document = doc;
+ let root: Element | Document = doc;
if (typeof opts.fragmentTagName === 'string') {
if (opts.qwikLoader) {
- opts.qwikLoader.include = false;
+ if (opts.qwikLoader.include === undefined) {
+ opts.qwikLoader.include = false;
+ }
} else {
opts.qwikLoader = { include: false };
}
- rootEl = doc.createElement(opts.fragmentTagName);
- doc.body.appendChild(rootEl);
+ root = doc.createElement(opts.fragmentTagName);
+ doc.body.appendChild(root);
}
+ const isFullDocument = isDocument(root);
const mapper = computeSymbolMapper(opts.manifest);
-
await setServerPlatform(doc, opts, mapper);
- await render(doc, rootNode);
+ await render(root, rootNode);
const renderDocTime = renderDocTimer();
const buildBase = getBuildBase(opts);
- const containerEl = getElement(doc);
+ const containerEl = getElement(root);
containerEl.setAttribute('q:base', buildBase);
let snapshotResult: SnapshotResult | null = null;
if (opts.snapshot !== false) {
- snapshotResult = pauseContainer(doc);
+ snapshotResult = pauseContainer(root);
}
const prefetchResources = getPrefetchResources(snapshotResult, opts, mapper);
+ const parentElm = isFullDocument ? doc.body : containerEl;
if (prefetchResources.length > 0) {
- applyPrefetchImplementation(doc, opts, prefetchResources);
+ applyPrefetchImplementation(doc, parentElm, opts, prefetchResources);
}
const includeLoader =
- !opts.qwikLoader || opts.qwikLoader.include === undefined ? 'head' : opts.qwikLoader.include;
+ !opts.qwikLoader || opts.qwikLoader.include === undefined ? 'bottom' : opts.qwikLoader.include;
if (includeLoader) {
const qwikLoaderScript = getQwikLoaderScript({
events: opts.qwikLoader?.events,
@@ -64,10 +67,12 @@ export async function renderToString(rootNode: any, opts: RenderToStringOptions
const scriptElm = doc.createElement('script') as HTMLScriptElement;
scriptElm.setAttribute('id', 'qwikloader');
scriptElm.textContent = qwikLoaderScript;
- if (includeLoader === 'body') {
- doc.body.appendChild(scriptElm);
- } else {
+ if (includeLoader === 'bottom') {
+ parentElm.appendChild(scriptElm);
+ } else if (isFullDocument) {
doc.head.appendChild(scriptElm);
+ } else {
+ parentElm.insertBefore(scriptElm, parentElm.firstChild);
}
}
@@ -76,7 +81,7 @@ export async function renderToString(rootNode: any, opts: RenderToStringOptions
const result: RenderToStringResult = {
prefetchResources,
snapshotResult,
- html: serializeDocument(rootEl, opts),
+ html: serializeDocument(root, opts),
timing: {
createDocument: createDocTime,
render: renderDocTime,
diff --git a/packages/qwik/src/server/types.ts b/packages/qwik/src/server/types.ts
index 5444a475d1d..2879fb88933 100644
--- a/packages/qwik/src/server/types.ts
+++ b/packages/qwik/src/server/types.ts
@@ -91,7 +91,7 @@ export interface RenderToStringOptions extends SerializeDocumentOptions {
/**
* Specifies if the Qwik Loader script is added to the document or not. Defaults to `{ include: true }`.
*/
- qwikLoader?: { events?: string[]; include?: boolean | 'body' | 'head' };
+ qwikLoader?: { events?: string[]; include?: boolean | 'top' | 'bottom' };
prefetchStrategy?: PrefetchStrategy | null;
/**
diff --git a/packages/qwik/src/testing/api.md b/packages/qwik/src/testing/api.md
index b425fda5d9f..22999df59ba 100644
--- a/packages/qwik/src/testing/api.md
+++ b/packages/qwik/src/testing/api.md
@@ -131,7 +131,7 @@ export interface RenderToStringOptions extends SerializeDocumentOptions {
prefetchStrategy?: PrefetchStrategy | null;
qwikLoader?: {
events?: string[];
- include?: boolean | 'body' | 'head';
+ include?: boolean | 'top' | 'bottom';
};
snapshot?: boolean;
}
diff --git a/starters/apps/e2e/src/components/containers/container.tsx b/starters/apps/e2e/src/components/containers/container.tsx
index f42ff821e03..b6812da1c79 100644
--- a/starters/apps/e2e/src/components/containers/container.tsx
+++ b/starters/apps/e2e/src/components/containers/container.tsx
@@ -15,7 +15,7 @@ export const Containers = component$(() => {
);
});
-export const Container = component$((props: ContainerProps) => {
+export const Container = component$(async (props: ContainerProps) => {
useScopedStyles$(`
.container {
margin: 20px;
@@ -36,10 +36,9 @@ export const Container = component$((props: ContainerProps) => {
}
`);
const url = `http://localhost:3300${props.url}?fragment&loader=false`;
- // const { default: fetch } = await import('node-fetch');
- // const res = await fetch(url);
- // const html = await res.text();
- const html = '';
+ const { default: fetch } = await import('node-fetch');
+ const res = await fetch(url);
+ const html = await res.text();
return (
{url}
diff --git a/starters/apps/e2e/src/entry.ssr.tsx b/starters/apps/e2e/src/entry.ssr.tsx
index 62ef5321479..75f51c5b245 100644
--- a/starters/apps/e2e/src/entry.ssr.tsx
+++ b/starters/apps/e2e/src/entry.ssr.tsx
@@ -47,7 +47,7 @@ export function render(opts: RenderToStringOptions) {
>,
{
debug: true,
- fragmentTagName: 'div',
+ fragmentTagName: 'container',
qwikLoader: {
include: url.searchParams.get('loader') !== 'false',
events: ['click'],