Skip to content

Commit 5f749fa

Browse files
committed
feat(core): allow qwikloader inlining
1 parent 0781f53 commit 5f749fa

File tree

6 files changed

+92
-98
lines changed

6 files changed

+92
-98
lines changed

.changeset/heavy-mammals-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@builder.io/qwik': minor
3+
---
4+
5+
feat: the qwikloader can now be inlined again if required (for testing or specific network conditions). Pass `qwikLoader: 'inline'` to the render options.

packages/docs/src/routes/api/qwik-server/api.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@
137137
"id": "qwikloaderoptions"
138138
}
139139
],
140-
"kind": "Interface",
141-
"content": "```typescript\nexport interface QwikLoaderOptions \n```\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[include?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n'always' \\| 'never' \\| 'auto'\n\n\n</td><td>\n\n_(Optional)_ Whether to include the qwikloader script in the document. Normally you don't need to worry about this, but in case of multi-container apps using different Qwik versions, you might want to only enable it on one of the containers.\n\nDefaults to `'auto'`<!-- -->.\n\n\n</td></tr>\n<tr><td>\n\n[position?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n'top' \\| 'bottom'\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n</tbody></table>",
140+
"kind": "TypeAlias",
141+
"content": "```typescript\nexport type QwikLoaderOptions = 'module' | 'inline' | 'never' | {\n include?: 'always' | 'never' | 'auto';\n position?: 'top' | 'bottom';\n};\n```",
142142
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/server/types.ts",
143143
"mdFile": "qwik.qwikloaderoptions.md"
144144
},
@@ -166,7 +166,7 @@
166166
}
167167
],
168168
"kind": "Interface",
169-
"content": "```typescript\nexport interface RenderOptions extends SerializeDocumentOptions \n```\n**Extends:** [SerializeDocumentOptions](#serializedocumentoptions)\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[base?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Specifies the root of the JS files of the client build. Setting a base, will cause the render of the `q:base` attribute in the `q:container` element.\n\n\n</td></tr>\n<tr><td>\n\n[containerAttributes?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, string&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[containerTagName?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring\n\n\n</td><td>\n\n_(Optional)_ When set, the app is serialized into a fragment. And the returned html is not a complete document. Defaults to `html`\n\n\n</td></tr>\n<tr><td>\n\n[locale?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Language to use when rendering the document.\n\n\n</td></tr>\n<tr><td>\n\n[prefetchStrategy?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PrefetchStrategy](#prefetchstrategy) \\| null\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[preloader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PreloaderOptions](#preloaderoptions) \\| false\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[qwikLoader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[QwikLoaderOptions](#qwikloaderoptions)\n\n\n</td><td>\n\n_(Optional)_ Specifies if the Qwik Loader script is added to the document or not.\n\nDefaults to `{ include: true }`<!-- -->.\n\n\n</td></tr>\n<tr><td>\n\n[qwikPrefetchServiceWorker?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nQwikPrefetchServiceWorkerOptions\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[serverData?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, any&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[snapshot?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nboolean\n\n\n</td><td>\n\n_(Optional)_ Defaults to `true`\n\n\n</td></tr>\n</tbody></table>",
169+
"content": "```typescript\nexport interface RenderOptions extends SerializeDocumentOptions \n```\n**Extends:** [SerializeDocumentOptions](#serializedocumentoptions)\n\n\n<table><thead><tr><th>\n\nProperty\n\n\n</th><th>\n\nModifiers\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\n[base?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Specifies the root of the JS files of the client build. Setting a base, will cause the render of the `q:base` attribute in the `q:container` element.\n\n\n</td></tr>\n<tr><td>\n\n[containerAttributes?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, string&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[containerTagName?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring\n\n\n</td><td>\n\n_(Optional)_ When set, the app is serialized into a fragment. And the returned html is not a complete document. Defaults to `html`\n\n\n</td></tr>\n<tr><td>\n\n[locale?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nstring \\| ((options: [RenderOptions](#renderoptions)<!-- -->) =&gt; string)\n\n\n</td><td>\n\n_(Optional)_ Language to use when rendering the document.\n\n\n</td></tr>\n<tr><td>\n\n[prefetchStrategy?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PrefetchStrategy](#prefetchstrategy) \\| null\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[preloader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[PreloaderOptions](#preloaderoptions) \\| false\n\n\n</td><td>\n\n_(Optional)_ Specifies how preloading is handled. This ensures that code is instantly available when needed.\n\n\n</td></tr>\n<tr><td>\n\n[qwikLoader?](#)\n\n\n</td><td>\n\n\n</td><td>\n\n[QwikLoaderOptions](#qwikloaderoptions)\n\n\n</td><td>\n\n_(Optional)_ Specifies how the Qwik Loader is included in the document. This enables interactivity and lazy loading.\n\n`module`<!-- -->: Use a `<script>` tag to load the Qwik Loader. Subsequent page loads will have the script cached and instantly running.\n\n`inline`<!-- -->: This embeds the Qwik Loader script directly in the document. This adds about 3kB before compression, which typically is reduced to about 1.6kB with gzip.\n\n`never`<!-- -->: Do not include the Qwik Loader script. This is mostly useful when embedding multiple containers on the same page.\n\nDefaults to `module`<!-- -->.\n\nNote that the Qwik Loader is absolutely required for Qwik to work. There must be an instance of it loaded for any interactivity to happen.\n\n\n</td></tr>\n<tr><td>\n\n[qwikPrefetchServiceWorker?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nQwikPrefetchServiceWorkerOptions\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[serverData?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nRecord&lt;string, any&gt;\n\n\n</td><td>\n\n_(Optional)_\n\n\n</td></tr>\n<tr><td>\n\n[snapshot?](#)\n\n\n</td><td>\n\n\n</td><td>\n\nboolean\n\n\n</td><td>\n\n_(Optional)_ Defaults to `true`\n\n\n</td></tr>\n</tbody></table>",
170170
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/server/types.ts",
171171
"mdFile": "qwik.renderoptions.md"
172172
},

packages/docs/src/routes/api/qwik-server/index.mdx

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -545,60 +545,16 @@ Defaults to `5`
545545
## QwikLoaderOptions
546546

547547
```typescript
548-
export interface QwikLoaderOptions
548+
export type QwikLoaderOptions =
549+
| "module"
550+
| "inline"
551+
| "never"
552+
| {
553+
include?: "always" | "never" | "auto";
554+
position?: "top" | "bottom";
555+
};
549556
```
550557

551-
<table><thead><tr><th>
552-
553-
Property
554-
555-
</th><th>
556-
557-
Modifiers
558-
559-
</th><th>
560-
561-
Type
562-
563-
</th><th>
564-
565-
Description
566-
567-
</th></tr></thead>
568-
<tbody><tr><td>
569-
570-
[include?](#)
571-
572-
</td><td>
573-
574-
</td><td>
575-
576-
'always' \| 'never' \| 'auto'
577-
578-
</td><td>
579-
580-
_(Optional)_ Whether to include the qwikloader script in the document. Normally you don't need to worry about this, but in case of multi-container apps using different Qwik versions, you might want to only enable it on one of the containers.
581-
582-
Defaults to `'auto'`.
583-
584-
</td></tr>
585-
<tr><td>
586-
587-
[position?](#)
588-
589-
</td><td>
590-
591-
</td><td>
592-
593-
'top' \| 'bottom'
594-
595-
</td><td>
596-
597-
_(Optional)_
598-
599-
</td></tr>
600-
</tbody></table>
601-
602558
[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/server/types.ts)
603559

604560
## Render
@@ -723,7 +679,7 @@ _(Optional)_
723679
724680
</td><td>
725681
726-
_(Optional)_
682+
_(Optional)_ Specifies how preloading is handled. This ensures that code is instantly available when needed.
727683
728684
</td></tr>
729685
<tr><td>
@@ -738,9 +694,17 @@ _(Optional)_
738694
739695
</td><td>
740696
741-
_(Optional)_ Specifies if the Qwik Loader script is added to the document or not.
697+
_(Optional)_ Specifies how the Qwik Loader is included in the document. This enables interactivity and lazy loading.
698+
699+
`module`: Use a `<script>` tag to load the Qwik Loader. Subsequent page loads will have the script cached and instantly running.
700+
701+
`inline`: This embeds the Qwik Loader script directly in the document. This adds about 3kB before compression, which typically is reduced to about 1.6kB with gzip.
702+
703+
`never`: Do not include the Qwik Loader script. This is mostly useful when embedding multiple containers on the same page.
704+
705+
Defaults to `module`.
742706
743-
Defaults to `{ include: true }`.
707+
Note that the Qwik Loader is absolutely required for Qwik to work. There must be an instance of it loaded for any interactivity to happen.
744708
745709
</td></tr>
746710
<tr><td>

packages/qwik/src/server/qwik.server.api.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,10 @@ export interface PreloaderOptions {
8383
}
8484

8585
// @public (undocumented)
86-
export interface QwikLoaderOptions {
86+
export type QwikLoaderOptions = 'module' | 'inline' | 'never' | {
8787
include?: 'always' | 'never' | 'auto';
88-
// @deprecated (undocumented)
8988
position?: 'top' | 'bottom';
90-
}
89+
};
9190

9291
// @public (undocumented)
9392
export type Render = RenderToString | RenderToStream;
@@ -101,7 +100,6 @@ export interface RenderOptions extends SerializeDocumentOptions {
101100
locale?: string | ((options: RenderOptions) => string);
102101
// @deprecated (undocumented)
103102
prefetchStrategy?: PrefetchStrategy | null;
104-
// (undocumented)
105103
preloader?: PreloaderOptions | false;
106104
qwikLoader?: QwikLoaderOptions;
107105
// Warning: (ae-forgotten-export) The symbol "QwikPrefetchServiceWorkerOptions" needs to be exported by the entry point index.d.ts

packages/qwik/src/server/render.ts

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ import { manifest as builtManifest } from '@qwik-client-manifest';
2020

2121
const DOCTYPE = '<!DOCTYPE html>';
2222

23+
enum QwikLoaderInclude {
24+
Module,
25+
Inline,
26+
Never,
27+
}
28+
2329
/**
2430
* Creates a server-side `document`, renders to root node to the document, then serializes the
2531
* document to a string.
@@ -119,10 +125,22 @@ export async function renderToStream(
119125
? injections.map((injection) => jsx(injection.tag, injection.attributes ?? {}))
120126
: [];
121127

122-
const includeMode = opts.qwikLoader?.include ?? 'auto';
128+
let includeMode = opts.qwikLoader
129+
? typeof opts.qwikLoader === 'object'
130+
? opts.qwikLoader.include === 'never'
131+
? QwikLoaderInclude.Never
132+
: QwikLoaderInclude.Module
133+
: opts.qwikLoader === 'inline'
134+
? QwikLoaderInclude.Inline
135+
: opts.qwikLoader === 'never'
136+
? QwikLoaderInclude.Never
137+
: QwikLoaderInclude.Module
138+
: QwikLoaderInclude.Module;
123139
const qwikLoaderChunk = resolvedManifest?.manifest.qwikLoader;
124-
let didAddQwikLoader = false;
125-
if (includeMode !== 'never' && qwikLoaderChunk) {
140+
if (includeMode === QwikLoaderInclude.Module && !qwikLoaderChunk) {
141+
includeMode = QwikLoaderInclude.Inline;
142+
}
143+
if (includeMode === QwikLoaderInclude.Module) {
126144
beforeContent.unshift(
127145
jsx('link', {
128146
rel: 'modulepreload',
@@ -136,7 +154,22 @@ export async function renderToStream(
136154
nonce,
137155
})
138156
);
139-
didAddQwikLoader = true;
157+
} else if (includeMode === QwikLoaderInclude.Inline) {
158+
// It would be nice to keep track of HTML size and wait 30kB before inlining the script, skipping if ended and not needed.
159+
const qwikLoaderScript = getQwikLoaderScript({
160+
debug: opts.debug,
161+
});
162+
beforeContent.unshift(
163+
jsx('script', {
164+
id: 'qwikloader',
165+
// Qwik only works when modules work
166+
type: 'module',
167+
// Execute asap, don't wait for domcontentloaded
168+
async: true,
169+
nonce,
170+
dangerouslySetInnerHTML: qwikLoaderScript,
171+
})
172+
);
140173
}
141174
preloaderPre(buildBase, resolvedManifest, opts.preloader, beforeContent, nonce);
142175

@@ -181,24 +214,6 @@ export async function renderToStream(
181214
);
182215
}
183216

184-
const needLoader = !snapshotResult || snapshotResult.mode !== 'static';
185-
const includeLoader = includeMode === 'always' || (includeMode === 'auto' && needLoader);
186-
if (!didAddQwikLoader && includeLoader) {
187-
const qwikLoaderScript = getQwikLoaderScript({
188-
debug: opts.debug,
189-
});
190-
children.push(
191-
jsx('script', {
192-
id: 'qwikloader',
193-
// execute even before DOM order
194-
async: true,
195-
type: 'module',
196-
dangerouslySetInnerHTML: qwikLoaderScript,
197-
nonce,
198-
})
199-
);
200-
}
201-
202217
// We emit the events separately so other qwikloaders can see them
203218
const extraListeners = Array.from(containerState.$events$, (s) => JSON.stringify(s));
204219
if (extraListeners.length > 0) {

packages/qwik/src/server/types.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,16 @@ export interface RenderResult {
128128
}
129129

130130
/** @public */
131-
export interface QwikLoaderOptions {
132-
/**
133-
* Whether to include the qwikloader script in the document. Normally you don't need to worry
134-
* about this, but in case of multi-container apps using different Qwik versions, you might want
135-
* to only enable it on one of the containers.
136-
*
137-
* Defaults to `'auto'`.
138-
*/
139-
include?: 'always' | 'never' | 'auto';
140-
/** @deprecated No longer used, the qwikloader is always loaded as soon as possible */
141-
position?: 'top' | 'bottom';
142-
}
131+
export type QwikLoaderOptions =
132+
| 'module'
133+
| 'inline'
134+
| 'never'
135+
| {
136+
/** @deprecated No longer used. */
137+
include?: 'always' | 'never' | 'auto';
138+
/** @deprecated No longer used. */
139+
position?: 'top' | 'bottom';
140+
};
143141

144142
/**
145143
* @deprecated This is no longer used as the preloading happens automatically in qrl-class.ts.
@@ -167,12 +165,26 @@ export interface RenderOptions extends SerializeDocumentOptions {
167165
locale?: string | ((options: RenderOptions) => string);
168166

169167
/**
170-
* Specifies if the Qwik Loader script is added to the document or not.
168+
* Specifies how the Qwik Loader is included in the document. This enables interactivity and lazy
169+
* loading.
170+
*
171+
* `module`: Use a `<script>` tag to load the Qwik Loader. Subsequent page loads will have the
172+
* script cached and instantly running.
173+
*
174+
* `inline`: This embeds the Qwik Loader script directly in the document. This adds about 3kB
175+
* before compression, which typically is reduced to about 1.6kB with gzip.
176+
*
177+
* `never`: Do not include the Qwik Loader script. This is mostly useful when embedding multiple
178+
* containers on the same page.
179+
*
180+
* Defaults to `module`.
171181
*
172-
* Defaults to `{ include: true }`.
182+
* Note that the Qwik Loader is absolutely required for Qwik to work. There must be an instance of
183+
* it loaded for any interactivity to happen.
173184
*/
174185
qwikLoader?: QwikLoaderOptions;
175186

187+
/** Specifies how preloading is handled. This ensures that code is instantly available when needed. */
176188
preloader?: PreloaderOptions | false;
177189

178190
/** @deprecated Use `preloader` instead */

0 commit comments

Comments
 (0)