Skip to content

feat: dynamic modulepreload #7453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/fair-cars-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@builder.io/qwik': minor
---

PERF: Prefetching now happens dynamically without service worker if the prefetchImplementation is set to "html-append" (default).
PERF: Initial prefetching now includes dynamic imports, which improves initial click delay.
5 changes: 5 additions & 0 deletions .changeset/yellow-frogs-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik-city': minor
---

CHORE: the service workers have been deprecated and replaced with entries that unregister them. If you have it enabled in production, you can remove it after a while once you are sure all your users have the new version.
2 changes: 0 additions & 2 deletions packages/docs/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { renderToStream, type RenderToStreamOptions } from '@builder.io/qwik/server';
import { manifest } from '@qwik-client-manifest';
import Root from './root';

export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, {
manifest,
qwikLoader: {
// The docs can be long so make sure to intercept events before the end of the document.
position: 'top',
Expand Down
2 changes: 2 additions & 0 deletions packages/docs/src/repl/bundled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import qCoreDts from '../../node_modules/@builder.io/qwik/dist/core.d.ts?raw-sou
import qCoreMinMjs from '../../node_modules/@builder.io/qwik/dist/core.min.mjs?raw-source';
import qCoreMjs from '../../node_modules/@builder.io/qwik/dist/core.mjs?raw-source';
import qOptimizerCjs from '../../node_modules/@builder.io/qwik/dist/optimizer.cjs?raw-source';
import qPreloaderMjs from '../../node_modules/@builder.io/qwik/dist/preloader.mjs?raw-source';
import qServerCjs from '../../node_modules/@builder.io/qwik/dist/server.cjs?raw-source';
import qServerDts from '../../node_modules/@builder.io/qwik/dist/server.d.ts?raw-source';
import qWasmCjs from '../../node_modules/@builder.io/qwik/bindings/qwik.wasm.cjs?raw-source';
Expand Down Expand Up @@ -55,6 +56,7 @@ export const bundled: PkgUrls = {
'/dist/optimizer.cjs': qOptimizerCjs,
'/dist/server.cjs': qServerCjs,
'/dist/server.d.ts': qServerDts,
'/dist/preloader.mjs': qPreloaderMjs,
'/bindings/qwik.wasm.cjs': qWasmCjs,
'/bindings/qwik_wasm_bg.wasm': qWasmBinUrl,
},
Expand Down
44 changes: 28 additions & 16 deletions packages/docs/src/repl/repl-output-modules.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { $, component$, useSignal } from '@builder.io/qwik';
import { $, component$, createSignal, useSignal } from '@builder.io/qwik';
import { CodeBlock } from '../components/code-block/code-block';
import type { ReplModuleOutput } from './types';
const FILE_MODULE_DIV_ID = 'file-modules-client-modules';
Expand Down Expand Up @@ -35,22 +35,34 @@ export const ReplOutputModules = component$(({ outputs, headerText }: ReplOutput
</div>
</div>
<div class="file-modules" id={FILE_MODULE_DIV_ID}>
{outputs.map((o, i) => (
<div class="file-item" data-output-item={i} key={o.path}>
<div class="file-info">
<span>{o.path}</span>
{o.size ? <span class="file-size">({o.size})</span> : null}
{outputs.map((o, i) => {
const isLarge = o.code.length > 3000;
if (isLarge && !o.shorten) {
o.shorten = createSignal(true);
}
const code = o.shorten?.value ? o.code.slice(0, 3000) : o.code;
return (
<div class="file-item" data-output-item={i} key={o.path}>
<div class="file-info">
<span>{o.path}</span>
{o.size ? <span class="file-size">({o.size})</span> : null}
</div>
<div class="file-text">
<CodeBlock
pathInView$={pathInView$}
path={o.path}
code={code}
observerRootId={FILE_MODULE_DIV_ID}
/>
{o.shorten && (
<button onClick$={() => (o.shorten.value = !o.shorten.value)}>
{o.shorten.value ? 'Truncated - show more' : 'Show less'}
</button>
)}
</div>
</div>
<div class="file-text">
<CodeBlock
pathInView$={pathInView$}
path={o.path}
code={o.code}
observerRootId={FILE_MODULE_DIV_ID}
/>
</div>
</div>
))}
);
})}
</div>
</div>
);
Expand Down
8 changes: 2 additions & 6 deletions packages/docs/src/repl/repl-output-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { component$, useComputed$ } from '@builder.io/qwik';
import { component$ } from '@builder.io/qwik';
import { CodeBlock } from '../components/code-block/code-block';
import { ReplOutputModules } from './repl-output-modules';
import { ReplOutputSymbols } from './repl-output-symbols';
Expand All @@ -8,10 +8,6 @@ import type { ReplAppInput, ReplStore } from './types';

export const ReplOutputPanel = component$(({ input, store }: ReplOutputPanelProps) => {
const diagnosticsLen = store.diagnostics.length + store.monacoDiagnostics.length;
const clientBundlesNoCore = useComputed$(() =>
// Qwik Core is not interesting and is large, slowing down the UI
store.clientBundles.filter((b) => !b.path.endsWith('qwikCore.js'))
);

return (
<div class="repl-panel repl-output-panel">
Expand Down Expand Up @@ -115,7 +111,7 @@ export const ReplOutputPanel = component$(({ input, store }: ReplOutputPanelProp
) : null}

{store.selectedOutputPanel === 'clientBundles' ? (
<ReplOutputModules headerText="/build/" outputs={clientBundlesNoCore.value} />
<ReplOutputModules headerText="/build/" outputs={store.clientBundles} />
) : null}

{store.selectedOutputPanel === 'serverModules' ? (
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/repl/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { NoSerialize, Signal } from '@builder.io/qwik';
import type {
Diagnostic,
QwikManifest,
QwikRollupPluginOptions,
TransformModule,
} from '@builder.io/qwik/optimizer';
import type { NoSerialize } from '@builder.io/qwik';

export interface ReplAppInput {
buildId: number;
Expand Down Expand Up @@ -58,6 +58,7 @@ export interface ReplModuleOutput {
path: string;
code: string;
size?: string;
shorten?: Signal<boolean>;
}

export interface ReplMessageBase {
Expand Down
8 changes: 0 additions & 8 deletions packages/docs/src/repl/worker/app-bundle-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,6 @@ export const appBundleClient = async (
});
}

result.transformedModules = result.transformedModules.filter((f) => {
return (
!f.path.endsWith('app.js') &&
!f.path.endsWith('entry.server.js') &&
!f.path.endsWith('root.js')
);
});

result.events.push({
kind: 'console-log',
scope: 'build',
Expand Down
9 changes: 9 additions & 0 deletions packages/docs/src/repl/worker/repl-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const replResolver = (options: ReplInputOptions, buildMode: 'client' | 's
if (id === '@builder.io/qwik/server') {
return '\0qwikServer';
}
if (id === '@builder.io/qwik/preloader') {
return '\0qwikPreloader';
}
// Simple relative file resolution
if (id.startsWith('./')) {
const extensions = ['', '.tsx', '.ts'];
Expand Down Expand Up @@ -69,6 +72,12 @@ export const replResolver = (options: ReplInputOptions, buildMode: 'client' | 's
}
throw new Error(`Unable to load Qwik core`);
}
if (id === '\0qwikPreloader') {
const rsp = await depResponse('@builder.io/qwik', '/preloader.mjs');
if (rsp) {
return rsp.text();
}
}

// We're the fallback, we know all the files
if (/\.[jt]sx?$/.test(id)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/root.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { component$, useContextProvider, useStore } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { Insights } from '@builder.io/qwik-labs';
import RealMetricsOptimization from './components/real-metrics-optimization/real-metrics-optimization';
import { RouterHead } from './components/router-head/router-head';
import { BUILDER_PUBLIC_API_KEY } from './constants';
import { GlobalStore, type SiteStore } from './context';
import './global.css';
import { BUILDER_PUBLIC_API_KEY } from './constants';
import { Insights } from '@builder.io/qwik-labs';

export const uwu = /*javascript*/ `
;(function () {
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-city/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@
}
],
"kind": "Function",
"content": "```typescript\nServiceWorkerRegister: (props: {\n nonce?: string;\n}) => import(\"@builder.io/qwik\").JSXNode<\"script\">\n```\n\n\n<table><thead><tr><th>\n\nParameter\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\nprops\n\n\n</td><td>\n\n{ nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nimport(\"@builder.io/qwik\").JSXNode&lt;\"script\"&gt;",
"content": "Loads the service workers that are defined in the routes. Any file named `service-worker.*` (all JS extensions are allowed) will be picked up, bundled into a separate file, and registered as a service worker.\n\n\n```typescript\nServiceWorkerRegister: (props: {\n nonce?: string;\n}) => JSXOutput\n```\n\n\n<table><thead><tr><th>\n\nParameter\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\nprops\n\n\n</td><td>\n\n{ nonce?: string; }\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nJSXOutput",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/sw-component.tsx",
"mdFile": "qwik-city.serviceworkerregister.md"
},
Expand Down
7 changes: 4 additions & 3 deletions packages/docs/src/routes/api/qwik-city/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2269,9 +2269,10 @@ export type ServerQRL<T extends ServerFunction> = QRL<

## ServiceWorkerRegister

Loads the service workers that are defined in the routes. Any file named `service-worker.*` (all JS extensions are allowed) will be picked up, bundled into a separate file, and registered as a service worker.

```typescript
ServiceWorkerRegister: (props: { nonce?: string }) =>
import("@builder.io/qwik").JSXNode<"script">;
ServiceWorkerRegister: (props: { nonce?: string }) => JSXOutput;
```

<table><thead><tr><th>
Expand Down Expand Up @@ -2301,7 +2302,7 @@ props
</tbody></table>
**Returns:**

import("@builder.io/qwik").JSXNode&lt;"script"&gt;
JSXOutput

[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/src/runtime/src/sw-component.tsx)

Expand Down
Loading