From 79716fc12956fdd6b86582ff532a521fe82aecaa Mon Sep 17 00:00:00 2001 From: ToanTrinh <85246658+ToanTrinh01@users.noreply.github.com> Date: Thu, 9 May 2024 12:36:28 -0700 Subject: [PATCH 001/412] chore: fix Test Typo (#6289) Co-authored-by: PatrickJS --- starters/e2e/qwikcity/actions.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starters/e2e/qwikcity/actions.spec.ts b/starters/e2e/qwikcity/actions.spec.ts index 94d09d2cff7..88e3eb0c46e 100644 --- a/starters/e2e/qwikcity/actions.spec.ts +++ b/starters/e2e/qwikcity/actions.spec.ts @@ -147,7 +147,7 @@ test.describe("actions", () => { }); }); - test.describe("isue3497", () => { + test.describe("issue3497", () => { test("should parse formdata", async ({ page }) => { await page.goto("/qwikcity-test/actions/issue3497/"); const success = page.locator("#issue3497-success"); From f2aecea65c63ebb6c97796ca0098c1ed2c6c70a4 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Fri, 10 May 2024 10:48:44 +0200 Subject: [PATCH 002/412] fix(plugin): csr typing (#6292) --- packages/qwik/src/optimizer/src/plugins/plugin.unit.ts | 6 ++++++ packages/qwik/src/optimizer/src/plugins/vite.ts | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts b/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts index 8b0b50afb5f..ac3f0194d15 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts @@ -3,9 +3,15 @@ import { assert, test } from 'vitest'; import type { QwikManifest } from '../types'; import { createPlugin } from './plugin'; import { normalizePath } from '../../../testing/util'; +import { qwikVite } from './vite'; const cwd = process.cwd(); +test('types', () => () => { + qwikVite({ csr: true }); + qwikVite({ csr: false, ssr: {} }); +}); + test('defaults', async () => { const plugin = await mockPlugin(); const opts = plugin.normalizeOptions(); diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts index ac3054e2b87..d6eb05649ef 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.ts @@ -910,8 +910,9 @@ interface QwikVitePluginCommonOptions { interface QwikVitePluginCSROptions extends QwikVitePluginCommonOptions { /** Client Side Rendering (CSR) mode. It will not support SSR, default to Vite's `index.html` file. */ csr: true; - ssr: never; - client: never; + client?: never; + devSsrServer?: never; + ssr?: never; } interface QwikVitePluginSSROptions extends QwikVitePluginCommonOptions { From cab4ae3ecdac78fdcf362e0d189f888a02a9e2f0 Mon Sep 17 00:00:00 2001 From: PatrickJS Date: Fri, 10 May 2024 06:02:07 -0700 Subject: [PATCH 003/412] feat(qwikloader): emit error (#6254) * feat(qwikloader): fixes https://github.com/QwikDev/qwik/issues/3716 * Update qwikloader.ts * Update qwikloader.ts * Update qwikloader.ts * style: lint * feat(qwikloader): importError --- packages/qwik/src/qwikloader.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/qwik/src/qwikloader.ts b/packages/qwik/src/qwikloader.ts index 4f954cb9373..787f2ee3f88 100644 --- a/packages/qwik/src/qwikloader.ts +++ b/packages/qwik/src/qwikloader.ts @@ -85,32 +85,35 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => { const base = new URL(container[getAttribute]('q:base')!, doc.baseURI); for (const qrl of attrValue.split('\n')) { const url = new URL(qrl, base); - const symbolName = url.hash[replace](/^#?([^?[|]*).*$/, '$1') || 'default'; + const symbol = url.hash[replace](/^#?([^?[|]*).*$/, '$1') || 'default'; const reqTime = performance.now(); let handler: any; const isSync = qrl.startsWith('#'); if (isSync) { - handler = ((container as QContainerElement).qFuncs || [])[Number.parseInt(symbolName)]; + handler = ((container as QContainerElement).qFuncs || [])[Number.parseInt(symbol)]; } else { - const module = import(/* @vite-ignore */ url.href.split('#')[0]); - resolveContainer(container); - handler = (await module)[symbolName]; + const uri = url.href.split('#')[0]; + try { + const module = import(/* @vite-ignore */ uri); + resolveContainer(container); + handler = (await module)[symbol]; + } catch (error) { + emitEvent('qerror', { importError: true, error, symbol, uri }); + } } const previousCtx = (doc as any)[Q_CONTEXT]; if (element[isConnected]) { + const eventData = { symbol, element, reqTime }; try { (doc as any)[Q_CONTEXT] = [element, ev, url]; - isSync || - emitEvent('qsymbol', { - symbol: symbolName, - element: element, - reqTime, - }); + isSync || emitEvent('qsymbol', eventData); const results = handler(ev, element); // only await if there is a promise returned if (isPromise(results)) { await results; } + } catch (error) { + emitEvent('qerror', { error, ...eventData }); } finally { (doc as any)[Q_CONTEXT] = previousCtx; } From de509267d95eb39fc4fb9c241b1fe60752856e17 Mon Sep 17 00:00:00 2001 From: Tobi Date: Fri, 10 May 2024 15:05:06 +0200 Subject: [PATCH 004/412] docs(polymorphism): changed hi to slot (#6294) * docs(polymorphism): changed hi to slot The examples were wrong, because was missing * updated indentation and wrapped examples in component$ --------- Co-authored-by: Tobias Zimmermann --- .../docs/(qwik)/components/overview/index.mdx | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/docs/src/routes/docs/(qwik)/components/overview/index.mdx b/packages/docs/src/routes/docs/(qwik)/components/overview/index.mdx index 6febd422135..7df5961c205 100644 --- a/packages/docs/src/routes/docs/(qwik)/components/overview/index.mdx +++ b/packages/docs/src/routes/docs/(qwik)/components/overview/index.mdx @@ -386,23 +386,33 @@ bundled with the parent component. When you want to output a different type of element depending on props but default to a `
`, you can do it using something like this: ```tsx - const Poly = component$( - ({ - as, - ...props - }: { as?: C } & PropsOf) => { - const Cmp = as || 'div'; - return hi; - } +const Poly = component$( + ({ + as, + ...props + }: { as?: C } & PropsOf) => { + const Cmp = as || 'div'; + return ( + + + ); + } +); -// These all work with correct types -<> - Hello from a div - Blog - console.log(el.value)} /> - - +export const TestComponent = component$(() => { + // These all work with correct types + return ( + <> + Hello from a div + + Blog + + console.log(el.value)} /> + + + ); +}); ``` Note the `string extends C`, this is only true when TypeScript cannot infer the type from the `as` prop allowing you to specify the default type. From ac6ea5c35afe6ba1cd152582748c4528a581be7f Mon Sep 17 00:00:00 2001 From: PatrickJS Date: Fri, 10 May 2024 06:11:05 -0700 Subject: [PATCH 005/412] feat(Form): multi onSubmit$ handlers (#6241) * feat(Form): multi onSubmit$ * style(Form): fmt * chore: api.update * fix: multiple onSubmit * feat(Form): "submitcompleted" event for action forms * style(Form): comments * revert: submitcompleted fires in action.submit * Revert "fix: multiple onSubmit" This reverts commit f60741dbd3046716d0a5238cb48c4ca8f3bae02a. * refactor(Form): array onSubmit new API * style: lint * chore(Form): api.update * refactor(Form): isArray once * feat(action): submitted * fix: test * chore: update tests * chore: correct test * test: fix spa only tests * chore: fix types * fix: submitted * Update index.tsx --- .../docs/src/routes/api/qwik-city/api.json | 4 +- .../docs/src/routes/api/qwik-city/index.md | 5 +- packages/docs/src/routes/api/qwik/api.json | 4 +- packages/docs/src/routes/api/qwik/index.md | 4 +- packages/qwik-city/runtime/src/api.md | 8 +- .../qwik-city/runtime/src/form-component.tsx | 75 ++++++++++++++----- .../qwik-city/runtime/src/server-functions.ts | 2 + packages/qwik-city/runtime/src/types.ts | 2 + .../actions/multiple-handlers/index.tsx | 50 +++++++++++++ starters/e2e/qwikcity/actions.spec.ts | 20 ++++- 10 files changed, 141 insertions(+), 33 deletions(-) create mode 100644 starters/apps/qwikcity-test/src/routes/(common)/actions/multiple-handlers/index.tsx diff --git a/packages/docs/src/routes/api/qwik-city/api.json b/packages/docs/src/routes/api/qwik-city/api.json index 084b18048c8..310774e9e1b 100644 --- a/packages/docs/src/routes/api/qwik-city/api.json +++ b/packages/docs/src/routes/api/qwik-city/api.json @@ -54,7 +54,7 @@ } ], "kind": "TypeAlias", - "content": "```typescript\nexport type ActionStore = {\n readonly actionPath: string;\n readonly isRunning: boolean;\n readonly status?: number;\n readonly formData: FormData | undefined;\n readonly value: RETURN | undefined;\n readonly submit: QRL Promise> : (form: INPUT | FormData | SubmitEvent) => Promise>>;\n};\n```\n**References:** [ActionReturn](#actionreturn)", + "content": "```typescript\nexport type ActionStore = {\n readonly actionPath: string;\n readonly isRunning: boolean;\n readonly status?: number;\n readonly formData: FormData | undefined;\n readonly value: RETURN | undefined;\n readonly submit: QRL Promise> : (form: INPUT | FormData | SubmitEvent) => Promise>>;\n readonly submitted: boolean;\n};\n```\n**References:** [ActionReturn](#actionreturn)", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/runtime/src/types.ts", "mdFile": "qwik-city.actionstore.md" }, @@ -250,7 +250,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface FormProps extends Omit \n```\n**Extends:** Omit<QwikJSX.IntrinsicElements\\['form'\\], 'action' \\| 'method'>\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[action?](#)\n\n\n\n\n\n\n\n[ActionStore](#actionstore)<O, I, true \\| false>\n\n\n\n\n_(Optional)_ Reference to the action returned by `action()`.\n\n\n
\n\n[key?](#)\n\n\n\n\n\n\n\nstring \\| number \\| null\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onSubmit$?](#)\n\n\n\n\n\n\n\n(event: Event, form: HTMLFormElement) => ValueOrPromise<void>\n\n\n\n\n_(Optional)_ Event handler executed right when the form is submitted.\n\n\n
\n\n[onSubmitCompleted$?](#)\n\n\n\n\n\n\n\n(event: CustomEvent<[FormSubmitCompletedDetail](#formsubmitsuccessdetail)<O>>, form: HTMLFormElement) => ValueOrPromise<void>\n\n\n\n\n_(Optional)_ Event handler executed right after the action is executed successfully and returns some data.\n\n\n
\n\n[reloadDocument?](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ When `true` the form submission will cause a full page reload, even if SPA mode is enabled and JS is available.\n\n\n
\n\n[spaReset?](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ When `true` all the form inputs will be reset in SPA mode, just like happens in a full page form submission.\n\nDefaults to `false`\n\n\n
", + "content": "```typescript\nexport interface FormProps extends Omit \n```\n**Extends:** Omit<QwikJSX.IntrinsicElements\\['form'\\], 'action' \\| 'method'>\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[action?](#)\n\n\n\n\n\n\n\n[ActionStore](#actionstore)<O, I, true \\| false>\n\n\n\n\n_(Optional)_ Reference to the action returned by `action()`.\n\n\n
\n\n[key?](#)\n\n\n\n\n\n\n\nstring \\| number \\| null\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onSubmit$?](#)\n\n\n\n\n\n\n\nQRLEventHandlerMulti<SubmitEvent, HTMLFormElement> \\| undefined\n\n\n\n\n_(Optional)_ Event handler executed right when the form is submitted.\n\n\n
\n\n[onSubmitCompleted$?](#)\n\n\n\n\n\n\n\nQRLEventHandlerMulti<CustomEvent<[FormSubmitCompletedDetail](#formsubmitsuccessdetail)<O>>, HTMLFormElement> \\| undefined\n\n\n\n\n_(Optional)_ Event handler executed right after the action is executed successfully and returns some data.\n\n\n
\n\n[reloadDocument?](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ When `true` the form submission will cause a full page reload, even if SPA mode is enabled and JS is available.\n\n\n
\n\n[spaReset?](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ When `true` all the form inputs will be reset in SPA mode, just like happens in a full page form submission.\n\nDefaults to `false`\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/runtime/src/form-component.tsx", "mdFile": "qwik-city.formprops.md" }, diff --git a/packages/docs/src/routes/api/qwik-city/index.md b/packages/docs/src/routes/api/qwik-city/index.md index 7b82ecbfdae..cdd1405f765 100644 --- a/packages/docs/src/routes/api/qwik-city/index.md +++ b/packages/docs/src/routes/api/qwik-city/index.md @@ -165,6 +165,7 @@ export type ActionStore = { ? (form?: INPUT | FormData | SubmitEvent) => Promise> : (form: INPUT | FormData | SubmitEvent) => Promise> >; + readonly submitted: boolean; }; ``` @@ -1264,7 +1265,7 @@ _(Optional)_ -(event: Event, form: HTMLFormElement) => ValueOrPromise<void> +QRLEventHandlerMulti<SubmitEvent, HTMLFormElement> \| undefined @@ -1279,7 +1280,7 @@ _(Optional)_ Event handler executed right when the form is submitted. -(event: CustomEvent<[FormSubmitCompletedDetail](#formsubmitsuccessdetail)<O>>, form: HTMLFormElement) => ValueOrPromise<void> +QRLEventHandlerMulti<CustomEvent<[FormSubmitCompletedDetail](#formsubmitsuccessdetail)<O>>, HTMLFormElement> \| undefined diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json index dfb62646924..00a2e7799a9 100644 --- a/packages/docs/src/routes/api/qwik/api.json +++ b/packages/docs/src/routes/api/qwik/api.json @@ -1704,7 +1704,7 @@ } ], "kind": "Function", - "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; manifestHash?: string; manifestURL?: string; }\n\n\n\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to `/build/`. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n
\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">", + "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; manifestHash?: string; manifestURL?: string; }\n\n\n\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to `/build/`. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n
\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").[JSXNode](#jsxnode)<\"script\">", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts", "mdFile": "qwik.prefetchgraph.md" }, @@ -1718,7 +1718,7 @@ } ], "kind": "Function", - "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; }\n\n\n\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. - `path` - Path to the service worker.\n\n\n
\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">", + "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<\"script\">\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; }\n\n\n\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. - `path` - Path to the service worker.\n\n\n
\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").[JSXNode](#jsxnode)<\"script\">", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts", "mdFile": "qwik.prefetchserviceworker.md" }, diff --git a/packages/docs/src/routes/api/qwik/index.md b/packages/docs/src/routes/api/qwik/index.md index 4b0efa2cdd8..ac0e7dc6cf0 100644 --- a/packages/docs/src/routes/api/qwik/index.md +++ b/packages/docs/src/routes/api/qwik/index.md @@ -3520,7 +3520,7 @@ _(Optional)_ Options for the loading prefetch graph. **Returns:** -import("@builder.io/qwik/jsx-runtime").JSXNode<"script"> +import("@builder.io/qwik/jsx-runtime").[JSXNode](#jsxnode)<"script"> [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts) @@ -3572,7 +3572,7 @@ Options for the prefetch service worker. **Returns:** -import("@builder.io/qwik/jsx-runtime").JSXNode<"script"> +import("@builder.io/qwik/jsx-runtime").[JSXNode](#jsxnode)<"script"> [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts) diff --git a/packages/qwik-city/runtime/src/api.md b/packages/qwik-city/runtime/src/api.md index 9c85597a2b9..124880e93b8 100644 --- a/packages/qwik-city/runtime/src/api.md +++ b/packages/qwik-city/runtime/src/api.md @@ -13,6 +13,7 @@ import type { EnvGetter } from '@builder.io/qwik-city/middleware/request-handler import { JSXNode } from '@builder.io/qwik'; import { JSXOutput } from '@builder.io/qwik'; import { QRL } from '@builder.io/qwik'; +import { QRLEventHandlerMulti } from '@builder.io/qwik'; import { QwikIntrinsicElements } from '@builder.io/qwik'; import { QwikJSX } from '@builder.io/qwik'; import type { ReadonlySignal } from '@builder.io/qwik'; @@ -23,7 +24,7 @@ import { RequestEventCommon } from '@builder.io/qwik-city/middleware/request-han import { RequestEventLoader } from '@builder.io/qwik-city/middleware/request-handler'; import { RequestHandler } from '@builder.io/qwik-city/middleware/request-handler'; import type { ResolveSyncValue } from '@builder.io/qwik-city/middleware/request-handler'; -import { ValueOrPromise } from '@builder.io/qwik'; +import type { ValueOrPromise } from '@builder.io/qwik'; import { z } from 'zod'; import type * as zod from 'zod'; @@ -68,6 +69,7 @@ export type ActionStore = { readonly formData: FormData | undefined; readonly value: RETURN | undefined; readonly submit: QRL Promise> : (form: INPUT | FormData | SubmitEvent) => Promise>>; + readonly submitted: boolean; }; // @public (undocumented) @@ -219,8 +221,8 @@ export interface FormProps extends Omit; // (undocumented) key?: string | number | null; - onSubmit$?: (event: Event, form: HTMLFormElement) => ValueOrPromise; - onSubmitCompleted$?: (event: CustomEvent>, form: HTMLFormElement) => ValueOrPromise; + onSubmit$?: QRLEventHandlerMulti | undefined; + onSubmitCompleted$?: QRLEventHandlerMulti>, HTMLFormElement> | undefined; reloadDocument?: boolean; spaReset?: boolean; } diff --git a/packages/qwik-city/runtime/src/form-component.tsx b/packages/qwik-city/runtime/src/form-component.tsx index 2ee04cade53..270b39573c3 100644 --- a/packages/qwik-city/runtime/src/form-component.tsx +++ b/packages/qwik-city/runtime/src/form-component.tsx @@ -1,10 +1,11 @@ import { jsx, _wrapSignal, - type QwikJSX, - type ValueOrPromise, component$, Slot, + $, + type QwikJSX, + type QRLEventHandlerMulti, } from '@builder.io/qwik'; import type { ActionStore } from './types'; import { useNavigate } from './use-functions'; @@ -36,13 +37,12 @@ export interface FormProps spaReset?: boolean; /** Event handler executed right when the form is submitted. */ - onSubmit$?: (event: Event, form: HTMLFormElement) => ValueOrPromise; + onSubmit$?: QRLEventHandlerMulti | undefined; /** Event handler executed right after the action is executed successfully and returns some data. */ - onSubmitCompleted$?: ( - event: CustomEvent>, - form: HTMLFormElement - ) => ValueOrPromise; + onSubmitCompleted$?: + | QRLEventHandlerMulti>, HTMLFormElement> + | undefined; key?: string | number | null; } @@ -53,13 +53,44 @@ export const Form = ( key: string | null ) => { if (action) { + const isArrayApi = Array.isArray(onSubmit$); + // if you pass an array you can choose where you want action.submit in it + if (isArrayApi) { + return jsx( + 'form', + { + ...rest, + action: action.actionPath, + 'preventdefault:submit': !reloadDocument, + onSubmit$: [ + ...onSubmit$, + // action.submit "submitcompleted" event for onSubmitCompleted$ events + !reloadDocument + ? $((evt: SubmitEvent) => { + if (!action.submitted) { + return action.submit(evt); + } + }) + : undefined, + ], + method: 'post', + ['data-spa-reset']: spaReset ? 'true' : undefined, + }, + key + ); + } return jsx( 'form', { ...rest, action: action.actionPath, 'preventdefault:submit': !reloadDocument, - onSubmit$: [!reloadDocument ? action.submit : undefined, onSubmit$], + onSubmit$: [ + // action.submit "submitcompleted" event for onSubmitCompleted$ events + !reloadDocument ? action.submit : undefined, + // TODO: v2 breaking change this should fire before the action.submit + onSubmit$, + ], method: 'post', ['data-spa-reset']: spaReset ? 'true' : undefined, }, @@ -87,15 +118,19 @@ export const GetForm = component$>( preventdefault:submit={!reloadDocument} data-spa-reset={spaReset ? 'true' : undefined} {...rest} - onSubmit$={async (_, form) => { - const formData = new FormData(form); - const params = new URLSearchParams(); - formData.forEach((value, key) => { - if (typeof value === 'string') { - params.append(key, value); - } - }); - nav('?' + params.toString(), { type: 'form', forceReload: true }).then(() => { + onSubmit$={[ + ...(Array.isArray(onSubmit$) ? onSubmit$ : [onSubmit$]), + $(async (_evt, form) => { + const formData = new FormData(form); + const params = new URLSearchParams(); + formData.forEach((value, key) => { + if (typeof value === 'string') { + params.append(key, value); + } + }); + await nav('?' + params.toString(), { type: 'form', forceReload: true }); + }), + $((_evt, form) => { if (form.getAttribute('data-spa-reset') === 'true') { form.reset(); } @@ -109,8 +144,10 @@ export const GetForm = component$>( }, }) ); - }); - }} + // + }), + // end of array + ]} > diff --git a/packages/qwik-city/runtime/src/server-functions.ts b/packages/qwik-city/runtime/src/server-functions.ts index 8acae378a41..1f31b01a272 100644 --- a/packages/qwik-city/runtime/src/server-functions.ts +++ b/packages/qwik-city/runtime/src/server-functions.ts @@ -57,6 +57,7 @@ export const routeActionQrl = (( const currentAction = useAction(); const initialState: Editable>> = { actionPath: `?${QACTION_KEY}=${id}`, + submitted: false, isRunning: false, status: undefined, value: undefined, @@ -104,6 +105,7 @@ Action.run() can only be called on the browser, for example when a user clicks a if (data instanceof FormData) { state.formData = data; } + state.submitted = true; state.isRunning = true; loc.isNavigating = true; currentAction.value = { diff --git a/packages/qwik-city/runtime/src/types.ts b/packages/qwik-city/runtime/src/types.ts index 913307bf7e2..072f5ff7dd2 100644 --- a/packages/qwik-city/runtime/src/types.ts +++ b/packages/qwik-city/runtime/src/types.ts @@ -677,6 +677,8 @@ export type ActionStore = { ? (form?: INPUT | FormData | SubmitEvent) => Promise> : (form: INPUT | FormData | SubmitEvent) => Promise> >; + /** Is action.submit was submitted */ + readonly submitted: boolean; }; type Failed = { diff --git a/starters/apps/qwikcity-test/src/routes/(common)/actions/multiple-handlers/index.tsx b/starters/apps/qwikcity-test/src/routes/(common)/actions/multiple-handlers/index.tsx new file mode 100644 index 00000000000..8e0dd5fc08b --- /dev/null +++ b/starters/apps/qwikcity-test/src/routes/(common)/actions/multiple-handlers/index.tsx @@ -0,0 +1,50 @@ +import { $, component$, useSignal } from "@builder.io/qwik"; +import { Form, routeAction$ } from "@builder.io/qwik-city"; + +export const useDotNotationAction = routeAction$(async (payload) => { + return { + success: true, + payload: payload, + }; +}); + +export default component$(() => { + const finished = useSignal(false); + const dotNotation = useDotNotationAction(); + + return ( + <> +

Dot Notation Form Inputs

+
{ + finished.value = false; + }), + $((evt) => dotNotation.submit(evt)), + $(() => { + finished.value = dotNotation.submitted; + }), + ]} + id="dot-notation-form" + > + + + + + + + + +
+ {dotNotation.value?.success && ( +
+ {JSON.stringify(dotNotation.value.payload)} +
+ )} +
{String(finished.value)}
+ + ); +}); diff --git a/starters/e2e/qwikcity/actions.spec.ts b/starters/e2e/qwikcity/actions.spec.ts index 88e3eb0c46e..2c997637364 100644 --- a/starters/e2e/qwikcity/actions.spec.ts +++ b/starters/e2e/qwikcity/actions.spec.ts @@ -3,12 +3,12 @@ import { expect, test } from "@playwright/test"; test.describe("actions", () => { test.describe("mpa", () => { test.use({ javaScriptEnabled: false }); - tests(); + MPA_and_SPA_tests(); }); test.describe("spa", () => { test.use({ javaScriptEnabled: true }); - tests(); + MPA_and_SPA_tests(); test.describe("issue4679", () => { test("should serialize Form without action", async ({ page }) => { @@ -19,9 +19,23 @@ test.describe("actions", () => { await expect(button).toHaveText("Toggle True"); }); }); + test.describe("multiple-handlers", () => { + test("should allow multiple handlers", async ({ page }) => { + await page.goto("/qwikcity-test/actions/multiple-handlers/"); + const success = page.locator("#multiple-handlers-success"); + + await expect(success).toBeHidden(); + await page.locator("#multiple-handlers-button").click(); + await expect(success).toHaveText( + '{"arrayOld":["0","1"],"arrayNew":["0","1"],"people":[{"name":"Fred"},{"name":"Sam"}]}', + ); + const finished = page.locator("#multiple-handlers-finished"); + await expect(finished).toContainText("true"); + }); + }); }); - function tests() { + function MPA_and_SPA_tests() { test.describe("login form", () => { test.beforeEach(async ({ page }) => { await page.goto("/qwikcity-test/actions/"); From a2fb6e3dff8d31ea10c5ef3b86c387144d260936 Mon Sep 17 00:00:00 2001 From: YURII D Date: Fri, 10 May 2024 19:20:09 +0300 Subject: [PATCH 006/412] docs: Update pages.json (#6295) --- packages/docs/scripts/pages.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/docs/scripts/pages.json b/packages/docs/scripts/pages.json index ebbc0a5f358..419865885d2 100644 --- a/packages/docs/scripts/pages.json +++ b/packages/docs/scripts/pages.json @@ -187,5 +187,9 @@ { "href": "https://numeia.com", "tags": "saas, marketing, ai" + }, + { + "href": "https://qit.tools/", + "tags": "online,tools,converters" } ] From 434737e01434c3c3caecfb0a6e1d747cc4f49b1c Mon Sep 17 00:00:00 2001 From: PatrickJS Date: Fri, 10 May 2024 11:58:18 -0700 Subject: [PATCH 007/412] feat(server$): config argument, optional GET, ServerError (#6290) --- .../api.json | 48 ++++++++++ .../index.md | 93 +++++++++++++++++++ .../docs/src/routes/api/qwik-city/api.json | 4 +- .../docs/src/routes/api/qwik-city/index.md | 19 +++- .../middleware/request-handler/api.md | 9 ++ .../request-handler/error-handler.ts | 10 ++ .../middleware/request-handler/index.ts | 2 +- .../request-handler/request-event.ts | 15 ++- .../resolve-request-handlers.ts | 10 +- packages/qwik-city/runtime/src/api.md | 7 +- packages/qwik-city/runtime/src/constants.ts | 2 + .../qwik-city/runtime/src/server-functions.ts | 36 +++++-- packages/qwik-city/runtime/src/types.ts | 17 ++++ .../components/router-head/router-head.tsx | 16 +++- .../server-func/server-configs/index.tsx | 60 ++++++++++++ .../server-func/server-error/index.tsx | 45 +++++++++ starters/e2e/qwikcity/server.spec.ts | 18 +++- 17 files changed, 391 insertions(+), 20 deletions(-) create mode 100644 starters/apps/qwikcity-test/src/routes/(common)/server-func/server-configs/index.tsx create mode 100644 starters/apps/qwikcity-test/src/routes/(common)/server-func/server-error/index.tsx diff --git a/packages/docs/src/routes/api/qwik-city-middleware-request-handler/api.json b/packages/docs/src/routes/api/qwik-city-middleware-request-handler/api.json index 361ef7206ab..4196fe13afe 100644 --- a/packages/docs/src/routes/api/qwik-city-middleware-request-handler/api.json +++ b/packages/docs/src/routes/api/qwik-city-middleware-request-handler/api.json @@ -86,6 +86,23 @@ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/types.ts", "mdFile": "qwik-city.cookievalue.md" }, + { + "name": "data", + "id": "servererror-data", + "hierarchy": [ + { + "name": "ServerError", + "id": "servererror-data" + }, + { + "name": "data", + "id": "servererror-data" + } + ], + "kind": "Property", + "content": "```typescript\ndata: T;\n```", + "mdFile": "qwik-city.servererror.data.md" + }, { "name": "DeferReturn", "id": "deferreturn", @@ -367,6 +384,20 @@ "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/types.ts", "mdFile": "qwik-city.resolvevalue.md" }, + { + "name": "ServerError", + "id": "servererror", + "hierarchy": [ + { + "name": "ServerError", + "id": "servererror" + } + ], + "kind": "Class", + "content": "```typescript\nexport declare class ServerError> extends Error \n```\n**Extends:** Error\n\n\n\n\n
\n\nConstructor\n\n\n\n\nModifiers\n\n\n\n\nDescription\n\n\n
\n\n[(constructor)(status, data)](#)\n\n\n\n\n\n\n\nConstructs a new instance of the `ServerError` class\n\n\n
\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[data](#servererror-data)\n\n\n\n\n\n\n\nT\n\n\n\n\n\n
\n\n[status](#servererror-status)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/error-handler.ts", + "mdFile": "qwik-city.servererror.md" + }, { "name": "ServerRenderOptions", "id": "serverrenderoptions", @@ -439,6 +470,23 @@ "kind": "MethodSignature", "content": "Sets a `Response` cookie header using the `Set-Cookie` header.\n\n\n```typescript\nset(name: string, value: string | number | Record, options?: CookieOptions): void;\n```\n\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nname\n\n\n\n\nstring\n\n\n\n\n\n
\n\nvalue\n\n\n\n\nstring \\| number \\| Record<string, any>\n\n\n\n\n\n
\n\noptions\n\n\n\n\n[CookieOptions](#cookieoptions)\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\nvoid", "mdFile": "qwik-city.cookie.set.md" + }, + { + "name": "status", + "id": "servererror-status", + "hierarchy": [ + { + "name": "ServerError", + "id": "servererror-status" + }, + { + "name": "status", + "id": "servererror-status" + } + ], + "kind": "Property", + "content": "```typescript\nstatus: number;\n```", + "mdFile": "qwik-city.servererror.status.md" } ] } \ No newline at end of file diff --git a/packages/docs/src/routes/api/qwik-city-middleware-request-handler/index.md b/packages/docs/src/routes/api/qwik-city-middleware-request-handler/index.md index 5ae6029ce37..033c7bff0d0 100644 --- a/packages/docs/src/routes/api/qwik-city-middleware-request-handler/index.md +++ b/packages/docs/src/routes/api/qwik-city-middleware-request-handler/index.md @@ -359,6 +359,12 @@ string [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/types.ts) +## data + +```typescript +data: T; +``` + ## DeferReturn ```typescript @@ -1382,6 +1388,87 @@ export interface ResolveValue [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/types.ts) +## ServerError + +```typescript +export declare class ServerError> extends Error +``` + +**Extends:** Error + + + +
+ +Constructor + + + +Modifiers + + + +Description + +
+ +[(constructor)(status, data)](#) + + + + + +Constructs a new instance of the `ServerError` class + +
+ + + + +
+ +Property + + + +Modifiers + + + +Type + + + +Description + +
+ +[data](#servererror-data) + + + + + +T + + + +
+ +[status](#servererror-status) + + + + + +number + + + +
+ +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/middleware/request-handler/error-handler.ts) + ## ServerRenderOptions ```typescript @@ -1675,3 +1762,9 @@ _(Optional)_ **Returns:** void + +## status + +```typescript +status: number; +``` diff --git a/packages/docs/src/routes/api/qwik-city/api.json b/packages/docs/src/routes/api/qwik-city/api.json index 310774e9e1b..ebab18cf9fe 100644 --- a/packages/docs/src/routes/api/qwik-city/api.json +++ b/packages/docs/src/routes/api/qwik-city/api.json @@ -670,7 +670,7 @@ } ], "kind": "Function", - "content": "```typescript\nserver$: (first: T) => ServerQRL\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nfirst\n\n\n\n\nT\n\n\n\n\n\n
\n**Returns:**\n\n[ServerQRL](#serverqrl)<T>", + "content": "```typescript\nserver$: (first: T, options?: ServerConfig | undefined) => ServerQRL\n```\n\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nfirst\n\n\n\n\nT\n\n\n\n\n\n
\n\noptions\n\n\n\n\nServerConfig \\| undefined\n\n\n\n\n_(Optional)_\n\n\n
\n**Returns:**\n\n[ServerQRL](#serverqrl)<T>", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/runtime/src/server-functions.ts", "mdFile": "qwik-city.server_.md" }, @@ -684,7 +684,7 @@ } ], "kind": "TypeAlias", - "content": "```typescript\nexport type ServerFunction = {\n (this: RequestEventBase, ...args: any[]): any;\n};\n```", + "content": "```typescript\nexport type ServerFunction = {\n (this: RequestEventBase, ...args: any[]): any;\n options?: ServerConfig;\n};\n```", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-city/runtime/src/types.ts", "mdFile": "qwik-city.serverfunction.md" }, diff --git a/packages/docs/src/routes/api/qwik-city/index.md b/packages/docs/src/routes/api/qwik-city/index.md index cdd1405f765..6e7a4e53303 100644 --- a/packages/docs/src/routes/api/qwik-city/index.md +++ b/packages/docs/src/routes/api/qwik-city/index.md @@ -2128,7 +2128,10 @@ RouterOutlet: import("@builder.io/qwik").Component; ## server$ ```typescript -server$: (first: T) => ServerQRL; +server$: ( + first: T, + options?: ServerConfig | undefined, +) => ServerQRL; ``` +
@@ -2154,6 +2157,19 @@ T +
+ +options + + + +ServerConfig \| undefined + + + +_(Optional)_ +
**Returns:** @@ -2167,6 +2183,7 @@ T ```typescript export type ServerFunction = { (this: RequestEventBase, ...args: any[]): any; + options?: ServerConfig; }; ``` diff --git a/packages/qwik-city/middleware/request-handler/api.md b/packages/qwik-city/middleware/request-handler/api.md index d7647018c74..17ed10f79b5 100644 --- a/packages/qwik-city/middleware/request-handler/api.md +++ b/packages/qwik-city/middleware/request-handler/api.md @@ -174,6 +174,15 @@ export interface ResolveValue { (action: Action): Promise; } +// @public (undocumented) +export class ServerError> extends Error { + constructor(status: number, data: T); + // (undocumented) + data: T; + // (undocumented) + status: number; +} + // @public (undocumented) export interface ServerRenderOptions extends RenderOptions { checkOrigin?: boolean; diff --git a/packages/qwik-city/middleware/request-handler/error-handler.ts b/packages/qwik-city/middleware/request-handler/error-handler.ts index 6b075f347cf..a8c8bace6ea 100644 --- a/packages/qwik-city/middleware/request-handler/error-handler.ts +++ b/packages/qwik-city/middleware/request-handler/error-handler.ts @@ -1,3 +1,13 @@ +/** @public */ +export class ServerError> extends Error { + constructor( + public status: number, + public data: T + ) { + super(); + } +} + export class ErrorResponse extends Error { constructor( public status: number, diff --git a/packages/qwik-city/middleware/request-handler/index.ts b/packages/qwik-city/middleware/request-handler/index.ts index d009a89169b..ae15ca08982 100644 --- a/packages/qwik-city/middleware/request-handler/index.ts +++ b/packages/qwik-city/middleware/request-handler/index.ts @@ -1,4 +1,4 @@ -export { getErrorHtml } from './error-handler'; +export { getErrorHtml, ServerError } from './error-handler'; export { mergeHeadersCookies } from './cookie'; export { AbortMessage, RedirectMessage } from './redirect-handler'; export { requestHandler } from './request-handler'; diff --git a/packages/qwik-city/middleware/request-handler/request-event.ts b/packages/qwik-city/middleware/request-handler/request-event.ts index 2d43574eacb..506387b5530 100644 --- a/packages/qwik-city/middleware/request-handler/request-event.ts +++ b/packages/qwik-city/middleware/request-handler/request-event.ts @@ -26,6 +26,7 @@ import type { ValueOrPromise } from '@builder.io/qwik'; import type { QwikManifest, ResolvedManifest } from '@builder.io/qwik/optimizer'; import { IsQData, QDATA_JSON, QDATA_JSON_LEN } from './user-response'; import { isPromise } from './../../runtime/src/utils'; +import { QDATA_KEY } from '../../runtime/src/constants'; const RequestEvLoaders = Symbol('RequestEvLoaders'); const RequestEvMode = Symbol('RequestEvMode'); @@ -243,7 +244,7 @@ export function createRequestEvent( if (requestData !== undefined) { return requestData; } - return (requestData = parseRequest(requestEv.request, sharedMap, qwikSerializer)); + return (requestData = parseRequest(requestEv, sharedMap, qwikSerializer)); }, json: (statusCode: number, data: any) => { @@ -313,7 +314,7 @@ export function getRequestMode(requestEv: RequestEventCommon) { const ABORT_INDEX = Number.MAX_SAFE_INTEGER; const parseRequest = async ( - request: Request, + { request, method, query }: RequestEventInternal, sharedMap: Map, qwikSerializer: QwikSerializer ): Promise => { @@ -326,6 +327,16 @@ const parseRequest = async ( const data = await request.json(); return data; } else if (type === 'application/qwik-json') { + if (method === 'GET' && query.has(QDATA_KEY)) { + const data = query.get(QDATA_KEY); + if (data) { + try { + return qwikSerializer._deserializeData(decodeURIComponent(data)); + } catch (err) { + // + } + } + } return qwikSerializer._deserializeData(await request.text()); } return undefined; diff --git a/packages/qwik-city/middleware/request-handler/resolve-request-handlers.ts b/packages/qwik-city/middleware/request-handler/resolve-request-handlers.ts index fb673d0b439..d44edd4d484 100644 --- a/packages/qwik-city/middleware/request-handler/resolve-request-handlers.ts +++ b/packages/qwik-city/middleware/request-handler/resolve-request-handlers.ts @@ -26,6 +26,7 @@ import type { Render, RenderToStringResult } from '@builder.io/qwik/server'; import type { QRL, _deserializeData, _serializeData } from '@builder.io/qwik'; import { getQwikCityServerData } from './response-page'; import { RedirectMessage } from './redirect-handler'; +import { ServerError } from './error-handler'; export const resolveRequestHandlers = ( serverPlugins: RouteModule[] | undefined, @@ -59,9 +60,11 @@ export const resolveRequestHandlers = ( requestHandlers.unshift(csrfCheckMiddleware); } if (isPageRoute) { - if (method === 'POST') { + // server$ + if (method === 'POST' || method === 'GET') { requestHandlers.push(pureServerFunction); } + requestHandlers.push(fixTrailingSlash); requestHandlers.push(renderQData); } @@ -306,6 +309,11 @@ async function pureServerFunction(ev: RequestEvent) { result = await (qrl as Function).apply(ev, args); } } catch (err) { + if (err instanceof ServerError) { + ev.headers.set('Content-Type', 'application/qwik-json'); + ev.send(err.status, await qwikSerializer._serializeData(err.data, true)); + return; + } ev.headers.set('Content-Type', 'application/qwik-json'); ev.send(500, await qwikSerializer._serializeData(err, true)); return; diff --git a/packages/qwik-city/runtime/src/api.md b/packages/qwik-city/runtime/src/api.md index 124880e93b8..4d561ad2670 100644 --- a/packages/qwik-city/runtime/src/api.md +++ b/packages/qwik-city/runtime/src/api.md @@ -412,19 +412,22 @@ export type RouteNavigate = QRL<(path?: string, options?: { // @public (undocumented) export const RouterOutlet: Component; +// Warning: (ae-forgotten-export) The symbol "ServerConfig" needs to be exported by the entry point index.d.ts +// // @public (undocumented) -export const server$: (first: T) => ServerQRL; +export const server$: (first: T, options?: ServerConfig | undefined) => ServerQRL; // @public (undocumented) export type ServerFunction = { (this: RequestEventBase, ...args: any[]): any; + options?: ServerConfig; }; // @public export type ServerQRL = QRL<((abort: AbortSignal, ...args: Parameters) => ReturnType) | ((...args: Parameters) => ReturnType)>; // @public (undocumented) -export const serverQrl: (qrl: QRL) => ServerQRL; +export const serverQrl: (qrl: QRL, options?: ServerConfig) => ServerQRL; // @public (undocumented) export const ServiceWorkerRegister: (props: { diff --git a/packages/qwik-city/runtime/src/constants.ts b/packages/qwik-city/runtime/src/constants.ts index 88bec55e1b8..c54e0a9a517 100644 --- a/packages/qwik-city/runtime/src/constants.ts +++ b/packages/qwik-city/runtime/src/constants.ts @@ -9,3 +9,5 @@ export const PREFETCHED_NAVIGATE_PATHS = new Set(); export const QACTION_KEY = 'qaction'; export const QFN_KEY = 'qfunc'; + +export const QDATA_KEY = 'qdata'; diff --git a/packages/qwik-city/runtime/src/server-functions.ts b/packages/qwik-city/runtime/src/server-functions.ts index 1f31b01a272..1088f78361d 100644 --- a/packages/qwik-city/runtime/src/server-functions.ts +++ b/packages/qwik-city/runtime/src/server-functions.ts @@ -14,7 +14,7 @@ import { } from '@builder.io/qwik'; import type { RequestEventLoader } from '../../middleware/request-handler/types'; -import { QACTION_KEY, QFN_KEY } from './constants'; +import { QACTION_KEY, QFN_KEY, QDATA_KEY } from './constants'; import { RouteStateContext } from './contexts'; import type { ActionConstructor, @@ -40,6 +40,7 @@ import type { ServerFunction, ServerQRL, RequestEventBase, + ServerConfig, } from './types'; import { useAction, useLocation, useQwikCityEnv } from './use-functions'; import { z } from 'zod'; @@ -268,7 +269,10 @@ export const zodQrl = (( export const zod$ = /*#__PURE__*/ implicit$FirstArg(zodQrl) as ZodConstructor; /** @public */ -export const serverQrl = (qrl: QRL): ServerQRL => { +export const serverQrl = ( + qrl: QRL, + options?: ServerConfig +): ServerQRL => { if (isServer) { const captured = qrl.getCaptured(); if (captured && captured.length > 0 && !_getContextElement()) { @@ -276,8 +280,14 @@ export const serverQrl = (qrl: QRL): ServerQRL = } } - function stuff() { + const method = options?.method?.toUpperCase?.() || 'POST'; + const headers = options?.headers || {}; + const origin = options?.origin || ''; + const fetchOptions = options?.fetchOptions || {}; + + function rpc() { return $(async function (this: RequestEventBase, ...args: Parameters) { + // move to ServerConfig const signal = args.length > 0 && args[0] instanceof AbortSignal ? (args.shift() as AbortSignal) @@ -310,16 +320,26 @@ export const serverQrl = (qrl: QRL): ServerQRL = }); const hash = qrl.getHash(); // Handled by `pureServerFunction` middleware - const res = await fetch(`?${QFN_KEY}=${hash}`, { - method: 'POST', + let query = ''; + const config = { + ...fetchOptions, + method, headers: { + ...headers, 'Content-Type': 'application/qwik-json', // Required so we don't call accidentally 'X-QRL': hash, }, signal, - body: await _serializeData([qrl, ...filtered], false), - }); + }; + const body = await _serializeData([qrl, ...filtered], false); + if (method === 'GET') { + query += `&${QDATA_KEY}=${encodeURIComponent(body)}`; + } else { + // PatrickJS: sorry Ryan Florence I prefer const still + config.body = body; + } + const res = await fetch(`${origin}?${QFN_KEY}=${hash}${query}`, config); const contentType = res.headers.get('Content-Type'); if (res.ok && contentType === 'text/qwik-json-stream' && res.body) { @@ -349,7 +369,7 @@ export const serverQrl = (qrl: QRL): ServerQRL = } }) as ServerQRL; } - return stuff(); + return rpc(); }; /** @public */ diff --git a/packages/qwik-city/runtime/src/types.ts b/packages/qwik-city/runtime/src/types.ts index 072f5ff7dd2..dafa4635fcc 100644 --- a/packages/qwik-city/runtime/src/types.ts +++ b/packages/qwik-city/runtime/src/types.ts @@ -789,9 +789,26 @@ export type ZodConstructorQRL = { ): TypedDataValidator; }; +/** @public */ +export interface ServerConfig { + // TODO: create id registry + // id?: string; + origin?: string; + // TODO: recreate sending arguments as queryParams + // only support "get" and "post" for now + method?: 'get' | 'post'; // | 'patch' | 'delete'; + headers?: Record; + // TODO: add cache interface + // cache?: any, + // TODO: cancel with signal + // signal?: Signal; + fetchOptions?: any; +} + /** @public */ export type ServerFunction = { (this: RequestEventBase, ...args: any[]): any; + options?: ServerConfig; }; /** diff --git a/starters/apps/qwikcity-test/src/components/router-head/router-head.tsx b/starters/apps/qwikcity-test/src/components/router-head/router-head.tsx index fc34007bc2a..3b1b5f4c7ad 100644 --- a/starters/apps/qwikcity-test/src/components/router-head/router-head.tsx +++ b/starters/apps/qwikcity-test/src/components/router-head/router-head.tsx @@ -27,11 +27,23 @@ export const RouterHead = component$(() => { ))} {head.styles.map((s) => ( - @@ -1123,7 +1123,7 @@ test('component useStylesScoped()', async () => { , ` - + From b35665d677022fc8c33c7959c41a4f185590963e Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Mon, 17 Jun 2024 22:24:07 +0200 Subject: [PATCH 185/412] refactor(optimizer): nicer module finding + fix dev (#6556) - refactored manifest generation - dev server collects QRL parents during ssr build - optimizer also stores dev info for noop QRLs - dev server also uses dev info as an extra fallback - dev source URLs are kept similar looking to the source tree - QRLs are annotated with parent so they can be built on demand --- .../src/routes/api/qwik-optimizer/api.json | 4 +- .../src/routes/api/qwik-optimizer/index.md | 13 +- packages/docs/src/routes/api/qwik/api.json | 2 +- packages/docs/src/routes/api/qwik/index.md | 2 +- packages/qwik/src/core/api.md | 5 +- packages/qwik/src/core/internal.ts | 2 +- packages/qwik/src/core/platform/types.ts | 3 +- packages/qwik/src/core/qrl/qrl.ts | 17 +- packages/qwik/src/core/qrl/qrl.unit.ts | 6 + packages/qwik/src/core/use/use-task.ts | 4 + ...est__example_derived_signals_children.snap | 2 +- ...re__test__example_derived_signals_cmp.snap | 2 +- ...ple_derived_signals_complext_children.snap | 2 +- ...re__test__example_derived_signals_div.snap | 2 +- ...ple_derived_signals_multiple_children.snap | 2 +- ..._core__test__example_dev_mode_inlined.snap | 2 +- ...example_immutable_function_components.snap | 2 +- .../qwik_core__test__example_issue_33443.snap | 2 +- .../qwik_core__test__example_issue_4438.snap | 2 +- ..._core__test__example_mutable_children.snap | 2 +- ...wik_core__test__example_noop_dev_mode.snap | 122 +++++++++++ ...ore__test__example_preserve_filenames.snap | 2 +- ...core__test__example_qwik_react_inline.snap | 2 +- ...core__test__example_strip_client_code.snap | 2 +- ...core__test__example_transpile_ts_only.snap | 2 +- .../snapshots/qwik_core__test__issue_476.snap | 2 +- .../qwik_core__test__special_jsx.snap | 2 +- packages/qwik/src/optimizer/core/src/test.rs | 38 ++++ .../qwik/src/optimizer/core/src/transform.rs | 17 +- packages/qwik/src/optimizer/core/src/words.rs | 1 + packages/qwik/src/optimizer/src/api.md | 9 +- packages/qwik/src/optimizer/src/manifest.ts | 106 +++++----- .../qwik/src/optimizer/src/plugins/plugin.ts | 190 ++++++++---------- .../qwik/src/optimizer/src/plugins/rollup.ts | 16 +- .../{vite-server.ts => vite-dev-server.ts} | 64 +++--- .../qwik/src/optimizer/src/plugins/vite.ts | 25 +-- packages/qwik/src/optimizer/src/types.ts | 24 +-- packages/qwik/src/server/platform.ts | 12 +- packages/qwik/src/testing/platform.ts | 6 + 39 files changed, 456 insertions(+), 264 deletions(-) create mode 100644 packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_noop_dev_mode.snap rename packages/qwik/src/optimizer/src/plugins/{vite-server.ts => vite-dev-server.ts} (86%) diff --git a/packages/docs/src/routes/api/qwik-optimizer/api.json b/packages/docs/src/routes/api/qwik-optimizer/api.json index a1b1a9c55d3..1002986fa93 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/api.json +++ b/packages/docs/src/routes/api/qwik-optimizer/api.json @@ -400,7 +400,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface QwikManifest \n```\n\n\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[bundles](#)\n\n\n\n\n\n\n\n{ \\[fileName: string\\]: [QwikBundle](#qwikbundle); }\n\n\n\n\n\n
\n\n[injections?](#)\n\n\n\n\n\n\n\n[GlobalInjections](#globalinjections)\\[\\]\n\n\n\n\n_(Optional)_\n\n\n
\n\n[manifestHash](#)\n\n\n\n\n\n\n\nstring\n\n\n\n\n\n
\n\n[mapping](#)\n\n\n\n\n\n\n\n{ \\[symbolName: string\\]: string; }\n\n\n\n\n\n
\n\n[options?](#)\n\n\n\n\n\n\n\n{ target?: string; buildMode?: string; entryStrategy?: { \\[key: string\\]: any; }; }\n\n\n\n\n_(Optional)_\n\n\n
\n\n[platform?](#)\n\n\n\n\n\n\n\n{ \\[name: string\\]: string; }\n\n\n\n\n_(Optional)_\n\n\n
\n\n[symbols](#)\n\n\n\n\n\n\n\n{ \\[symbolName: string\\]: [QwikSymbol](#qwiksymbol); }\n\n\n\n\n\n
\n\n[version](#)\n\n\n\n\n\n\n\nstring\n\n\n\n\n\n
", + "content": "The metadata of the build. One of its uses is storing where QRL symbols are located.\n\n\n```typescript\nexport interface QwikManifest \n```\n\n\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[bundles](#)\n\n\n\n\n\n\n\n{ \\[fileName: string\\]: [QwikBundle](#qwikbundle); }\n\n\n\n\nAll code bundles, used to know the import graph\n\n\n
\n\n[injections?](#)\n\n\n\n\n\n\n\n[GlobalInjections](#globalinjections)\\[\\]\n\n\n\n\n_(Optional)_ CSS etc to inject in the document head\n\n\n
\n\n[manifestHash](#)\n\n\n\n\n\n\n\nstring\n\n\n\n\nContent hash of the manifest, if this changes, the code changed\n\n\n
\n\n[mapping](#)\n\n\n\n\n\n\n\n{ \\[symbolName: string\\]: string; }\n\n\n\n\nWhere QRLs are located\n\n\n
\n\n[options?](#)\n\n\n\n\n\n\n\n{ target?: string; buildMode?: string; entryStrategy?: { \\[key: string\\]: any; }; }\n\n\n\n\n_(Optional)_\n\n\n
\n\n[platform?](#)\n\n\n\n\n\n\n\n{ \\[name: string\\]: string; }\n\n\n\n\n_(Optional)_\n\n\n
\n\n[symbols](#)\n\n\n\n\n\n\n\n{ \\[symbolName: string\\]: [QwikSymbol](#qwiksymbol); }\n\n\n\n\nQRL symbols\n\n\n
\n\n[version](#)\n\n\n\n\n\n\n\nstring\n\n\n\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/types.ts", "mdFile": "qwik.qwikmanifest.md" }, @@ -644,7 +644,7 @@ } ], "kind": "TypeAlias", - "content": "```typescript\nexport type SymbolMapperFn = (symbolName: string, mapper: SymbolMapper | undefined) => readonly [symbol: string, chunk: string] | undefined;\n```\n**References:** [SymbolMapper](#symbolmapper)", + "content": "```typescript\nexport type SymbolMapperFn = (symbolName: string, mapper: SymbolMapper | undefined, parent?: string) => readonly [symbol: string, chunk: string] | undefined;\n```\n**References:** [SymbolMapper](#symbolmapper)", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/types.ts", "mdFile": "qwik.symbolmapperfn.md" }, diff --git a/packages/docs/src/routes/api/qwik-optimizer/index.md b/packages/docs/src/routes/api/qwik-optimizer/index.md index f658f242106..3f64e1d494a 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/index.md +++ b/packages/docs/src/routes/api/qwik-optimizer/index.md @@ -1547,6 +1547,8 @@ _(Optional)_ ## QwikManifest +The metadata of the build. One of its uses is storing where QRL symbols are located. + ```typescript export interface QwikManifest ``` @@ -1580,6 +1582,8 @@ Description +All code bundles, used to know the import graph + @@ -1593,7 +1597,7 @@ Description -_(Optional)_ +_(Optional)_ CSS etc to inject in the document head @@ -1608,6 +1612,8 @@ string +Content hash of the manifest, if this changes, the code changed + @@ -1621,6 +1627,8 @@ string +Where QRLs are located + @@ -1664,6 +1672,8 @@ _(Optional)_ +QRL symbols + @@ -2758,6 +2768,7 @@ export type SymbolMapper = Record< export type SymbolMapperFn = ( symbolName: string, mapper: SymbolMapper | undefined, + parent?: string, ) => readonly [symbol: string, chunk: string] | undefined; ``` diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json index c99a639c616..ab33e84b6fd 100644 --- a/packages/docs/src/routes/api/qwik/api.json +++ b/packages/docs/src/routes/api/qwik/api.json @@ -544,7 +544,7 @@ } ], "kind": "Interface", - "content": "Low-level API for platform abstraction.\n\nDifferent platforms (browser, node, service workers) may have different ways of handling things such as `requestAnimationFrame` and imports. To make Qwik platform-independent Qwik uses the `CorePlatform` API to access the platform API.\n\n`CorePlatform` also is responsible for importing symbols. The import map is different on the client (browser) then on the server. For this reason, the server has a manifest that is used to map symbols to javascript chunks. The manifest is encapsulated in `CorePlatform`, for this reason, the `CorePlatform` can't be global as there may be multiple applications running at server concurrently.\n\nThis is a low-level API and there should not be a need for you to access this.\n\n\n```typescript\nexport interface CorePlatform \n```\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[chunkForSymbol](#)\n\n\n\n\n\n\n\n(symbolName: string, chunk: string \\| null) => readonly \\[symbol: string, chunk: string\\] \\| undefined\n\n\n\n\nRetrieve chunk name for the symbol.\n\nWhen the application is running on the server the symbols may be imported from different files (as server build is typically a single javascript chunk.) For this reason, it is necessary to convert the chunks from server format to client (browser) format. This is done by looking up symbols (which are globally unique) in the manifest. (Manifest is the mapping of symbols to the client chunk names.)\n\n\n
\n\n[importSymbol](#)\n\n\n\n\n\n\n\n(containerEl: Element \\| undefined, url: string \\| URL \\| undefined \\| null, symbol: string) => [ValueOrPromise](#valueorpromise)<any>\n\n\n\n\nRetrieve a symbol value from QRL.\n\nQwik needs to lazy load data and closures. For this Qwik uses QRLs that are serializable references of resources that are needed. The QRLs contain all the information necessary to retrieve the reference using `importSymbol`.\n\nWhy not use `import()`? Because `import()` is relative to the current file, and the current file is always the Qwik framework. So QRLs have additional information that allows them to serialize imports relative to application base rather than the Qwik framework file.\n\n\n
\n\n[isServer](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\nTrue of running on the server platform.\n\n\n
\n\n[nextTick](#)\n\n\n\n\n\n\n\n(fn: () => any) => Promise<any>\n\n\n\n\nPerform operation on next tick.\n\n\n
\n\n[raf](#)\n\n\n\n\n\n\n\n(fn: () => any) => Promise<any>\n\n\n\n\nPerform operation on next request-animation-frame.\n\n\n
", + "content": "Low-level API for platform abstraction.\n\nDifferent platforms (browser, node, service workers) may have different ways of handling things such as `requestAnimationFrame` and imports. To make Qwik platform-independent Qwik uses the `CorePlatform` API to access the platform API.\n\n`CorePlatform` also is responsible for importing symbols. The import map is different on the client (browser) then on the server. For this reason, the server has a manifest that is used to map symbols to javascript chunks. The manifest is encapsulated in `CorePlatform`, for this reason, the `CorePlatform` can't be global as there may be multiple applications running at server concurrently.\n\nThis is a low-level API and there should not be a need for you to access this.\n\n\n```typescript\nexport interface CorePlatform \n```\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[chunkForSymbol](#)\n\n\n\n\n\n\n\n(symbolName: string, chunk: string \\| null, parent?: string) => readonly \\[symbol: string, chunk: string\\] \\| undefined\n\n\n\n\nRetrieve chunk name for the symbol.\n\nWhen the application is running on the server the symbols may be imported from different files (as server build is typically a single javascript chunk.) For this reason, it is necessary to convert the chunks from server format to client (browser) format. This is done by looking up symbols (which are globally unique) in the manifest. (Manifest is the mapping of symbols to the client chunk names.)\n\n\n
\n\n[importSymbol](#)\n\n\n\n\n\n\n\n(containerEl: Element \\| undefined, url: string \\| URL \\| undefined \\| null, symbol: string) => [ValueOrPromise](#valueorpromise)<any>\n\n\n\n\nRetrieve a symbol value from QRL.\n\nQwik needs to lazy load data and closures. For this Qwik uses QRLs that are serializable references of resources that are needed. The QRLs contain all the information necessary to retrieve the reference using `importSymbol`.\n\nWhy not use `import()`? Because `import()` is relative to the current file, and the current file is always the Qwik framework. So QRLs have additional information that allows them to serialize imports relative to application base rather than the Qwik framework file.\n\n\n
\n\n[isServer](#)\n\n\n\n\n\n\n\nboolean\n\n\n\n\nTrue of running on the server platform.\n\n\n
\n\n[nextTick](#)\n\n\n\n\n\n\n\n(fn: () => any) => Promise<any>\n\n\n\n\nPerform operation on next tick.\n\n\n
\n\n[raf](#)\n\n\n\n\n\n\n\n(fn: () => any) => Promise<any>\n\n\n\n\nPerform operation on next request-animation-frame.\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/platform/types.ts", "mdFile": "qwik.coreplatform.md" }, diff --git a/packages/docs/src/routes/api/qwik/index.md b/packages/docs/src/routes/api/qwik/index.md index d494a041f0f..6a71eb33845 100644 --- a/packages/docs/src/routes/api/qwik/index.md +++ b/packages/docs/src/routes/api/qwik/index.md @@ -1558,7 +1558,7 @@ Description -(symbolName: string, chunk: string \| null) => readonly [symbol: string, chunk: string] \| undefined +(symbolName: string, chunk: string \| null, parent?: string) => readonly [symbol: string, chunk: string] \| undefined diff --git a/packages/qwik/src/core/api.md b/packages/qwik/src/core/api.md index f19e1aff9d0..82e20f25229 100644 --- a/packages/qwik/src/core/api.md +++ b/packages/qwik/src/core/api.md @@ -142,7 +142,7 @@ export interface ContextId { // @public export interface CorePlatform { - chunkForSymbol: (symbolName: string, chunk: string | null) => readonly [symbol: string, chunk: string] | undefined; + chunkForSymbol: (symbolName: string, chunk: string | null, parent?: string) => readonly [symbol: string, chunk: string] | undefined; importSymbol: (containerEl: Element | undefined, url: string | URL | undefined | null, symbol: string) => ValueOrPromise; isServer: boolean; nextTick: (fn: () => any) => Promise; @@ -542,6 +542,9 @@ export type NativeWheelEvent = WheelEvent; // @internal (undocumented) export const _noopQrl: (symbolName: string, lexicalScopeCapture?: any[]) => QRL; +// @internal (undocumented) +export const _noopQrlDEV: (symbolName: string, opts: QRLDev, lexicalScopeCapture?: any[]) => QRL; + // @public export type NoSerialize = (T & { __no_serialize__: true; diff --git a/packages/qwik/src/core/internal.ts b/packages/qwik/src/core/internal.ts index 57629802450..cad738b5834 100644 --- a/packages/qwik/src/core/internal.ts +++ b/packages/qwik/src/core/internal.ts @@ -1,5 +1,5 @@ export { _pauseFromContexts, _serializeData } from './container/pause'; -export { _noopQrl, _regSymbol } from './qrl/qrl'; +export { _noopQrl, _noopQrlDEV, _regSymbol } from './qrl/qrl'; export { _renderSSR } from './render/ssr/render-ssr'; export { _hW } from './render/dom/notify-render'; export { _wrapSignal, _wrapProp } from './state/signal'; diff --git a/packages/qwik/src/core/platform/types.ts b/packages/qwik/src/core/platform/types.ts index a7833c95d1d..2dd7fecec54 100644 --- a/packages/qwik/src/core/platform/types.ts +++ b/packages/qwik/src/core/platform/types.ts @@ -97,7 +97,8 @@ export interface CorePlatform { // chunkForSymbol: ( symbolName: string, - chunk: string | null + chunk: string | null, + parent?: string ) => readonly [symbol: string, chunk: string] | undefined; } diff --git a/packages/qwik/src/core/qrl/qrl.ts b/packages/qwik/src/core/qrl/qrl.ts index b48b3c16e43..fa71916643f 100644 --- a/packages/qwik/src/core/qrl/qrl.ts +++ b/packages/qwik/src/core/qrl/qrl.ts @@ -124,6 +124,17 @@ export const _noopQrl = ( return createQRL(null, symbolName, null, null, null, lexicalScopeCapture, null); }; +/** @internal */ +export const _noopQrlDEV = ( + symbolName: string, + opts: QRLDev, + lexicalScopeCapture: any[] = EMPTY_ARRAY +): QRL => { + const newQrl = _noopQrl(symbolName, lexicalScopeCapture) as QRLInternal; + newQrl.dev = opts; + return newQrl; +}; + /** @internal */ export const qrlDEV = ( chunkOrFn: string | (() => Promise), @@ -163,12 +174,14 @@ export const serializeQRL = (qrl: QRLInternal, opts: QRLSerializeOptions = {}) = const platform = getPlatform(); if (platform) { - const result = platform.chunkForSymbol(refSymbol, chunk); + const result = platform.chunkForSymbol(refSymbol, chunk, qrl.dev?.file); if (result) { chunk = result[1]; if (!qrl.$refSymbol$) { symbol = result[0]; } + } else { + console.error('serializeQRL: Cannot resolve symbol', symbol, 'in', chunk, qrl.dev?.file); } } @@ -197,7 +210,7 @@ export const serializeQRL = (qrl: QRLInternal, opts: QRLSerializeOptions = {}) = throwErrorAndStop('Sync QRL without containerState'); } } - let output = `${chunk}#${symbol}`; + let output = `${encodeURI(chunk)}#${symbol}`; const capture = qrl.$capture$; const captureRef = qrl.$captureRef$; if (captureRef && captureRef.length) { diff --git a/packages/qwik/src/core/qrl/qrl.unit.ts b/packages/qwik/src/core/qrl/qrl.unit.ts index 7f28f16d6d4..6203a8619a3 100644 --- a/packages/qwik/src/core/qrl/qrl.unit.ts +++ b/packages/qwik/src/core/qrl/qrl.unit.ts @@ -99,6 +99,12 @@ describe('serialization', () => { serializeQRL(createQRL('c', 's1', null, null, [1 as any, '2'], null, null)), 'c#s1[1 2]' ); + assert.equal( + serializeQRL( + createQRL('src/routes/[...index]/c', 's1', null, null, [1 as any, '2'], null, null) + ), + 'src/routes/%5B...index%5D/c#s1[1 2]' + ); }); test('should parse reference', () => { diff --git a/packages/qwik/src/core/use/use-task.ts b/packages/qwik/src/core/use/use-task.ts index b569bf866b4..b687efe2626 100644 --- a/packages/qwik/src/core/use/use-task.ts +++ b/packages/qwik/src/core/use/use-task.ts @@ -793,6 +793,10 @@ const getTaskHandlerQrl = (task: SubscriberEffect): QRL<(ev: Event) => void> => [task], taskQrl.$symbol$ ); + // Needed for chunk lookup in dev mode + if (taskQrl.dev) { + taskHandler.dev = taskQrl.dev; + } return taskHandler; }; diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_children.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_children.snap index 5a0df7c0d4f..19510ba041f 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_children.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_children.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2728 +assertion_line: 2749 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_cmp.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_cmp.snap index 82163dec076..4a07bd23a74 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_cmp.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_cmp.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2861 +assertion_line: 2882 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_complext_children.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_complext_children.snap index ad4c9435fde..365a3cd8514 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_complext_children.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_complext_children.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2830 +assertion_line: 2850 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_div.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_div.snap index 9fe590e94fe..3b9a2028b0c 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_div.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_div.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2640 +assertion_line: 2661 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_multiple_children.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_multiple_children.snap index 3679e3c5760..b16f3e42837 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_multiple_children.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_derived_signals_multiple_children.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2783 +assertion_line: 2804 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_dev_mode_inlined.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_dev_mode_inlined.snap index eaf9dfdf0bc..c17ff151777 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_dev_mode_inlined.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_dev_mode_inlined.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2187 +assertion_line: 2208 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_function_components.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_function_components.snap index 115e6554c99..e8589924e07 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_function_components.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_immutable_function_components.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2469 +assertion_line: 2490 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_33443.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_33443.snap index ae561cafb9a..0eabedc2669 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_33443.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_33443.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2914 +assertion_line: 2935 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_4438.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_4438.snap index 6d906afdb60..35709fa70d9 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_4438.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_issue_4438.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2704 +assertion_line: 2725 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_mutable_children.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_mutable_children.snap index d63d01af881..336b916c0fb 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_mutable_children.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_mutable_children.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2361 +assertion_line: 2382 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_noop_dev_mode.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_noop_dev_mode.snap new file mode 100644 index 00000000000..efa63976cce --- /dev/null +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_noop_dev_mode.snap @@ -0,0 +1,122 @@ +--- +source: packages/qwik/src/optimizer/core/src/test.rs +assertion_line: 3502 +expression: output +--- +==INPUT== + + +import { component$, useStore, serverStuff$, $ } from '@builder.io/qwik'; + +export const App = component$(() => { + const stuff = useStore(); + serverStuff$(async () => { + // should be removed but keep scope + console.log(stuff.count) + }) + serverStuff$(async () => { + // should be removed + }) + + return ( + +

stuff.count} + onClick$={() => console.log('warn')} + > + Hello Qwik +

+
+ ); +}); + +============================= test.js == + +import { componentQrl } from "@builder.io/qwik"; +import { qrlDEV } from "@builder.io/qwik"; +export const App = /*#__PURE__*/ componentQrl(/*#__PURE__*/ qrlDEV(()=>import("./app_component_ckepmxzlub0"), "App_component_ckEPmXZlub0", { + file: "/user/qwik/src/test.tsx", + lo: 107, + hi: 569, + displayName: "App_component" +})); + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;AAGA,OAAO,MAAM,oBAAM;;;;;IAoBhB\"}") +============================= app_component_ckepmxzlub0.js (ENTRY POINT)== + +import { _jsxC } from "@builder.io/qwik"; +import { _jsxQ } from "@builder.io/qwik"; +import { _noopQrlDEV } from "@builder.io/qwik"; +import { serverStuffQrl } from "@builder.io/qwik"; +import { useStore } from "@builder.io/qwik"; +export const App_component_ckEPmXZlub0 = ()=>{ + const stuff = useStore(); + serverStuffQrl(/*#__PURE__*/ _noopQrlDEV("App_component_serverStuff_ebyHaP15ytQ", { + file: "/user/qwik/src/test.tsx", + lo: 0, + hi: 0, + displayName: "App_component_serverStuff" + }, [ + stuff + ])); + serverStuffQrl(/*#__PURE__*/ _noopQrlDEV("App_component_serverStuff_1_PQCqO0ANabY", { + file: "/user/qwik/src/test.tsx", + lo: 0, + hi: 0, + displayName: "App_component_serverStuff_1" + })); + return /*#__PURE__*/ _jsxC(Cmp, { + children: /*#__PURE__*/ _jsxQ("p", null, { + class: "stuff", + shouldRemove$: /*#__PURE__*/ _noopQrlDEV("App_component_Cmp_p_shouldRemove_uU0MG0jvQD4", { + file: "/user/qwik/src/test.tsx", + lo: 0, + hi: 0, + displayName: "App_component_Cmp_p_shouldRemove" + }, [ + stuff + ]), + onClick$: /*#__PURE__*/ _noopQrlDEV("App_component_Cmp_p_onClick_vuXzfUTkpto", { + file: "/user/qwik/src/test.tsx", + lo: 0, + hi: 0, + displayName: "App_component_Cmp_p_onClick" + }) + }, "Hello Qwik", 3, null, { + fileName: "test.tsx", + lineNumber: 16, + columnNumber: 13 + }) + }, 3, "u6_0", { + fileName: "test.tsx", + lineNumber: 15, + columnNumber: 9 + }); +}; + + +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;yCAG8B,IAAM;IAChC,MAAM,QAAQ;IACd;;;;;;;;IAIA;;;;;;IAIA,qBACI,MAAC;kBACG,cAAA,MAAC;YAAE,OAAM;YACL,aAAa;;;;;;;;YACb,QAAQ;;;;;;WACX;;;;;;;;;;AAKb\"}") +/* +{ + "origin": "test.tsx", + "name": "App_component_ckEPmXZlub0", + "entry": null, + "displayName": "App_component", + "hash": "ckEPmXZlub0", + "canonicalFilename": "app_component_ckepmxzlub0", + "path": "", + "extension": "js", + "parent": null, + "ctxKind": "function", + "ctxName": "component$", + "captures": false, + "loc": [ + 107, + 569 + ] +} +*/ +== DIAGNOSTICS == + +[] diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_preserve_filenames.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_preserve_filenames.snap index 9a723b60fc7..65bb27d6ae4 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_preserve_filenames.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_preserve_filenames.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2546 +assertion_line: 2567 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react_inline.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react_inline.snap index ba50a96458e..a161ad0d09b 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react_inline.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_qwik_react_inline.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 3088 +assertion_line: 3109 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_client_code.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_client_code.snap index b13f8f8883b..3c3f7909cc9 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_client_code.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_strip_client_code.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 1814 +assertion_line: 1816 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_transpile_ts_only.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_transpile_ts_only.snap index 71aaee415c8..caaca8aa2f4 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_transpile_ts_only.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_transpile_ts_only.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2491 +assertion_line: 2512 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_476.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_476.snap index 467306ea848..4abf0c9f463 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_476.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__issue_476.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 1974 +assertion_line: 1976 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__special_jsx.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__special_jsx.snap index 412cfa7a10f..29aab4dc067 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__special_jsx.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__special_jsx.snap @@ -1,6 +1,6 @@ --- source: packages/qwik/src/optimizer/core/src/test.rs -assertion_line: 2165 +assertion_line: 2167 expression: output --- ==INPUT== diff --git a/packages/qwik/src/optimizer/core/src/test.rs b/packages/qwik/src/optimizer/core/src/test.rs index 46bdcf2a5f6..1662ddfe208 100644 --- a/packages/qwik/src/optimizer/core/src/test.rs +++ b/packages/qwik/src/optimizer/core/src/test.rs @@ -3497,6 +3497,44 @@ fn example_of_synchronous_qrl() { }); } +#[test] +fn example_noop_dev_mode() { + test_input!(TestInput { + code: r#" +import { component$, useStore, serverStuff$, $ } from '@builder.io/qwik'; + +export const App = component$(() => { + const stuff = useStore(); + serverStuff$(async () => { + // should be removed but keep scope + console.log(stuff.count) + }) + serverStuff$(async () => { + // should be removed + }) + + return ( + +

stuff.count} + onClick$={() => console.log('warn')} + > + Hello Qwik +

+
+ ); +}); +"# + .to_string(), + mode: EmitMode::Dev, + transpile_ts: true, + transpile_jsx: true, + strip_event_handlers: true, + strip_ctx_name: Some(vec!["server".into()]), + ..TestInput::default() + }); +} + // TODO(misko): Make this test work by implementing strict serialization. // #[test] // fn example_of_synchronous_qrl_that_cant_be_serialized() { diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index 0af05019e31..d028eb201bc 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -761,8 +761,8 @@ impl<'a> QwikTransform<'a> { .as_ref() .map(|e| { fix_path( - dbg!(&self.options.path_data.base_dir), - dbg!(&self.options.path_data.abs_dir), + &self.options.path_data.base_dir, + &self.options.path_data.abs_dir, &["./", e.as_ref()].concat(), ) .map(|f| f.to_string()) @@ -1661,6 +1661,17 @@ impl<'a> QwikTransform<'a> { value: symbol_name.into(), raw: None, }))]; + + let mut fn_name: &JsWord = &_NOOP_QRL; + if self.options.mode == EmitMode::Dev { + args.push(get_qrl_dev_obj( + &self.options.path_data.abs_path, + &hook_data, + &DUMMY_SP, + )); + fn_name = &_NOOP_QRL_DEV; + }; + // Injects state if !hook_data.scoped_idents.is_empty() { args.push(ast::Expr::Array(ast::ArrayLit { @@ -1677,7 +1688,7 @@ impl<'a> QwikTransform<'a> { .collect(), })) } - self.create_internal_call(&_NOOP_QRL, args, true) + self.create_internal_call(fn_name, args, true) } } diff --git a/packages/qwik/src/optimizer/core/src/words.rs b/packages/qwik/src/optimizer/core/src/words.rs index c6865214026..a21cc91ccd7 100644 --- a/packages/qwik/src/optimizer/core/src/words.rs +++ b/packages/qwik/src/optimizer/core/src/words.rs @@ -14,6 +14,7 @@ lazy_static! { pub static ref _INLINED_QRL: JsWord = JsWord::from("inlinedQrl"); pub static ref _INLINED_QRL_DEV: JsWord = JsWord::from("inlinedQrlDEV"); pub static ref _NOOP_QRL: JsWord = JsWord::from("_noopQrl"); + pub static ref _NOOP_QRL_DEV: JsWord = JsWord::from("_noopQrlDEV"); pub static ref _REST_PROPS: JsWord = JsWord::from("_restProps"); // TODO rename hooks to qrls pub static ref QHOOK: JsWord = JsWord::from("$"); diff --git a/packages/qwik/src/optimizer/src/api.md b/packages/qwik/src/optimizer/src/api.md index 27b1c7d7774..6f4305f3872 100644 --- a/packages/qwik/src/optimizer/src/api.md +++ b/packages/qwik/src/optimizer/src/api.md @@ -212,17 +212,13 @@ export interface QwikBundle { symbols?: string[]; } -// @public (undocumented) +// @public export interface QwikManifest { - // (undocumented) bundles: { [fileName: string]: QwikBundle; }; - // (undocumented) injections?: GlobalInjections[]; - // (undocumented) manifestHash: string; - // (undocumented) mapping: { [symbolName: string]: string; }; @@ -238,7 +234,6 @@ export interface QwikManifest { platform?: { [name: string]: string; }; - // (undocumented) symbols: { [symbolName: string]: QwikSymbol; }; @@ -382,7 +377,7 @@ export type SourceMapsOption = 'external' | 'inline' | undefined | null; export type SymbolMapper = Record; // @public (undocumented) -export type SymbolMapperFn = (symbolName: string, mapper: SymbolMapper | undefined) => readonly [symbol: string, chunk: string] | undefined; +export type SymbolMapperFn = (symbolName: string, mapper: SymbolMapper | undefined, parent?: string) => readonly [symbol: string, chunk: string] | undefined; // @public (undocumented) export type SystemEnvironment = 'node' | 'deno' | 'bun' | 'webworker' | 'browsermain' | 'unknown'; diff --git a/packages/qwik/src/optimizer/src/manifest.ts b/packages/qwik/src/optimizer/src/manifest.ts index e179ebc2581..6bb517e9732 100644 --- a/packages/qwik/src/optimizer/src/manifest.ts +++ b/packages/qwik/src/optimizer/src/manifest.ts @@ -1,13 +1,6 @@ -import type { NormalizedQwikPluginOptions } from './plugins/plugin'; -import type { - GeneratedOutputBundle, - GlobalInjections, - HookAnalysis, - Path, - QwikBundle, - QwikManifest, - QwikSymbol, -} from './types'; +import type { OutputBundle } from 'rollup'; +import { type NormalizedQwikPluginOptions } from './plugins/plugin'; +import type { GlobalInjections, HookAnalysis, Path, QwikBundle, QwikManifest } from './types'; // This is just the initial prioritization of the symbols and entries // at build time so there's less work during each SSR. However, SSR should @@ -178,8 +171,8 @@ function sortBundleNames(manifest: QwikManifest) { function updateSortAndPriorities(manifest: QwikManifest) { const prioritizedSymbolNames = prioritizeSymbolNames(manifest); - const prioritizedSymbols: { [symbolName: string]: QwikSymbol } = {}; - const prioritizedMapping: { [symbolName: string]: string } = {}; + const prioritizedSymbols: QwikManifest['symbols'] = {}; + const prioritizedMapping: QwikManifest['mapping'] = {}; for (const symbolName of prioritizedSymbolNames) { prioritizedSymbols[symbolName] = manifest.symbols[symbolName]; @@ -247,7 +240,7 @@ export function generateManifestFromBundles( path: Path, hooks: HookAnalysis[], injections: GlobalInjections[], - outputBundles: GeneratedOutputBundle[], + outputBundles: OutputBundle, opts: NormalizedQwikPluginOptions ) { const manifest: QwikManifest = { @@ -264,55 +257,29 @@ export function generateManifestFromBundles( }, }; - for (const hook of hooks) { - const buildFilePath = `${hook.canonicalFilename}.${hook.extension}`; - - const outputBundle = outputBundles.find((b) => { - return Object.keys(b.modules).find((f) => f.endsWith(buildFilePath)); - }); - - if (outputBundle) { - const symbolName = hook.name; - const bundleFileName = path.basename(outputBundle.fileName); - - manifest.mapping[symbolName] = bundleFileName; - - manifest.symbols[symbolName] = { - origin: hook.origin, - displayName: hook.displayName, - canonicalFilename: hook.canonicalFilename, - hash: hook.hash, - ctxKind: hook.ctxKind, - ctxName: hook.ctxName, - captures: hook.captures, - parent: hook.parent, - loc: hook.loc, - }; - - addBundleToManifest(path, manifest, outputBundle, bundleFileName); + // We need to find our QRL exports + const qrlNames = new Set([...hooks.map((h) => h.name)]); + for (const [fileName, outputBundle] of Object.entries(outputBundles)) { + if (outputBundle.type !== 'chunk') { + continue; } - } - - for (const outputBundle of outputBundles) { - const bundleFileName = path.basename(outputBundle.fileName); - addBundleToManifest(path, manifest, outputBundle, bundleFileName); - } - - return updateSortAndPriorities(manifest); -} + const bundleFileName = path.basename(fileName); -function addBundleToManifest( - path: Path, - manifest: QwikManifest, - outputBundle: GeneratedOutputBundle, - bundleFileName: string -) { - if (!manifest.bundles[bundleFileName]) { const buildDirName = path.dirname(outputBundle.fileName); const bundle: QwikBundle = { - size: outputBundle.size, + size: outputBundle.code.length, }; + for (const symbol of outputBundle.exports) { + if (qrlNames.has(symbol)) { + // When not minifying we see both the entry and the hook file + // The hook file will only have 1 export, we want the entry + if (!manifest.mapping[symbol] || outputBundle.exports.length !== 1) { + manifest.mapping[symbol] = bundleFileName; + } + } + } + const bundleImports = outputBundle.imports .filter((i) => path.dirname(i) === buildDirName) .map((i) => path.relative(buildDirName, i)); @@ -327,11 +294,36 @@ function addBundleToManifest( bundle.dynamicImports = bundleDynamicImports; } - const modulePaths = Object.keys(outputBundle.modules).filter((m) => !m.startsWith(`\u0000`)); + // Rollup doesn't provide the moduleIds in the outputBundle but Vite does + const ids = outputBundle.moduleIds || Object.keys(outputBundle.modules); + const modulePaths = ids.filter((m) => !m.startsWith(`\u0000`)); if (modulePaths.length > 0) { bundle.origins = modulePaths; } manifest.bundles[bundleFileName] = bundle; } + + for (const hook of hooks) { + const symbol = hook.name; + const bundle = manifest.mapping[symbol]; + if (!bundle) { + console.error(`Unable to find bundle for hook: ${hook.name}`, manifest); + throw new Error(`Unable to find bundle for hook: ${hook.hash}`); + } + (manifest.bundles[bundle].symbols ||= []).push(symbol); + manifest.symbols[symbol] = { + origin: hook.origin, + displayName: hook.displayName, + canonicalFilename: hook.canonicalFilename, + hash: hook.hash, + ctxKind: hook.ctxKind, + ctxName: hook.ctxName, + captures: hook.captures, + parent: hook.parent, + loc: hook.loc, + }; + } + + return updateSortAndPriorities(manifest); } diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index 1ce2ca86a7c..2ce329f0dbc 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -1,11 +1,10 @@ -import type { Rollup } from 'vite'; +import type { Rollup, Plugin } from 'vite'; import { hashCode } from '../../../core/util/hash_code'; import { generateManifestFromBundles, getValidManifest } from '../manifest'; import { createOptimizer } from '../optimizer'; import type { Diagnostic, EntryStrategy, - GeneratedOutputBundle, GlobalInjections, HookAnalysis, InsightManifest, @@ -19,6 +18,7 @@ import type { TransformOutput, } from '../types'; import { createLinter, type QwikLinter } from './eslint-plugin'; +import type { LoadResult, OutputBundle, TransformResult } from 'rollup'; const REG_CTX_NAME = ['server']; @@ -65,10 +65,10 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { const ssrResults = new Map(); const ssrTransformedOutputs = new Map(); + const foundQrls = new Map(); let internalOptimizer: Optimizer | null = null; let linter: QwikLinter | undefined = undefined; - const hookManifest: Record = {}; let diagnosticsCallback: ( d: Diagnostic[], optimizer: Optimizer, @@ -302,7 +302,6 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } else { opts.lint = updatedOpts.buildMode === 'development'; } - return { ...opts }; }; @@ -331,10 +330,9 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } }; - const buildStart = async (ctx: any) => { - debug(`buildStart()`, opts.buildMode, opts.scope); + const buildStart = async (ctx: Rollup.PluginContext) => { + debug(`buildStart()`, opts.buildMode, opts.scope, opts.target); const optimizer = getOptimizer(); - if (optimizer.sys.env === 'node' && opts.target === 'ssr' && opts.lint) { try { linter = await createLinter(optimizer.sys, opts.rootDir, opts.tsconfigFileNames); @@ -404,12 +402,11 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { debug(`buildStart() add transformedOutput`, key, output.hook?.displayName); transformedOutputs.set(key, [output, key]); ssrTransformedOutputs.set(key, [output, key]); - if (output.hook) { - hookManifest[output.hook.hash] = key; - } else if (output.isEntry) { + if (opts.target === 'client' && output.isEntry) { ctx.emitFile({ id: key, type: 'chunk', + preserveSignature: 'allow-extension', }); } } @@ -425,9 +422,9 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { ctx: Rollup.PluginContext, id: string, importer: string | undefined, - ssrOpts?: { ssr?: boolean } + resolveOpts?: Parameters>[2] ) => { - debug(`resolveId()`, 'Start', id, importer); + debug(`resolveId()`, 'Start', id, importer, resolveOpts, opts.target); if (id.startsWith('\0') || id.startsWith('/@fs')) { return; } @@ -456,12 +453,8 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { moduleSideEffects: false, }; } - let firstInput: string; - if (Array.isArray(opts.input)) { - firstInput = opts.input[0]; - } else { - firstInput = Object.values(opts.input)[0]; - } + + const firstInput = Object.values(opts.input)[0]; return { id: normalizePath(getPath().resolve(firstInput, QWIK_CLIENT_MANIFEST_ID)), moduleSideEffects: false, @@ -469,13 +462,32 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } const path = getPath(); - const isSSR = ssrOpts?.ssr ?? opts.target === 'ssr'; + const isSSR = !!resolveOpts?.ssr; if (importer) { // Only process ids that look like paths if (!(id.startsWith('.') || path.isAbsolute(id))) { return; } + if (opts.target === 'ssr') { + const match = /^([^?]*)\?_qrl_parent=(.*)/.exec(id); + if (match) { + // ssr mode asking for a client qrl, this will fall through to the devserver + // building here via ctx.load doesn't seem to work (target is always ssr?) + // eslint-disable-next-line prefer-const + let [, qrlId, parentId] = match; + // If the parent is not in root (e.g. pnpm symlink), the qrl also isn't + if (parentId.startsWith(opts.rootDir)) { + qrlId = `${opts.rootDir}${qrlId}`; + } + if (!transformedOutputs.has(qrlId)) { + // fall back to dev server which can wait for transform() to finish + return null; + } + debug(`resolveId()`, 'Resolved', qrlId); + return { id: qrlId }; + } + } const parsedId = parseId(id); let importeePathId = normalizePath(parsedId.pathId); const ext = path.extname(importeePathId).toLowerCase(); @@ -518,14 +530,19 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } } } + // We don't (yet) know this id + debug(`resolveId()`, 'Not resolved', id, importer, resolveOpts); return null; }; - - const load = async (_ctx: any, id: string, ssrOpts: { ssr?: boolean } = {}) => { + const load = async ( + ctx: Rollup.PluginContext, + id: string, + loadOpts?: Parameters>[1] + ): Promise => { if (id.startsWith('\0') || id.startsWith('/@fs/')) { return; } - const isSSR = ssrOpts?.ssr ?? opts.target === 'ssr'; + const isSSR = !!loadOpts?.ssr; if (opts.resolveQwikBuild && id.endsWith(QWIK_BUILD_ID)) { debug(`load()`, QWIK_BUILD_ID, opts.buildMode); return { @@ -549,15 +566,11 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { if (transformedModule) { debug(`load()`, 'Found', id); + let { code } = transformedModule[0]; + const { map, hook } = transformedModule[0]; - let code = transformedModule[0].code; - let firstInput: string; - if (Array.isArray(opts.input)) { - firstInput = opts.input[0]; - } else { - firstInput = Object.values(opts.input)[0]; - } if (opts.target === 'ssr') { + const firstInput = Object.values(opts.input)[0]; // doing this because vite will not use resolveId() when "noExternal" is false // so we need to turn the @qwik-client-manifest import into a relative import code = code.replace( @@ -565,15 +578,10 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { normalizePath(path.resolve(firstInput, QWIK_CLIENT_MANIFEST_ID)) ); } - return { - code, - map: transformedModule[0].map, - meta: { - hook: transformedModule[0].hook, - }, - }; + return { code, map, meta: { hook } }; } + debug('load()', 'Not found', id, parsedId); return null; }; @@ -581,12 +589,12 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { ctx: Rollup.PluginContext, code: string, id: string, - ssrOpts: { ssr?: boolean } = {} - ) { + transformOpts: Parameters>[2] = {} + ): Promise { if (id.startsWith('\0') || id.startsWith('/@fs/')) { return; } - const isSSR = ssrOpts.ssr ?? opts.target === 'ssr'; + const isSSR = !!transformOpts.ssr; const currentOutputs = isSSR ? ssrTransformedOutputs : transformedOutputs; if (currentOutputs.has(id)) { return; @@ -604,9 +612,17 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { TRANSFORM_REGEX.test(pathId) || insideRoots(ext, dir, opts.srcDir, opts.vendorRoots) ) { + /** Strip client|server code from server|client */ const strip = opts.target === 'client' || opts.target === 'ssr'; const normalizedID = normalizePath(pathId); - debug(`transform()`, 'Transforming', pathId); + debug(`transform()`, 'Transforming', { + pathId, + id, + parsedPathId, + strip, + isSSR, + target: opts.target, + }); let filePath = base; if (opts.srcDir) { @@ -616,20 +632,9 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { const srcDir = opts.srcDir ? opts.srcDir : normalizePath(dir); const mode = opts.target === 'lib' ? 'lib' : opts.buildMode === 'development' ? 'dev' : 'prod'; - // const entryStrategy: EntryStrategy = ['hoist', 'hook', 'inline'].includes(opts.entryStrategy.type) - // ? opts.entryStrategy - // : { - // type: 'hook', - // manual: hookManifest, - // }; const entryStrategy: EntryStrategy = opts.entryStrategy; const transformOpts: TransformModulesOptions = { - input: [ - { - code: code, - path: filePath, - }, - ], + input: [{ code, path: filePath }], entryStrategy, minify: 'simplify', // Always enable sourcemaps in dev for click-to-source @@ -638,17 +643,17 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { transpileJsx: true, explicitExtensions: true, preserveFilenames: true, - srcDir: srcDir, + srcDir, rootDir: opts.rootDir, - mode: mode, - scope: opts.scope ? opts.scope : void 0, + mode, + scope: opts.scope || undefined, }; + if (isSSR) { - transformOpts.isServer = isSSR; + transformOpts.isServer = true; transformOpts.entryStrategy = { type: 'hoist' }; } if (strip) { - transformOpts.isServer = isSSR; if (isSSR) { transformOpts.stripCtxName = CLIENT_STRIP_CTX_NAME; transformOpts.stripEventHandlers = true; @@ -660,7 +665,18 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } const newOutput = optimizer.transformModulesSync(transformOpts); + const module = newOutput.modules.find((mod) => !isAdditionalFile(mod))!; + if (opts.target === 'ssr') { + // we're in dev mode. All QRLs that might be emitted in SSR HTML are defined here. + // register them so that they can be resolved by the dev server + const matches = module.code.matchAll(/_([a-zA-Z0-9]{11,11})['"][,)]/g); + for (const [, symbol] of matches) { + foundQrls.set(symbol, id); + } + } + // uncomment to show transform results + // debug({ isSSR, strip }, transformOpts, newOutput); diagnosticsCallback(newOutput.diagnostics, optimizer, srcDir); if (isSSR) { @@ -673,47 +689,19 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } const deps = new Set(); for (const mod of newOutput.modules) { - if (isTransformedFile(mod)) { + if (mod !== module) { const key = normalizePath(path.join(srcDir, mod.path)); currentOutputs.set(key, [mod, id]); deps.add(key); - } - } - if (isSSR && strip) { - const clientTransformOpts: TransformModulesOptions = { - input: [ - { - code: code, - path: filePath, - }, - ], - entryStrategy: opts.entryStrategy, - minify: 'simplify', - sourceMaps: opts.sourcemap || 'development' === opts.buildMode, - transpileTs: true, - transpileJsx: true, - explicitExtensions: true, - preserveFilenames: true, - srcDir: srcDir, - rootDir: opts.rootDir, - mode: mode, - scope: opts.scope ? opts.scope : void 0, - }; - clientTransformOpts.stripCtxName = SERVER_STRIP_CTX_NAME; - clientTransformOpts.stripExports = SERVER_STRIP_EXPORTS; - clientTransformOpts.isServer = false; - const clientNewOutput = optimizer.transformModulesSync(clientTransformOpts); - - diagnosticsCallback(clientNewOutput.diagnostics, optimizer, srcDir); - - results.set(normalizedID, clientNewOutput); - for (const mod of clientNewOutput.modules) { - if (isTransformedFile(mod)) { - const key = normalizePath(path.join(srcDir, mod.path)); - ctx.addWatchFile(key); - transformedOutputs.set(key, [mod, id]); - deps.add(key); + // rollup must be told about entry points + if (opts.target === 'client' && mod.isEntry) { + ctx.emitFile({ + id: key, + type: 'chunk', + preserveSignature: 'allow-extension', + }); } + ctx.addWatchFile(key); } } @@ -725,7 +713,6 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { await ctx.load({ id }); } - const module = newOutput.modules.find((mod) => !isTransformedFile(mod))!; return { code: module.code, map: module.map, @@ -736,16 +723,14 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { }; } - debug(`transform()`, 'No Transforming', id); + debug(`transform()`, 'Not transforming', id); return null; }; - const createOutputAnalyzer = () => { - const outputBundles: GeneratedOutputBundle[] = []; + const createOutputAnalyzer = (rollupBundle: OutputBundle) => { const injections: GlobalInjections[] = []; - const addBundle = (b: GeneratedOutputBundle) => outputBundles.push(b); const addInjection = (b: GlobalInjections) => injections.push(b); const generateManifest = async () => { const optimizer = getOptimizer(); @@ -756,7 +741,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { .map((mod) => mod.hook) .filter((h) => !!h) as HookAnalysis[]; - const manifest = generateManifestFromBundles(path, hooks, injections, outputBundles, opts); + const manifest = generateManifestFromBundles(path, hooks, injections, rollupBundle, opts); for (const symbol of Object.values(manifest.symbols)) { if (symbol.origin) { @@ -780,7 +765,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { return manifest; }; - return { addBundle, addInjection, generateManifest }; + return { addInjection, generateManifest }; }; const getOptions = () => opts; @@ -869,6 +854,7 @@ export const manifest = ${JSON.stringify(manifest)};\n`; transform, validateSource, setSourceMapSupport, + foundQrls, }; } @@ -887,7 +873,7 @@ const insideRoots = (ext: string, dir: string, srcDir: string | null, vendorRoot return false; }; -function isTransformedFile(mod: TransformModule) { +function isAdditionalFile(mod: TransformModule) { return mod.isEntry || mod.hook; } diff --git a/packages/qwik/src/optimizer/src/plugins/rollup.ts b/packages/qwik/src/optimizer/src/plugins/rollup.ts index acfc40fd685..e5d14621c77 100644 --- a/packages/qwik/src/optimizer/src/plugins/rollup.ts +++ b/packages/qwik/src/optimizer/src/plugins/rollup.ts @@ -117,22 +117,8 @@ export function qwikRollup(qwikRollupOpts: QwikRollupPluginOptions = {}): any { if (opts.target === 'client') { // client build - const outputAnalyzer = qwikPlugin.createOutputAnalyzer(); - - for (const fileName in rollupBundle) { - const b = rollupBundle[fileName]; - if (b.type === 'chunk') { - outputAnalyzer.addBundle({ - fileName, - modules: b.modules, - imports: b.imports, - dynamicImports: b.dynamicImports, - size: b.code.length, - }); - } - } - const optimizer = qwikPlugin.getOptimizer(); + const outputAnalyzer = qwikPlugin.createOutputAnalyzer(rollupBundle); const manifest = await outputAnalyzer.generateManifest(); manifest.platform = { ...versions, diff --git a/packages/qwik/src/optimizer/src/plugins/vite-server.ts b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts similarity index 86% rename from packages/qwik/src/optimizer/src/plugins/vite-server.ts rename to packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts index 6dee7ad1def..ef7ed97b38a 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite-server.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts @@ -4,7 +4,7 @@ import { magenta } from 'kleur/colors'; import type { IncomingMessage, ServerResponse } from 'http'; import type { Connect, ViteDevServer } from 'vite'; -import type { OptimizerSystem, Path, QwikManifest, SymbolMapper } from '../types'; +import type { OptimizerSystem, Path, QwikManifest } from '../types'; import { type NormalizedQwikPluginOptions, parseId } from './plugin'; import type { QwikViteDevResponse } from './vite'; import { formatError } from './vite-utils'; @@ -28,12 +28,14 @@ function getOrigin(req: IncomingMessage) { } export async function configureDevServer( + base: string, server: ViteDevServer, opts: NormalizedQwikPluginOptions, sys: OptimizerSystem, path: Path, isClientDevOnly: boolean, - clientDevInput: string | undefined + clientDevInput: string | undefined, + foundQrls: Map ) { if (typeof fetch !== 'function' && sys.env === 'node') { // polyfill fetch() when not available in Node.js @@ -54,7 +56,7 @@ export async function configureDevServer( } // qwik middleware injected BEFORE vite internal middlewares - server.middlewares.use(async (req: any, res: any, next: any) => { + server.middlewares.use(async (req, res, next) => { try { const { ORIGIN } = process.env; const domain = ORIGIN ?? getOrigin(req); @@ -84,12 +86,7 @@ export async function configureDevServer( return; } - let firstInput: string; - if (Array.isArray(opts.input)) { - firstInput = opts.input[0]; - } else { - firstInput = Object.values(opts.input)[0]; - } + const firstInput = Object.values(opts.input)[0]; const ssrModule = await server.ssrLoadModule(firstInput); const render: Render = ssrModule.default ?? ssrModule.render; @@ -136,10 +133,6 @@ export async function configureDevServer( }); }); - const srcBase = opts.srcDir - ? path.relative(opts.rootDir, opts.srcDir).replace(/\\/g, '/') - : 'src'; - const renderOpts: RenderToStreamOptions = { debug: true, locale: serverData.locale, @@ -148,26 +141,33 @@ export async function configureDevServer( manifest: isClientDevOnly ? undefined : manifest, symbolMapper: isClientDevOnly ? undefined - : (symbolName: string, mapper: SymbolMapper | undefined) => { + : (symbolName, mapper, parent) => { if (symbolName === SYNC_QRL) { return [symbolName, '']; } - const defaultChunk = [ - symbolName, - `/${srcBase}/${symbolName.toLowerCase()}.js`, - ] as const; - if (mapper) { - const hash = getSymbolHash(symbolName); - return mapper[hash] ?? defaultChunk; - } else { - return defaultChunk; + const chunk = mapper && mapper[getSymbolHash(symbolName)]; + if (chunk) { + return chunk; } + parent ||= foundQrls.get(symbolName); + if (!parent) { + console.error( + 'qwik vite-dev-server: unknown qrl requested without parent:', + symbolName + ); + return [symbolName, `${base}${symbolName.toLowerCase()}.js`]; + } + const parentPath = path.dirname(parent); + // support getting files through pnpm link symlinks + const qrlPath = parentPath.startsWith(opts.rootDir) + ? path.relative(opts.rootDir, parentPath) + : parentPath; + const qrlFile = `${qrlPath}/${symbolName.toLowerCase()}.js?_qrl_parent=${parent}`; + return [symbolName, `${base}${qrlFile}`]; }, prefetchStrategy: null, serverData, - containerAttributes: { - ...serverData.containerAttributes, - }, + containerAttributes: { ...serverData.containerAttributes }, }; res.setHeader('Content-Type', 'text/html; charset=utf-8'); @@ -206,6 +206,18 @@ export async function configureDevServer( next(); } } else { + // We didn't ssr, but maybe a qrl was requested + const parent = url.searchParams.get('_qrl_parent'); + if (parent && url.pathname.endsWith('.js')) { + // load the parent so it populates the qrl cache + await server.transformRequest(parent); + const result = await server.transformRequest(url.pathname); + if (result) { + res.setHeader('content-type', 'application/javascript'); + res.write(result.code); + res.end(); + } + } next(); } } catch (e: any) { diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts index 0ab90e473f5..676bffc6b5f 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.ts @@ -33,7 +33,7 @@ import { type QwikPluginOptions, } from './plugin'; import { createRollupError, normalizeRollupOutputOptions } from './rollup'; -import { VITE_DEV_CLIENT_QS, configureDevServer, configurePreviewServer } from './vite-server'; +import { VITE_DEV_CLIENT_QS, configureDevServer, configurePreviewServer } from './vite-dev-server'; const DEDUPE = [QWIK_CORE_ID, QWIK_JSX_RUNTIME_ID, QWIK_JSX_DEV_RUNTIME_ID]; @@ -490,18 +490,10 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { if (opts.target === 'client') { // client build - const outputAnalyzer = qwikPlugin.createOutputAnalyzer(); + const outputAnalyzer = qwikPlugin.createOutputAnalyzer(rollupBundle); for (const [fileName, b] of Object.entries(rollupBundle)) { - if (b.type === 'chunk') { - outputAnalyzer.addBundle({ - fileName, - modules: b.modules, - imports: b.imports, - dynamicImports: b.dynamicImports, - size: b.code.length, - }); - } else { + if (b.type === 'asset') { const baseFilename = basePathname + fileName; if (STYLING.some((ext) => fileName.endsWith(ext))) { if (typeof b.source === 'string' && b.source.length < opts.inlineStylesUpToBytes) { @@ -666,7 +658,16 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { const opts = qwikPlugin.getOptions(); const sys = qwikPlugin.getSys(); const path = qwikPlugin.getPath(); - await configureDevServer(server, opts, sys, path, isClientDevOnly, clientDevInput); + await configureDevServer( + basePathname, + server, + opts, + sys, + path, + isClientDevOnly, + clientDevInput, + qwikPlugin.foundQrls + ); }; const isNEW = (globalThis as any).__qwikCityNew === true; if (isNEW) { diff --git a/packages/qwik/src/optimizer/src/types.ts b/packages/qwik/src/optimizer/src/types.ts index 527fc88e347..80874ab2901 100644 --- a/packages/qwik/src/optimizer/src/types.ts +++ b/packages/qwik/src/optimizer/src/types.ts @@ -201,12 +201,21 @@ export interface SmartEntryStrategy { manual?: Record; } -/** @public */ +/** + * The metadata of the build. One of its uses is storing where QRL symbols are located. + * + * @public + */ export interface QwikManifest { + /** Content hash of the manifest, if this changes, the code changed */ manifestHash: string; + /** QRL symbols */ symbols: { [symbolName: string]: QwikSymbol }; + /** Where QRLs are located */ mapping: { [symbolName: string]: string }; + /** All code bundles, used to know the import graph */ bundles: { [fileName: string]: QwikBundle }; + /** CSS etc to inject in the document head */ injections?: GlobalInjections[]; version: string; options?: { @@ -233,7 +242,8 @@ export type SymbolMapper = Record readonly [symbol: string, chunk: string] | undefined; /** @public */ @@ -265,16 +275,6 @@ export interface GlobalInjections { location: 'head' | 'body'; } -export interface GeneratedOutputBundle { - fileName: string; - modules: { - [id: string]: any; - }; - imports: string[]; - dynamicImports: string[]; - size: number; -} - // PATH UTIL *************** /** @public */ diff --git a/packages/qwik/src/server/platform.ts b/packages/qwik/src/server/platform.ts index 87d97ddfd82..a275d3924d4 100644 --- a/packages/qwik/src/server/platform.ts +++ b/packages/qwik/src/server/platform.ts @@ -15,7 +15,7 @@ export function createPlatform( const mapper = resolvedManifest?.mapper; const mapperFn = opts.symbolMapper ? opts.symbolMapper - : (symbolName: string): readonly [string, string] | undefined => { + : (symbolName: string, _chunk: any, parent?: string): readonly [string, string] | undefined => { if (mapper) { const hash = getSymbolHash(symbolName); const result = mapper[hash]; @@ -27,7 +27,11 @@ export function createPlatform( if (isRegistered) { return [symbolName, '_'] as const; } - console.error('Cannot resolve symbol', symbolName, 'in', mapper); + if (parent) { + // In dev mode, SSR may need to refer to a symbol that wasn't built yet on the client + return [symbolName, `${parent}?qrl=${symbolName}`] as const; + } + console.error('Cannot resolve symbol', symbolName, 'in', mapper, parent); } return result; } @@ -65,8 +69,8 @@ export function createPlatform( }); }); }, - chunkForSymbol(symbolName: string) { - return mapperFn(symbolName, mapper); + chunkForSymbol(symbolName: string, _chunk, parent) { + return mapperFn(symbolName, mapper, parent); }, }; return serverPlatform; diff --git a/packages/qwik/src/testing/platform.ts b/packages/qwik/src/testing/platform.ts index c87325eb6f0..56e3c19c75f 100644 --- a/packages/qwik/src/testing/platform.ts +++ b/packages/qwik/src/testing/platform.ts @@ -26,10 +26,16 @@ function createPlatform() { const importPath = toPath(urlDoc); const mod = moduleCache.get(importPath); if (mod) { + if (!mod || !(symbolName in mod)) { + throw new Error(`Q-ERROR: missing symbol '${symbolName}' in module '${url}'.`); + } return mod[symbolName]; } return import(importPath).then((mod) => { moduleCache.set(importPath, mod); + if (!mod || !(symbolName in mod)) { + throw new Error(`Q-ERROR: missing symbol '${symbolName}' in module '${url}'.`); + } return mod[symbolName]; }); }, From e10f74de508f53b8808b008a219f054d83c61377 Mon Sep 17 00:00:00 2001 From: Ahmed Zougari Date: Wed, 19 Jun 2024 00:17:56 +0100 Subject: [PATCH 186/412] fix(docs): update valibot snippet (#6569) --- .../demo/integration/modular-forms/index.tsx | 24 +++++---- .../docs/integrations/modular-forms/index.mdx | 52 ++++++++++--------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/docs/src/routes/demo/integration/modular-forms/index.tsx b/packages/docs/src/routes/demo/integration/modular-forms/index.tsx index aeb6dbfbb8d..96d6b110c25 100644 --- a/packages/docs/src/routes/demo/integration/modular-forms/index.tsx +++ b/packages/docs/src/routes/demo/integration/modular-forms/index.tsx @@ -4,20 +4,22 @@ import { $, component$, type QRL } from '@builder.io/qwik'; import { routeLoader$ } from '@builder.io/qwik-city'; import type { InitialValues, SubmitHandler } from '@modular-forms/qwik'; import { formAction$, useForm, valiForm$ } from '@modular-forms/qwik'; -import { email, type Input, minLength, object, string } from 'valibot'; +import * as v from 'valibot'; -const LoginSchema = object({ - email: string([ - minLength(1, 'Please enter your email.'), - email('The email address is badly formatted.'), - ]), - password: string([ - minLength(1, 'Please enter your password.'), - minLength(8, 'Your password must have 8 characters or more.'), - ]), +const LoginSchema = v.object({ + email: v.pipe( + v.string(), + v.nonEmpty('Please enter your email.'), + v.email('The email address is badly formatted.') + ), + password: v.pipe( + v.string(), + v.nonEmpty('Please enter your password.'), + v.minLength(8, 'Your password must have 8 characters or more.') + ), }); -type LoginForm = Input; +type LoginForm = v.InferInput; export const useFormLoader = routeLoader$>(() => ({ email: '', diff --git a/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx b/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx index 3bf707a7320..ac029d1b2fc 100644 --- a/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx +++ b/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx @@ -38,20 +38,22 @@ type LoginForm = { Since Modular Forms supports [Valibot](https://valibot.dev/) and [Zod](https://zod.dev/) for input validation, you can optionally derive the type definition from a schema. ```ts -import { email, type Input, minLength, object, string } from 'valibot'; - -const LoginSchema = object({ - email: string([ - minLength(1, 'Please enter your email.'), - email('The email address is badly formatted.'), - ]), - password: string([ - minLength(1, 'Please enter your password.'), - minLength(8, 'Your password must have 8 characters or more.'), - ]), +import * as v from 'valibot'; + +const LoginSchema = v.object({ + email: v.pipe( + v.string(), + v.nonEmpty('Please enter your email.'), + v.email('he email address is badly formatted.'), + ), + password: v.pipe( + v.string(), + v.nonEmpty('Please enter your password.'), + v.minLength(8, 'Your password must have 8 characters or more.'), + ), }); -type LoginForm = Input; +type LoginForm = v.InferInput; ``` If you're wondering why this guide favors Valibot over Zod, I recommend reading this [announcement post](https://www.builder.io/blog/introducing-valibot). @@ -183,20 +185,22 @@ import { $, component$, type QRL } from '@builder.io/qwik'; import { routeLoader$ } from '@builder.io/qwik-city'; import type { InitialValues, SubmitHandler } from '@modular-forms/qwik'; import { formAction$, useForm, valiForm$ } from '@modular-forms/qwik'; -import { email, type Input, minLength, object, string } from 'valibot'; - -const LoginSchema = object({ - email: string([ - minLength(1, 'Please enter your email.'), - email('The email address is badly formatted.'), - ]), - password: string([ - minLength(1, 'Please enter your password.'), - minLength(8, 'Your password must have 8 characters or more.'), - ]), +import * as v from 'valibot'; + +const LoginSchema = v.object({ + email: v.pipe( + v.string(), + v.nonEmpty('Please enter your email.'), + v.email('The email address is badly formatted.'), + ), + password: v.pipe( + v.string(), + v.nonEmpty('Please enter your password.'), + v.minLength(8, 'Your password must have 8 characters or more.'), + ), }); -type LoginForm = Input; +type LoginForm = v.InferInput; export const useFormLoader = routeLoader$>(() => ({ email: '', From 2284b1a266c78ecae1a877902c9bc10c7a560a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Tue, 18 Jun 2024 21:14:20 -0400 Subject: [PATCH 187/412] feat(core): Add support to shadow DOM for QwikLoader. (#6547) Shadow DOM can be used to isolate an application. The isolation also includes: - Events don't bubble across shadow boundaries. - QuerySelector doesn't cross shadow boundaries. QwikLoader relies on both event bubbling and querySelector to process events such as `on:click` and dispatch such as `on-window:resize`. To enable QwikLoader to work with shadow DOM, it is necessary to tell qwikloader that when it is registering an event listener, as well as performing querySelector, it should do so not just on document but also inside the shadow DOM. To do that it is the responsibility of the developer to tell qwikloader about the shadow DOM roots. CONSTRAINTS: The qwikloader applications `q:container` must be fully inside the shadow root. (i.e. the application itself must not cross the shadow DOM boundaries.) Developer responsibility: (choose one) - Have qwik loader find the roots by annotate the elements which have shadow DOM with `q:shadowRoot` attribute. - Manually insert a `">' + '' ); }); From a2edce0f1d4b4f3bc275b3b8876ed94cf272aac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sebastian=C2=AE?= Date: Mon, 24 Jun 2024 17:42:40 -0700 Subject: [PATCH 206/412] docs: Document how to access current url from within root.tsx (#6598) Document how to access current url from within root.tsx Document how to access current url from within root.tsx Thanks to @shairez. --- packages/docs/src/routes/docs/(qwikcity)/api/index.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/docs/src/routes/docs/(qwikcity)/api/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/api/index.mdx index 90d4a639316..5dd5af43c62 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/api/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/api/index.mdx @@ -22,6 +22,7 @@ contributors: - billykwok - julianobrasil - hamatoyogi + - srapport updated_at: '2023-10-03T18:53:23Z' created_at: '2023-03-20T23:45:13Z' --- @@ -172,6 +173,14 @@ The above code would generate:

skuId: 1234

``` +### `useLocation` in `root.tsx` is not supported + +To access the current url from `root.tsx` use this pattern: +```ts +const serverDataUrl = useServerData('url'); +const url = new URL(serverDataUrl || 'https://unknown'); +``` + > Notice that `useLocation` is a read-only API, you should never attempt to mutate the values of the `loc` object returned. Instead look into the `useNavigate()` API. ## `useNavigate()` From 35aba35e0a37c4c0399470d9beb457bb57ffe4d7 Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:14:37 +0200 Subject: [PATCH 207/412] feat: manage assetsDir (#6588) --- .../src/routes/api/qwik-optimizer/api.json | 2 +- .../src/routes/api/qwik-optimizer/index.md | 13 ++++ .../adapters/cloudflare-pages/vite/index.ts | 7 +- .../qwik-city/adapters/shared/vite/index.ts | 5 +- .../adapters/shared/vite/post-build.ts | 13 ++-- packages/qwik-city/buildtime/types.ts | 4 +- packages/qwik-city/buildtime/vite/plugin.ts | 3 +- packages/qwik/src/optimizer/src/api.md | 2 + .../qwik/src/optimizer/src/plugins/plugin.ts | 6 ++ .../qwik/src/optimizer/src/plugins/rollup.ts | 32 ++++---- .../qwik/src/optimizer/src/plugins/vite.ts | 15 +++- .../src/optimizer/src/plugins/vite.unit.ts | 73 ++++++++++++++++++- 12 files changed, 141 insertions(+), 34 deletions(-) diff --git a/packages/docs/src/routes/api/qwik-optimizer/api.json b/packages/docs/src/routes/api/qwik-optimizer/api.json index 1002986fa93..2093eea9af6 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/api.json +++ b/packages/docs/src/routes/api/qwik-optimizer/api.json @@ -498,7 +498,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface QwikVitePluginApi \n```\n\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[getClientOutDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
\n\n[getClientPublicOutDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
\n\n[getInsightsManifest](#)\n\n\n\n\n\n\n\n(clientOutDir?: string \\| null) => Promise<[InsightManifest](#insightmanifest) \\| null>\n\n\n\n\n\n
\n\n[getManifest](#)\n\n\n\n\n\n\n\n() => [QwikManifest](#qwikmanifest) \\| null\n\n\n\n\n\n
\n\n[getOptimizer](#)\n\n\n\n\n\n\n\n() => [Optimizer](#optimizer) \\| null\n\n\n\n\n\n
\n\n[getOptions](#)\n\n\n\n\n\n\n\n() => NormalizedQwikPluginOptions\n\n\n\n\n\n
\n\n[getRootDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
", + "content": "```typescript\nexport interface QwikVitePluginApi \n```\n\n\n\n\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[getAssetsDir](#)\n\n\n\n\n\n\n\n() => string \\| undefined\n\n\n\n\n\n
\n\n[getClientOutDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
\n\n[getClientPublicOutDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
\n\n[getInsightsManifest](#)\n\n\n\n\n\n\n\n(clientOutDir?: string \\| null) => Promise<[InsightManifest](#insightmanifest) \\| null>\n\n\n\n\n\n
\n\n[getManifest](#)\n\n\n\n\n\n\n\n() => [QwikManifest](#qwikmanifest) \\| null\n\n\n\n\n\n
\n\n[getOptimizer](#)\n\n\n\n\n\n\n\n() => [Optimizer](#optimizer) \\| null\n\n\n\n\n\n
\n\n[getOptions](#)\n\n\n\n\n\n\n\n() => NormalizedQwikPluginOptions\n\n\n\n\n\n
\n\n[getRootDir](#)\n\n\n\n\n\n\n\n() => string \\| null\n\n\n\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/plugins/vite.ts", "mdFile": "qwik.qwikvitepluginapi.md" }, diff --git a/packages/docs/src/routes/api/qwik-optimizer/index.md b/packages/docs/src/routes/api/qwik-optimizer/index.md index 3f64e1d494a..9f799a65b64 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/index.md +++ b/packages/docs/src/routes/api/qwik-optimizer/index.md @@ -2291,6 +2291,19 @@ Description +[getAssetsDir](#) + + + + + +() => string \| undefined + + + + + + [getClientOutDir](#) diff --git a/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts b/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts index cec2d1a0a83..da287350835 100644 --- a/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts +++ b/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts @@ -37,14 +37,15 @@ export function cloudflarePagesAdapter(opts: CloudflarePagesAdapterOptions = {}) }; }, - async generate({ clientOutDir, serverOutDir, basePathname }) { + async generate({ clientOutDir, serverOutDir, basePathname, assetsDir }) { const routesJsonPath = join(clientOutDir, '_routes.json'); const hasRoutesJson = fs.existsSync(routesJsonPath); if (!hasRoutesJson && opts.functionRoutes !== false) { + const pathName = assetsDir ? join(basePathname, assetsDir) : basePathname; const routesJson = { version: 1, - include: [basePathname + '*'], - exclude: [basePathname + 'build/*', basePathname + 'assets/*'], + include: [pathName + '*'], + exclude: [pathName + 'build/*', pathName + 'assets/*'], }; await fs.promises.writeFile(routesJsonPath, JSON.stringify(routesJson, undefined, 2)); } diff --git a/packages/qwik-city/adapters/shared/vite/index.ts b/packages/qwik-city/adapters/shared/vite/index.ts index bc5ce38e996..cda6150b5ef 100644 --- a/packages/qwik-city/adapters/shared/vite/index.ts +++ b/packages/qwik-city/adapters/shared/vite/index.ts @@ -120,6 +120,7 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { const basePathname = qwikCityPlugin.api.getBasePathname(); const clientOutDir = qwikVitePlugin.api.getClientOutDir()!; const clientPublicOutDir = qwikVitePlugin.api.getClientPublicOutDir()!; + const assetsDir = qwikVitePlugin.api.getAssetsDir(); const rootDir = qwikVitePlugin.api.getRootDir() ?? undefined; if (renderModulePath && qwikCityPlanModulePath && clientOutDir && clientPublicOutDir) { @@ -168,7 +169,7 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { const { staticPathsCode, notFoundPathsCode } = await postBuild( clientPublicOutDir, - basePathname, + assetsDir ? join(basePathname, assetsDir) : basePathname, staticPaths, format, !!opts.cleanStaticGenerated @@ -189,6 +190,7 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { clientPublicOutDir, basePathname, routes, + assetsDir, warn: (message) => this.warn(message), error: (message) => this.error(message), }); @@ -234,6 +236,7 @@ interface ViteAdapterPluginOptions { serverOutDir: string; basePathname: string; routes: BuildRoute[]; + assetsDir?: string; warn: (message: string) => void; error: (message: string) => void; }) => Promise; diff --git a/packages/qwik-city/adapters/shared/vite/post-build.ts b/packages/qwik-city/adapters/shared/vite/post-build.ts index a7abef9c89f..8ce44d6d58a 100644 --- a/packages/qwik-city/adapters/shared/vite/post-build.ts +++ b/packages/qwik-city/adapters/shared/vite/post-build.ts @@ -4,12 +4,15 @@ import { getErrorHtml } from '@builder.io/qwik-city/middleware/request-handler'; export async function postBuild( clientOutDir: string, - basePathname: string, + pathName: string, userStaticPaths: string[], format: string, cleanStatic: boolean ) { - const ignorePathnames = new Set([basePathname + 'build/', basePathname + 'assets/']); + if (pathName && !pathName.endsWith('/')) { + pathName += '/'; + } + const ignorePathnames = new Set([pathName + 'build/', pathName + 'assets/']); const staticPaths = new Set(userStaticPaths.map(normalizeTrailingSlash)); const notFounds: string[][] = []; @@ -51,11 +54,11 @@ export async function postBuild( }; if (fs.existsSync(clientOutDir)) { - await loadDir(clientOutDir, basePathname); + await loadDir(clientOutDir, pathName); } - const notFoundPathsCode = createNotFoundPathsModule(basePathname, notFounds, format); - const staticPathsCode = createStaticPathsModule(basePathname, staticPaths, format); + const notFoundPathsCode = createNotFoundPathsModule(pathName, notFounds, format); + const staticPathsCode = createStaticPathsModule(pathName, staticPaths, format); return { notFoundPathsCode, diff --git a/packages/qwik-city/buildtime/types.ts b/packages/qwik-city/buildtime/types.ts index 66c621d1417..79958f86cc7 100644 --- a/packages/qwik-city/buildtime/types.ts +++ b/packages/qwik-city/buildtime/types.ts @@ -141,7 +141,9 @@ export interface MdxPlugins { rehypeAutolinkHeadings: boolean; } -export interface NormalizedPluginOptions extends Required {} +export interface NormalizedPluginOptions extends Required { + assetsDir?: string; +} export interface MarkdownAttributes { [name: string]: string; diff --git a/packages/qwik-city/buildtime/vite/plugin.ts b/packages/qwik-city/buildtime/vite/plugin.ts index 2dd8a9a5352..533a7f27215 100644 --- a/packages/qwik-city/buildtime/vite/plugin.ts +++ b/packages/qwik-city/buildtime/vite/plugin.ts @@ -292,9 +292,10 @@ function qwikCityPlugin(userOpts?: QwikCityVitePluginOptions): any { } if (outDir && clientOutDir) { + const assetsDir = qwikPlugin!.api.getAssetsDir(); const { staticPathsCode, notFoundPathsCode } = await postBuild( clientOutDir, - api.getBasePathname(), + assetsDir ? join(api.getBasePathname(), assetsDir) : api.getBasePathname(), [], ssrFormat, false diff --git a/packages/qwik/src/optimizer/src/api.md b/packages/qwik/src/optimizer/src/api.md index 6f4305f3872..d7d317baf2c 100644 --- a/packages/qwik/src/optimizer/src/api.md +++ b/packages/qwik/src/optimizer/src/api.md @@ -306,6 +306,8 @@ export interface QwikVitePlugin { // @public (undocumented) export interface QwikVitePluginApi { + // (undocumented) + getAssetsDir: () => string | undefined; // (undocumented) getClientOutDir: () => string | null; // (undocumented) diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index cc0d119d85a..71ea032a608 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -84,6 +84,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { tsconfigFileNames: ['./tsconfig.json'], input: null as any, outDir: null as any, + assetsDir: null as any, resolveQwikBuild: true, entryStrategy: null as any, srcDir: null as any, @@ -140,6 +141,10 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { opts.debug = !!updatedOpts.debug; + if (updatedOpts.assetsDir) { + opts.assetsDir = updatedOpts.assetsDir; + } + updatedOpts.target === 'test'; if ( updatedOpts.target === 'ssr' || @@ -981,6 +986,7 @@ export interface QwikPluginOptions { insightsManifest?: InsightManifest | null; input?: string[] | string | { [entry: string]: string }; outDir?: string; + assetsDir?: string; srcDir?: string | null; scope?: string | null; srcInputs?: TransformModuleInput[] | null; diff --git a/packages/qwik/src/optimizer/src/plugins/rollup.ts b/packages/qwik/src/optimizer/src/plugins/rollup.ts index e5d14621c77..8658f08cf1f 100644 --- a/packages/qwik/src/optimizer/src/plugins/rollup.ts +++ b/packages/qwik/src/optimizer/src/plugins/rollup.ts @@ -6,7 +6,6 @@ import type { OptimizerOptions, QwikManifest, TransformModuleInput, - Path, TransformModule, } from '../types'; import { @@ -69,11 +68,7 @@ export function qwikRollup(qwikRollupOpts: QwikRollupPluginOptions = {}): any { }, outputOptions(rollupOutputOpts) { - return normalizeRollupOutputOptionsObject( - qwikPlugin.getPath(), - qwikPlugin.getOptions(), - rollupOutputOpts - ); + return normalizeRollupOutputOptionsObject(qwikPlugin.getOptions(), rollupOutputOpts, false); }, async buildStart() { @@ -151,9 +146,9 @@ export function qwikRollup(qwikRollupOpts: QwikRollupPluginOptions = {}): any { } export function normalizeRollupOutputOptions( - path: Path, opts: NormalizedQwikPluginOptions, - rollupOutputOpts: Rollup.OutputOptions | Rollup.OutputOptions[] | undefined + rollupOutputOpts: Rollup.OutputOptions | Rollup.OutputOptions[] | undefined, + useAssetsDir: boolean ): Rollup.OutputOptions[] { const outputOpts: Rollup.OutputOptions[] = Array.isArray(rollupOutputOpts) ? // fill the `outputOpts` array with all existing option entries @@ -167,14 +162,14 @@ export function normalizeRollupOutputOptions( } return outputOpts.map((outputOptsObj) => - normalizeRollupOutputOptionsObject(path, opts, outputOptsObj) + normalizeRollupOutputOptionsObject(opts, outputOptsObj, useAssetsDir) ); } export function normalizeRollupOutputOptionsObject( - path: Path, opts: NormalizedQwikPluginOptions, - rollupOutputOptsObj: Rollup.OutputOptions | undefined + rollupOutputOptsObj: Rollup.OutputOptions | undefined, + useAssetsDir: boolean ): Rollup.OutputOptions { const outputOpts: Rollup.OutputOptions = { ...rollupOutputOptsObj }; @@ -183,22 +178,29 @@ export function normalizeRollupOutputOptionsObject( } if (opts.target === 'client') { // client output + outputOpts.assetFileNames = useAssetsDir + ? `${opts.assetsDir}/${outputOpts.assetFileNames}` + : outputOpts.assetFileNames; if (opts.buildMode === 'production') { // client production output if (!outputOpts.entryFileNames) { - outputOpts.entryFileNames = 'build/q-[hash].js'; + const fileName = 'build/q-[hash].js'; + outputOpts.entryFileNames = useAssetsDir ? `${opts.assetsDir}/${fileName}` : fileName; } if (!outputOpts.chunkFileNames) { - outputOpts.chunkFileNames = 'build/q-[hash].js'; + const fileName = 'build/q-[hash].js'; + outputOpts.chunkFileNames = useAssetsDir ? `${opts.assetsDir}/${fileName}` : fileName; } } else { // client development output if (!outputOpts.entryFileNames) { - outputOpts.entryFileNames = 'build/[name].js'; + const fileName = 'build/[name].js'; + outputOpts.entryFileNames = useAssetsDir ? `${opts.assetsDir}/${fileName}` : fileName; } if (!outputOpts.chunkFileNames) { - outputOpts.chunkFileNames = 'build/[name].js'; + const fileName = 'build/[name].js'; + outputOpts.chunkFileNames = useAssetsDir ? `${opts.assetsDir}/${fileName}` : fileName; } } } else if (opts.buildMode === 'production') { diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts index f0f8e346a00..68113609514 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.ts @@ -50,6 +50,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { let clientOutDir: string | null = null; let basePathname: string = '/'; let clientPublicOutDir: string | null = null; + let viteAssetsDir: string | undefined; let srcDir: string | null = null; let rootDir: string | null = null; @@ -83,6 +84,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { getRootDir: () => qwikPlugin.getOptions().rootDir, getClientOutDir: () => clientOutDir, getClientPublicOutDir: () => clientPublicOutDir, + getAssetsDir: () => viteAssetsDir, }; // We provide two plugins to Vite. The first plugin is the main plugin that handles all the @@ -144,6 +146,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { const vendorRoots = shouldFindVendors ? await findQwikRoots(sys, path.join(sys.cwd(), 'package.json')) : []; + viteAssetsDir = viteConfig.build?.assetsDir; + const useAssetsDir = target === 'client' && !!viteAssetsDir && viteAssetsDir !== '_astro'; const pluginOpts: QwikPluginOptions = { target, buildMode, @@ -157,6 +161,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { transformedModuleOutput: qwikViteOpts.transformedModuleOutput, vendorRoots: [...(qwikViteOpts.vendorRoots ?? []), ...vendorRoots.map((v) => v.path)], outDir: viteConfig.build?.outDir, + assetsDir: useAssetsDir ? viteAssetsDir : undefined, devTools: qwikViteOpts.devTools, sourcemap: !!viteConfig.build?.sourcemap, lint: qwikViteOpts.lint, @@ -334,9 +339,9 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { updatedViteConfig.build!.rollupOptions = { input: opts.input, output: normalizeRollupOutputOptions( - path, opts, - viteConfig.build?.rollupOptions?.output + viteConfig.build?.rollupOptions?.output, + useAssetsDir ).map((outputOptsObj) => { outputOptsObj.dir = buildOutputDir; return outputOptsObj; @@ -555,12 +560,13 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { fileName: Q_MANIFEST_FILENAME, source: clientManifestStr, }); + const sys = qwikPlugin.getSys(); + const filePath = sys.path.dirname(_.chunkFileNames as string); this.emitFile({ type: 'asset', - fileName: `build/q-bundle-graph-${manifest.manifestHash}.json`, + fileName: sys.path.join(filePath, `q-bundle-graph-${manifest.manifestHash}.json`), source: JSON.stringify(convertManifestToBundleGraph(manifest)), }); - const sys = qwikPlugin.getSys(); const fs: typeof import('fs') = await sys.dynamicImport('node:fs'); const workerScriptPath = (await this.resolve('@builder.io/qwik/qwik-prefetch.js'))!.id; const workerScript = await fs.promises.readFile(workerScriptPath, 'utf-8'); @@ -1041,6 +1047,7 @@ export interface QwikVitePluginApi { getRootDir: () => string | null; getClientOutDir: () => string | null; getClientPublicOutDir: () => string | null; + getAssetsDir: () => string | undefined; } /** @public */ diff --git a/packages/qwik/src/optimizer/src/plugins/vite.unit.ts b/packages/qwik/src/optimizer/src/plugins/vite.unit.ts index d6279829eb7..427c9ddc06b 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.unit.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.unit.ts @@ -1,9 +1,9 @@ import path, { resolve } from 'node:path'; -import { convertManifestToBundleGraph, qwikVite } from './vite'; -import type { OptimizerOptions, QwikBundle, QwikManifest } from '../types'; import type { Rollup } from 'vite'; +import { assert, expect, suite, test } from 'vitest'; import { normalizePath } from '../../../testing/util'; -import { assert, test, suite, expect } from 'vitest'; +import type { OptimizerOptions, QwikBundle, QwikManifest } from '../types'; +import { convertManifestToBundleGraph, qwikVite } from './vite'; const cwd = process.cwd(); @@ -310,6 +310,73 @@ test('command: serve, --mode ssr', async () => { assert.deepEqual(opts.resolveQwikBuild, true); }); +test('command: serve, --mode ssr with build.assetsDir', async () => { + const initOpts = { + optimizerOptions: mockOptimizerOptions(), + ssr: { + input: resolve(cwd, 'src', 'renderz.tsx'), + outDir: resolve(cwd, 'ssr-dist'), + }, + }; + const plugin = qwikVite(initOpts)[0]; + const c: any = (await plugin.config( + { build: { emptyOutDir: true, assetsDir: 'my-assets-dir' } }, + { command: 'serve', mode: 'ssr' } + ))!; + const opts = await plugin.api?.getOptions(); + const build = c.build!; + const rollupOptions = build!.rollupOptions!; + + assert.deepEqual(opts.target, 'ssr'); + assert.deepEqual(opts.buildMode, 'development'); + assert.deepEqual(build.minify, undefined); + assert.deepEqual(build.ssr, undefined); + assert.deepEqual(rollupOptions.input, [normalizePath(resolve(cwd, 'src', 'renderz.tsx'))]); + assert.deepEqual(c.build.outDir, normalizePath(resolve(cwd, 'ssr-dist'))); + assert.deepEqual(build.emptyOutDir, undefined); + assert.deepEqual(c.publicDir, undefined); + assert.deepEqual(opts.resolveQwikBuild, true); +}); + +test('should use the dist/ fallback with client target', async () => { + const initOpts = { + optimizerOptions: mockOptimizerOptions(), + }; + const plugin = qwikVite(initOpts)[0]; + const c: any = (await plugin.config( + { build: { assetsDir: 'my-assets-dir/' } }, + { command: 'serve' } + ))!; + + assert.equal(c.build.outDir, normalizePath(resolve(cwd, `dist`))); +}); + +test('should use build.outDir config with client target', async () => { + const initOpts = { + optimizerOptions: mockOptimizerOptions(), + }; + const plugin = qwikVite(initOpts)[0]; + const c: any = (await plugin.config( + { build: { outDir: 'my-dist/', assetsDir: 'my-assets-dir' } }, + { command: 'serve' } + ))!; + + assert.equal(c.build.outDir, normalizePath(resolve(cwd, `my-dist`))); +}); + +test('should use build.outDir config when assetsDir is _astro', async () => { + const initOpts = { + optimizerOptions: mockOptimizerOptions(), + }; + + const plugin = qwikVite(initOpts)[0]; + + // Astro sets a build.assetsDir of _astro, but we don't want to change that + const c: any = (await plugin.config({ build: { assetsDir: '_astro' } }, { command: 'serve' }))!; + + assert.equal(c.build.outDir, normalizePath(resolve(cwd, `dist/`))); +}); + test('command: build, --mode lib', async () => { const initOpts = { optimizerOptions: mockOptimizerOptions(), From 655712c02d3bd2462a6e6b348cb04196e6f2274d Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:15:29 +0200 Subject: [PATCH 208/412] fix: disable PrefetchGraph in dev mode (#6604) --- packages/qwik/src/core/components/prefetch.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/qwik/src/core/components/prefetch.ts b/packages/qwik/src/core/components/prefetch.ts index 01589d89d14..2fcb81e58e8 100644 --- a/packages/qwik/src/core/components/prefetch.ts +++ b/packages/qwik/src/core/components/prefetch.ts @@ -143,6 +143,13 @@ const PREFETCH_CODE = /*#__PURE__*/ (( export const PrefetchGraph = ( opts: { base?: string; manifestHash?: string; manifestURL?: string; nonce?: string } = {} ) => { + const isTest = import.meta.env.TEST; + if (isDev && !isTest) { + const props = { + dangerouslySetInnerHTML: '', + }; + return _jsxC('script', props, 0, 'prefetch-graph'); + } const serverData = useServerData>('containerAttributes', {}); const resolvedOpts = { // /build/q-bundle-graph-${manifestHash}.json is always within the q:base location /build/ From 3a9ee62e26fe94e08f30a4b809e92470086ed290 Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Tue, 25 Jun 2024 22:17:51 +0200 Subject: [PATCH 209/412] fix: disable PrefetchServiceWorker in dev mode (#6606) --- packages/qwik/src/core/components/prefetch.ts | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/packages/qwik/src/core/components/prefetch.ts b/packages/qwik/src/core/components/prefetch.ts index 2fcb81e58e8..cec617bd361 100644 --- a/packages/qwik/src/core/components/prefetch.ts +++ b/packages/qwik/src/core/components/prefetch.ts @@ -30,6 +30,14 @@ export const PrefetchServiceWorker = (opts: { fetchBundleGraph?: boolean; nonce?: string; }): JSXNode<'script'> => { + const isTest = import.meta.env.TEST; + if (isDev && !isTest) { + const props = { + dangerouslySetInnerHTML: '', + }; + return _jsxC('script', props, 0, 'prefetch-service-worker'); + } + const serverData = useServerData>('containerAttributes', {}); // if an MFE app has a custom BASE_URL then this will be the correct value // if you're not using MFE from another codebase then you want to override this value to your custom setup @@ -51,32 +59,6 @@ export const PrefetchServiceWorker = (opts: { // the file 'qwik-prefetch-service-worker.js' is not located in /build/ resolvedOpts.path = baseUrl + resolvedOpts.path; } - // dev only errors - if (isDev) { - // Check if base ends with a '/' - if (!resolvedOpts.base.endsWith('/')) { - throw new Error( - `The 'base' option should always end with a '/'. Received: ${resolvedOpts.base}` - ); - } - // Check if path does not start with a '/' and ends with '.js' - if (!resolvedOpts.path.endsWith('.js')) { - throw new Error(`The 'path' option must end with '.js'. Received: ${resolvedOpts.path}`); - } - // Validate service worker scope (must start with a '/' and not contain spaces) - if (!resolvedOpts.scope.startsWith('/') || /\s/.test(resolvedOpts.scope)) { - throw new Error( - `Invalid 'scope' option for service worker. It must start with '/' and contain no spaces. Received: ${resolvedOpts.scope}` - ); - } - if (resolvedOpts.verbose) { - // eslint-disable-next-line no-console - console.log( - 'Installing service-worker with options:', - resolvedOpts - ); - } - } let code = PREFETCH_CODE.replace('URL', resolvedOpts.path).replace('SCOPE', resolvedOpts.scope); if (!isDev) { code = code.replaceAll(/\s+/gm, ''); From a2661bd183db4350162400b24b3ae2ffbbb2c0aa Mon Sep 17 00:00:00 2001 From: Cody Roberts Date: Wed, 26 Jun 2024 07:24:04 -0500 Subject: [PATCH 210/412] docs: small grammar change (#6609) Small grammar change Small grammar fix in the $ symbol section about the optimizer. --- packages/docs/src/routes/docs/(qwik)/advanced/dollar/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs/src/routes/docs/(qwik)/advanced/dollar/index.mdx b/packages/docs/src/routes/docs/(qwik)/advanced/dollar/index.mdx index 970e701ebe8..167eb0f44c7 100644 --- a/packages/docs/src/routes/docs/(qwik)/advanced/dollar/index.mdx +++ b/packages/docs/src/routes/docs/(qwik)/advanced/dollar/index.mdx @@ -27,7 +27,7 @@ The `$` suffix is used to signal both the optimizer and the developer when this ## Compiler-time implications -[Optimizer](../optimizer/index.mdx) runs as a Vite plugin during bundling. The purpose of the Optimizer is to break up the application into many small lazy-loadable chunks. The Optimizer moves expressions (usually functions) into new files and leaves behind a reference pointing to where the expression was moved from. +The [optimizer](../optimizer/index.mdx) runs as a Vite plugin during bundling. The purpose of the Optimizer is to break up the application into many small lazy-loadable chunks. The Optimizer moves expressions (usually functions) into new files and leaves behind a reference pointing to where the expression was moved from. The `$` tells the optimizer which functions to extract into a separate file and which ones to leave untouched. The optimizer does not keep an internal list of magic functions, instead, it solely relies on the `$` suffix to know which functions to transform. The system is extendable and developers can create their own `$` functions, such as `myCustomFunction$()`. From 0fc3c74bb600e3ae4cb2706a3b9fe2565a372258 Mon Sep 17 00:00:00 2001 From: gimonaa <125867318+gimonaa@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:33:13 +0200 Subject: [PATCH 211/412] docs: added an example for creating a dynamic Leaflet map (#6599) --------- Co-authored-by: gioboa --- packages/docs/package.json | 2 + .../demo/cookbook/leaflet-map/index.tsx | 174 ++++++++++++++ .../docs/integrations/leaflet-map/index.mdx | 222 ++++++++++++++++++ pnpm-lock.yaml | 23 ++ 4 files changed, 421 insertions(+) create mode 100644 packages/docs/src/routes/demo/cookbook/leaflet-map/index.tsx diff --git a/packages/docs/package.json b/packages/docs/package.json index abd093a3d4b..f5e9e420d24 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -21,6 +21,7 @@ "@mui/system": "^5.15.14", "@mui/x-data-grid": "^6.19.6", "@supabase/supabase-js": "^2.39.8", + "@types/leaflet": "^1.9.12", "@types/prismjs": "^1.26.3", "@types/react": "^18.2.79", "@types/react-dom": "^18.2.25", @@ -30,6 +31,7 @@ "autoprefixer": "^10.4.18", "fflate": "^0.8.2", "gray-matter": "4.0.3", + "leaflet": "^1.9.4", "openai": "^3.3.0", "postcss": "^8.4.37", "prettier": "^3.2.5", diff --git a/packages/docs/src/routes/demo/cookbook/leaflet-map/index.tsx b/packages/docs/src/routes/demo/cookbook/leaflet-map/index.tsx new file mode 100644 index 00000000000..f65ea1308bb --- /dev/null +++ b/packages/docs/src/routes/demo/cookbook/leaflet-map/index.tsx @@ -0,0 +1,174 @@ +import { + component$, + noSerialize, + useSignal, + useStyles$, + useVisibleTask$, + type Signal, +} from '@builder.io/qwik'; +import * as L from 'leaflet'; +import leafletStyles from 'leaflet/dist/leaflet.css?inline'; + +export default component$(() => { + useStyles$( + leafletStyles + + ` + .marker-label { + color: red; + font-weight: 700; + } + ` + ); + + const markers: Record = { + FDA: [ + { + name: "Terzo d'Aquileia", + label: 'TRZ', + lat: '45.770946', + lon: '13.31338', + }, + { + name: 'Musi', + label: 'MUS', + lat: '46.312663', + lon: '13.274682', + }, + ], + FVG: [ + { + name: 'Borgo Grotta Gigante', + label: 'BGG', + lat: '45.709385', + lon: '13.764681', + }, + { + name: 'Muggia', + label: 'MGG', + lat: '45.610495', + lon: '13.752682', + }, + ], + }; + + const groupSig = useSignal('FDA'); + const currentLocation = useSignal({ + name: 'Udine', + point: [46.06600881056668, 13.237724558490601], + zoom: 10, + marker: true, + }); + + return ( + <> + Change markers:{' '} + + + + ); +}); + +// The properties (props) used in the `LeafletMap` component and other related components are defined as follows: + +export interface MapProps { + location: Signal; + markers?: MarkersProps[]; + group?: Signal; +} + +export interface LocationsProps { + name: string; + point: [number, number]; + zoom: number; + marker: boolean; +} + +export interface MarkersProps { + name: string; + label: string; + lat: string; + lon: string; +} + +/* +The `LeafletMap` component leverages the Leaflet library to render an interactive map. +This component can be configured with various properties (props) to set the central location, add markers, and draw boundaries. +In the `LeafletMap` component, both the location and the group signal are tracked. +This ensures that when the signal changes, the server function is called, and the map is updated with the new data. +*/ + +export const LeafletMap = component$( + ({ location, markers, group }) => { + const mapContainerSig = useSignal(); + + useVisibleTask$(async ({ track }) => { + track(location); + group && track(group); + + if (mapContainerSig.value) { + mapContainerSig.value.remove(); + } + + // center location + const { value: locationData } = location; + const centerPosition = locationData.point; + + // layers + const markersLayer = new L.LayerGroup(); + const bordersLayer = new L.LayerGroup(); + + // map + const map = L.map('map', { + layers: [markersLayer, bordersLayer], + }).setView(centerPosition, locationData.zoom || 14); + L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: + '© OpenStreetMap', + }).addTo(map); + + // center position marker + + const qwikMarker = L.divIcon({ + html: ` + + + + + + `, + className: '', + iconSize: [24, 40], + }); + + locationData.marker && + L.marker(centerPosition, { icon: qwikMarker }) + .bindPopup(`Udine`) + .addTo(map); + + // add markers to map + const markersList = await markers; + markersList && + markersList.map((m) => { + const myIcon = L.divIcon({ + className: 'marker-point', + html: `
${m.label}
`, + }); + L.marker([+m.lat, +m.lon], { icon: myIcon }).addTo(markersLayer); + }); + + mapContainerSig.value = noSerialize(map); + }); + + return
; + } +); diff --git a/packages/docs/src/routes/docs/integrations/leaflet-map/index.mdx b/packages/docs/src/routes/docs/integrations/leaflet-map/index.mdx index 760551dc452..d2ba4614f83 100644 --- a/packages/docs/src/routes/docs/integrations/leaflet-map/index.mdx +++ b/packages/docs/src/routes/docs/integrations/leaflet-map/index.mdx @@ -5,10 +5,14 @@ contributors: - mugan86 - igorbabko - anartzdev + - gimonaa + - gioboa updated_at: '2023-10-03T18:53:23Z' created_at: '2023-09-13T18:55:37Z' --- +import CodeSandbox from '../../../../components/code-sandbox/index.tsx'; + # LeafletJS Map Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. @@ -38,6 +42,224 @@ It also adds new files to your project folder: - `src/components/leaflet-map/index.tsx`: Leaflet map simple map features component. - `src/routes/basic-map/index.tsx`: Example to consume Leaflet Map component with demo data +## Example + +The main component configures the map, including the initial position and the group of markers to load. +This setup allows you to create a dynamic and interactive map that can be easily configured and extended with different locations and markers. + +### Here is an example: + + +```tsx +import { + component$, + noSerialize, + useSignal, + useStyles$, + useVisibleTask$, + type Signal, +} from '@builder.io/qwik'; +import * as L from 'leaflet'; +import leafletStyles from 'leaflet/dist/leaflet.css?inline'; + +// Sample data json and geojson + +export const fvg: any = { + type: 'FeatureCollection', + name: 'FVG_line_0_001', + crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' } }, + features: [ + { + type: 'Feature', + properties: { ID_OGG: '08020060000', NAME: 'GEOJSON NAME' }, + geometry: { + type: 'MultiLineString', + coordinates: [ + [ + [12.4188, 46.3528], + [12.4178, 46.3547], + [12.4284, 46.3517], + [12.4425, 46.3599], + [12.4488, 46.3605], + [12.4554, 46.3652], + [12.4552, 46.3672], + [12.4513, 46.3706], + ], + ], + }, + }, + ], +}; + +const markers: Record = { + FDA: [ + { + name: "Terzo d'Aquileia", + label: 'TRZ', + lat: '45.770946', + lon: '13.31338', + }, + { + name: 'Musi', + label: 'MUS', + lat: '46.312663', + lon: '13.274682', + }, + ], + FVG: [ + { + name: 'Borgo Grotta Gigante', + label: 'BGG', + lat: '45.709385', + lon: '13.764681', + }, + { + name: 'Muggia', + label: 'MGG', + lat: '45.610495', + lon: '13.752682', + }, + ], +}; + +export default component$(() => { + useStyles$( + leafletStyles + + ` + .marker-label { + color: red; + font-weight: 700; + } + ` + ); + + const groupSig = useSignal('FDA'); + const currentLocation = useSignal({ + name: 'Udine', + point: [46.06600881056668, 13.237724558490601], + zoom: 10, + marker: true, + }); + + return ( + <> + Change markers:{' '} + + + + ); +}); + +// The properties (props) used in the `LeafletMap` component and other related components are defined as follows: + +export interface MapProps { + location: Signal; + markers?: MarkersProps[]; + group?: Signal; +} + +export interface LocationsProps { + name: string; + point: [number, number]; + zoom: number; + marker: boolean; +} + +export interface MarkersProps { + name: string; + label: string; + lat: string; + lon: string; +} + +/* +The `LeafletMap` component leverages the Leaflet library to render an interactive map. +This component can be configured with various properties (props) to set the central location, add markers, and draw boundaries. +In the `LeafletMap` component, both the location and the group signal are tracked. +This ensures that when the signal changes, the server function is called, and the map is updated with the new data. +*/ + +export const LeafletMap = component$( + ({ location, markers, group }) => { + const mapContainerSig = useSignal(); + + useVisibleTask$(async ({ track }) => { + track(location); + group && track(group); + + if (mapContainerSig.value) { + mapContainerSig.value.remove(); + } + + // center location + const { value: locationData } = location; + const centerPosition = locationData.point; + + // layers + const markersLayer = new L.LayerGroup(); + const bordersLayer = new L.LayerGroup(); + + // map + const map = L.map('map', { + layers: [markersLayer, bordersLayer], + }).setView(centerPosition, locationData.zoom || 14); + L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: + '© OpenStreetMap', + }).addTo(map); + + // center position marker + + const qwikMarker = L.divIcon({ + html: ` + + + + + + `, + className: '', + iconSize: [24, 40], + }); + + locationData.marker && + L.marker(centerPosition, { icon: qwikMarker }) + .bindPopup(`Udine`) + .addTo(map); + + // add boundaries to map + L.geoJSON(fvg, { style: { color: '#005DA4' } }).addTo(bordersLayer); + + // add markers to map + const markersList = await markers; + markersList && + markersList.map((m) => { + const myIcon = L.divIcon({ + className: 'marker-point', + html: `
${m.label}
`, + }); + L.marker([+m.lat, +m.lon], { icon: myIcon }).addTo(markersLayer); + }); + + mapContainerSig.value = noSerialize(map); + }); + + return
; + } +); +``` +
+ ## Interesting info about LeafletJS Map: ### Official diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07cc647deee..35368446b19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -296,6 +296,9 @@ importers: '@supabase/supabase-js': specifier: ^2.39.8 version: 2.43.1 + '@types/leaflet': + specifier: ^1.9.12 + version: 1.9.12 '@types/prismjs': specifier: ^1.26.3 version: 1.26.3 @@ -323,6 +326,9 @@ importers: gray-matter: specifier: 4.0.3 version: 4.0.3 + leaflet: + specifier: ^1.9.4 + version: 1.9.4 openai: specifier: ^3.3.0 version: 3.3.0 @@ -2997,6 +3003,9 @@ packages: '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/geojson@7946.0.14': + resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==} + '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} @@ -3024,6 +3033,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/leaflet@1.9.12': + resolution: {integrity: sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==} + '@types/mdast@4.0.3': resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} @@ -6468,6 +6480,9 @@ packages: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} + leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -12013,6 +12028,8 @@ snapshots: '@types/qs': 6.9.11 '@types/serve-static': 1.15.5 + '@types/geojson@7946.0.14': {} + '@types/hast@2.3.10': dependencies: '@types/unist': 2.0.10 @@ -12041,6 +12058,10 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/leaflet@1.9.12': + dependencies: + '@types/geojson': 7946.0.14 + '@types/mdast@4.0.3': dependencies: '@types/unist': 3.0.2 @@ -16015,6 +16036,8 @@ snapshots: dependencies: readable-stream: 2.3.8 + leaflet@1.9.4: {} + leven@3.1.0: {} levn@0.4.1: From 3ce3456aea58415eebda737466d3bde7e655ed8e Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:05:24 +0200 Subject: [PATCH 212/412] fix: fix up csr mode (#6611) Closed: #6567 --- packages/qwik/src/optimizer/src/plugins/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index 71ea032a608..9b631840a41 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -330,7 +330,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { if (typeof opts.srcDir === 'string' && !fs.existsSync(opts.srcDir)) { throw new Error(`Qwik srcDir "${opts.srcDir}" not found.`); } - for (const [_, input] of Object.entries(opts.input)) { + for (const [_, input] of Object.entries(opts.input || {})) { const resolved = await resolver(input); if (!resolved) { throw new Error(`Qwik input "${input}" not found.`); From 52d27993d4f6cdbf9c47662e85b52954a1d1668a Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Fri, 28 Jun 2024 00:41:44 +0200 Subject: [PATCH 213/412] fix: solve _routes issue with assetsDir (#6612) --- packages/qwik-city/adapters/cloudflare-pages/vite/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts b/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts index da287350835..2ae972b0ff4 100644 --- a/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts +++ b/packages/qwik-city/adapters/cloudflare-pages/vite/index.ts @@ -41,10 +41,13 @@ export function cloudflarePagesAdapter(opts: CloudflarePagesAdapterOptions = {}) const routesJsonPath = join(clientOutDir, '_routes.json'); const hasRoutesJson = fs.existsSync(routesJsonPath); if (!hasRoutesJson && opts.functionRoutes !== false) { - const pathName = assetsDir ? join(basePathname, assetsDir) : basePathname; + let pathName = assetsDir ? join(basePathname, assetsDir) : basePathname; + if (!pathName.endsWith('/')) { + pathName += '/'; + } const routesJson = { version: 1, - include: [pathName + '*'], + include: [basePathname + '*'], exclude: [pathName + 'build/*', pathName + 'assets/*'], }; await fs.promises.writeFile(routesJsonPath, JSON.stringify(routesJson, undefined, 2)); From 860327535e6e754d7f23c1af7f0b546b8da01a07 Mon Sep 17 00:00:00 2001 From: Srinand Diddi <71537129+lollyxsrinand@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:19:03 +0530 Subject: [PATCH 214/412] docs: fix typo (#6615) --- .../docs/src/routes/docs/integrations/modular-forms/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx b/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx index ac029d1b2fc..a691df416cd 100644 --- a/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx +++ b/packages/docs/src/routes/docs/integrations/modular-forms/index.mdx @@ -44,7 +44,7 @@ const LoginSchema = v.object({ email: v.pipe( v.string(), v.nonEmpty('Please enter your email.'), - v.email('he email address is badly formatted.'), + v.email('The email address is badly formatted.'), ), password: v.pipe( v.string(), From eddcfbf2eabedbedc9bfc040920cf1b55bd0509b Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:35:10 +0200 Subject: [PATCH 215/412] chore: 1.6.0 (#6616) --- package.json | 2 +- packages/create-qwik/package.json | 2 +- packages/eslint-plugin-qwik/package.json | 2 +- packages/qwik-city/package.json | 2 +- packages/qwik/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2602320175c..6dec6473a89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qwik-monorepo", - "version": "1.5.7", + "version": "1.6.0", "comments": { "01": "devDependencies includes reference to @builder.io/qwik: workspace: *. This is needed or e2e tests will fail", "02": " It would be nice to be able to remove this dependency and fix the test.", diff --git a/packages/create-qwik/package.json b/packages/create-qwik/package.json index 0ab6772f513..ace0a099db8 100644 --- a/packages/create-qwik/package.json +++ b/packages/create-qwik/package.json @@ -1,7 +1,7 @@ { "name": "create-qwik", "description": "Interactive CLI for create Qwik projects and adding features.", - "version": "1.5.7", + "version": "1.6.0", "author": "Builder.io Team", "bin": "./create-qwik.cjs", "bugs": "https://github.com/QwikDev/qwik/issues", diff --git a/packages/eslint-plugin-qwik/package.json b/packages/eslint-plugin-qwik/package.json index fc99b2447af..6d3ac3716d4 100644 --- a/packages/eslint-plugin-qwik/package.json +++ b/packages/eslint-plugin-qwik/package.json @@ -1,7 +1,7 @@ { "name": "eslint-plugin-qwik", "description": "An Open-Source sub-framework designed with a focus on server-side-rendering, lazy-loading, and styling/animation.", - "version": "1.5.7", + "version": "1.6.0", "author": "Builder Team", "bugs": "https://github.com/QwikDev/qwik/issues", "dependencies": { diff --git a/packages/qwik-city/package.json b/packages/qwik-city/package.json index cbd4d1e3c6c..84183170980 100644 --- a/packages/qwik-city/package.json +++ b/packages/qwik-city/package.json @@ -1,7 +1,7 @@ { "name": "@builder.io/qwik-city", "description": "The meta-framework for Qwik.", - "version": "1.5.7", + "version": "1.6.0", "bugs": "https://github.com/QwikDev/qwik/issues", "dependencies": { "@mdx-js/mdx": "^3.0.1", diff --git a/packages/qwik/package.json b/packages/qwik/package.json index b7f9fa78ef6..ec978f467e4 100644 --- a/packages/qwik/package.json +++ b/packages/qwik/package.json @@ -1,7 +1,7 @@ { "name": "@builder.io/qwik", "description": "An Open-Source sub-framework designed with a focus on server-side-rendering, lazy-loading, and styling/animation.", - "version": "1.5.7", + "version": "1.6.0", "annotation": "This package.json is for internal use in the monorepo, the build actually makes a new package.json for the published package via scripts/package-json.ts", "bin": "./qwik-cli.cjs", "bugs": "https://github.com/QwikDev/qwik/issues", From b41be8342f28c8fb72f4866aeb7dd6e3ab139e34 Mon Sep 17 00:00:00 2001 From: Giorgio Boa <35845425+gioboa@users.noreply.github.com> Date: Sat, 29 Jun 2024 17:59:41 +0200 Subject: [PATCH 216/412] chore: improve needs reproduction message (#6618) --- .github/workflows/labeling-issues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/labeling-issues.yml b/.github/workflows/labeling-issues.yml index 1a4ceba9641..5321f8a7460 100644 --- a/.github/workflows/labeling-issues.yml +++ b/.github/workflows/labeling-issues.yml @@ -28,6 +28,7 @@ jobs: issue-number: ${{ github.event.issue.number }} body: | Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://qwik.new). + [Here](https://antfu.me/posts/why-reproductions-are-required#why-reproduction) is why we really need a minimal reproduction. Issues marked with `STATUS-2: needs reproduction` will be automatically closed if they have no activity within 14 days. Thanks 🙏 From 4485d32546c6f854d6d4dd3c35a30bc2e6789c92 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Mon, 1 Jul 2024 01:07:13 +0200 Subject: [PATCH 217/412] fix(dev): path to outside-project files (#6624) / gets prepended due to the request and parentPath already has / --- packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts index 156406bfc48..8e05b69289d 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts @@ -165,7 +165,7 @@ export async function configureDevServer( // use the full path, otherwise relative to the source const qrlPath = parentPath.startsWith(opts.rootDir) ? path.relative(opts.rootDir, parentPath) - : `/@fs/${parentPath}`; + : `@fs${parentPath}`; const qrlFile = `${encode(qrlPath)}/${symbolName.toLowerCase()}.js?_qrl_parent=${encode(parent)}`; return [symbolName, `${base}${qrlFile}`]; }, From 9236a5f27e1e9d08d9bd47f05cca337aea4db374 Mon Sep 17 00:00:00 2001 From: Cody Roberts Date: Mon, 1 Jul 2024 21:43:02 -0500 Subject: [PATCH 218/412] docs: Small grammatical corrections (#6630) --- .../docs/src/routes/docs/(qwikcity)/route-loader/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docs/src/routes/docs/(qwikcity)/route-loader/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/route-loader/index.mdx index 272c8577960..3e41c10f83a 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/route-loader/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/route-loader/index.mdx @@ -128,7 +128,7 @@ The above example shows two `routeLoader$`s being used in the same file. A gener Just like [middleware](/docs/middleware/) or [endpoint](/docs/endpoints/) `onRequest` and `onGet`, `routeLoader$`s have access to the [`RequestEvent`](/docs/middleware#requestevent) API which includes information about the current HTTP request. -This information comes in handy when the loader needs to conditionally return data based on the request, or it needs to override the response status, headers or body manually. +This information comes in handy when the loader needs to conditionally return data based on the request, or it needs to override the response status, headers, or body manually. ```tsx /requestEvent/ title="src/routes/product/[user]/index.tsx" import { routeLoader$ } from '@builder.io/qwik-city'; @@ -235,8 +235,8 @@ export default component$(() => { ## Performance considerations -Route Loaders are executed on the server, after every navigation. This means that they are executed every time a user navigates to a page in a SPA or MPA, and they are executed even if the user is navigating to the same page. +Route Loaders are executed on the server, after every navigation. This means that they are executed every time a user navigates to a page in an SPA or MPA, and they are executed even if the user is navigating to the same page. -Loaders execute after the Qwik Middlewares handlers (`onRequest`, `onGet`, `onPost`, etc), and before the Qwik Components are rendered. This allows the loaders to start fetching data as soon as possible, reducing latency. +Loaders execute after the Qwik Middleware handlers (`onRequest`, `onGet`, `onPost`, etc), and before the Qwik Components are rendered. This allows the loaders to start fetching data as soon as possible, reducing latency. From 8a5ef13c27a41a3521cfdaef87d9d39b7ddb2d86 Mon Sep 17 00:00:00 2001 From: StEve YoUng <2747745470@qq.com> Date: Tue, 2 Jul 2024 11:23:44 +0800 Subject: [PATCH 219/412] docs(starts): fix typo depencies (#6631) --- starters/apps/base/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starters/apps/base/vite.config.ts b/starters/apps/base/vite.config.ts index 7de1684d069..bd9b3f7f4a1 100644 --- a/starters/apps/base/vite.config.ts +++ b/starters/apps/base/vite.config.ts @@ -30,7 +30,7 @@ export default defineConfig(({ command, mode }): UserConfig => { }, /** - * This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev depencies. (otherwise things will break in production) + * This is an advanced setting. It improves the bundling of your server code. To use it, make sure you understand when your consumed packages are dependencies or dev dependencies. (otherwise things will break in production) */ // ssr: // command === "build" && mode === "production" From 220536fa8d938209d26fa654ad4add5971c3b506 Mon Sep 17 00:00:00 2001 From: Jack Shelton <104264123+thejackshelton@users.noreply.github.com> Date: Tue, 2 Jul 2024 22:33:00 -0500 Subject: [PATCH 220/412] refactor: simplify CLI starters (#6632) --- .../src/run-create-interactive-cli.ts | 5 +- scripts/validate-cli.ts | 3 +- starters/apps/empty/package.json | 2 +- starters/apps/library/package.json | 4 +- .../apps/{basic => playground}/package.json | 4 +- .../{basic => playground}/public/favicon.svg | 0 .../public/fonts/poppins-400.woff2 | Bin .../public/fonts/poppins-500.woff2 | Bin .../public/fonts/poppins-700.woff2 | Bin .../public/manifest.json | 0 .../{basic => playground}/public/robots.txt | 0 .../components/router-head/router-head.tsx | 0 .../starter/counter/counter.module.css | 0 .../components/starter/counter/counter.tsx | 0 .../starter/footer/footer.module.css | 0 .../src/components/starter/footer/footer.tsx | 0 .../components/starter/gauge/gauge.module.css | 0 .../src/components/starter/gauge/index.tsx | 0 .../starter/header/header.module.css | 0 .../src/components/starter/header/header.tsx | 0 .../components/starter/hero/hero.module.css | 0 .../src/components/starter/hero/hero.tsx | 0 .../src/components/starter/icons/qwik.tsx | 0 .../starter/infobox/infobox.module.css | 0 .../components/starter/infobox/infobox.tsx | 0 .../starter/next-steps/next-steps.module.css | 0 .../starter/next-steps/next-steps.tsx | 0 .../apps/{basic => playground}/src/global.css | 0 .../src/media/thunder.png | Bin .../apps/{basic => playground}/src/root.tsx | 0 .../src/routes/demo/flower/flower.css | 0 .../src/routes/demo/flower/index.tsx | 0 .../src/routes/demo/todolist/index.tsx | 0 .../routes/demo/todolist/todolist.module.css | 0 .../src/routes/index.tsx | 0 .../src/routes/layout.tsx | 0 .../src/routes/service-worker.ts | 0 .../src/routes/styles.css | 0 starters/apps/site-with-visual-cms/.env | 2 - starters/apps/site-with-visual-cms/README.md | 15 -- .../apps/site-with-visual-cms/package.json | 17 --- .../site-with-visual-cms/public/favicon.svg | 1 - .../site-with-visual-cms/public/manifest.json | 9 -- .../site-with-visual-cms/public/robots.txt | 0 .../src/components/builder-registry.ts | 25 ---- .../src/components/counter/counter.module.css | 23 ---- .../src/components/counter/counter.tsx | 81 ----------- .../src/components/footer/footer.module.css | 17 --- .../src/components/footer/footer.tsx | 14 -- .../src/components/gauge/gauge.module.css | 22 --- .../src/components/gauge/index.tsx | 36 ----- .../src/components/header/header.module.css | 50 ------- .../src/components/header/header.tsx | 43 ------ .../src/components/icons/qwik.tsx | 44 ------ .../components/router-head/router-head.tsx | 32 ----- .../site-with-visual-cms/src/entry.dev.tsx | 17 --- .../src/entry.preview.tsx | 20 --- .../site-with-visual-cms/src/entry.ssr.tsx | 33 ----- .../apps/site-with-visual-cms/src/global.css | 128 ------------------ .../apps/site-with-visual-cms/src/root.tsx | 32 ----- .../src/routes/[...index]/index.tsx | 61 --------- .../src/routes/layout.tsx | 15 -- .../src/routes/service-worker.ts | 18 --- .../apps/site-with-visual-cms/vite.config.ts | 11 -- 64 files changed, 10 insertions(+), 774 deletions(-) rename starters/apps/{basic => playground}/package.json (61%) rename starters/apps/{basic => playground}/public/favicon.svg (100%) rename starters/apps/{basic => playground}/public/fonts/poppins-400.woff2 (100%) rename starters/apps/{basic => playground}/public/fonts/poppins-500.woff2 (100%) rename starters/apps/{basic => playground}/public/fonts/poppins-700.woff2 (100%) rename starters/apps/{basic => playground}/public/manifest.json (100%) rename starters/apps/{basic => playground}/public/robots.txt (100%) rename starters/apps/{basic => playground}/src/components/router-head/router-head.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/counter/counter.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/counter/counter.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/footer/footer.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/footer/footer.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/gauge/gauge.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/gauge/index.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/header/header.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/header/header.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/hero/hero.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/hero/hero.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/icons/qwik.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/infobox/infobox.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/infobox/infobox.tsx (100%) rename starters/apps/{basic => playground}/src/components/starter/next-steps/next-steps.module.css (100%) rename starters/apps/{basic => playground}/src/components/starter/next-steps/next-steps.tsx (100%) rename starters/apps/{basic => playground}/src/global.css (100%) rename starters/apps/{basic => playground}/src/media/thunder.png (100%) rename starters/apps/{basic => playground}/src/root.tsx (100%) rename starters/apps/{basic => playground}/src/routes/demo/flower/flower.css (100%) rename starters/apps/{basic => playground}/src/routes/demo/flower/index.tsx (100%) rename starters/apps/{basic => playground}/src/routes/demo/todolist/index.tsx (100%) rename starters/apps/{basic => playground}/src/routes/demo/todolist/todolist.module.css (100%) rename starters/apps/{basic => playground}/src/routes/index.tsx (100%) rename starters/apps/{basic => playground}/src/routes/layout.tsx (100%) rename starters/apps/{basic => playground}/src/routes/service-worker.ts (100%) rename starters/apps/{basic => playground}/src/routes/styles.css (100%) delete mode 100644 starters/apps/site-with-visual-cms/.env delete mode 100644 starters/apps/site-with-visual-cms/README.md delete mode 100644 starters/apps/site-with-visual-cms/package.json delete mode 100644 starters/apps/site-with-visual-cms/public/favicon.svg delete mode 100644 starters/apps/site-with-visual-cms/public/manifest.json delete mode 100644 starters/apps/site-with-visual-cms/public/robots.txt delete mode 100644 starters/apps/site-with-visual-cms/src/components/builder-registry.ts delete mode 100644 starters/apps/site-with-visual-cms/src/components/counter/counter.module.css delete mode 100644 starters/apps/site-with-visual-cms/src/components/counter/counter.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/components/footer/footer.module.css delete mode 100644 starters/apps/site-with-visual-cms/src/components/footer/footer.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/components/gauge/gauge.module.css delete mode 100644 starters/apps/site-with-visual-cms/src/components/gauge/index.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/components/header/header.module.css delete mode 100644 starters/apps/site-with-visual-cms/src/components/header/header.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/components/icons/qwik.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/components/router-head/router-head.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/entry.dev.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/entry.preview.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/entry.ssr.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/global.css delete mode 100644 starters/apps/site-with-visual-cms/src/root.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/routes/[...index]/index.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/routes/layout.tsx delete mode 100644 starters/apps/site-with-visual-cms/src/routes/service-worker.ts delete mode 100644 starters/apps/site-with-visual-cms/vite.config.ts diff --git a/packages/create-qwik/src/run-create-interactive-cli.ts b/packages/create-qwik/src/run-create-interactive-cli.ts index d467a97afef..788bf5228be 100644 --- a/packages/create-qwik/src/run-create-interactive-cli.ts +++ b/packages/create-qwik/src/run-create-interactive-cli.ts @@ -43,7 +43,10 @@ export async function runCreateInteractiveCli(): Promise { throw new Error('Base app not found'); } - const starterApps = templateManager.templates.filter((a) => a.id !== baseApp.id); + // sorted alphabetically + const starterApps = templateManager.templates + .filter((a) => a.id !== baseApp.id) + .sort((a, b) => a.name.localeCompare(b.name)); const outDir: string = resolveRelativeDir(projectNameAnswer.trim()); baseApp.target = outDir; diff --git a/scripts/validate-cli.ts b/scripts/validate-cli.ts index 7d88e52b69d..0840a65b6b7 100644 --- a/scripts/validate-cli.ts +++ b/scripts/validate-cli.ts @@ -44,9 +44,8 @@ async function validateCreateQwikCli() { const tmpDir = join(__dirname, '..', 'dist-dev'); await Promise.all([ - validateStarter(api, tmpDir, 'basic', true, `👻`), + validateStarter(api, tmpDir, 'playground', true, `👻`), validateStarter(api, tmpDir, 'empty', true, `🫙`), - validateStarter(api, tmpDir, 'site-with-visual-cms', true, `😈`), validateStarter(api, tmpDir, 'library', false, `📚`), ]).catch((e) => { console.error(e); diff --git a/starters/apps/empty/package.json b/starters/apps/empty/package.json index 8ef63d4d9b7..96020d4a21f 100644 --- a/starters/apps/empty/package.json +++ b/starters/apps/empty/package.json @@ -1,6 +1,6 @@ { "name": "qwik-empty-starter", - "description": "App with Routing built-in ready to create your app", + "description": "Blank project with routing included", "type": "module", "__qwik__": { "priority": 1, diff --git a/starters/apps/library/package.json b/starters/apps/library/package.json index 1fec5dc0978..ed7ab48d57b 100644 --- a/starters/apps/library/package.json +++ b/starters/apps/library/package.json @@ -1,6 +1,6 @@ { "name": "qwik-library-name", - "description": "Create a reusable Qwik component library", + "description": "Create a Qwik library", "version": "0.0.1", "private": false, "main": "./lib/index.qwik.mjs", @@ -48,7 +48,7 @@ "vite-tsconfig-paths": "^4.2.1" }, "__qwik__": { - "displayName": "Component library (Qwik)", + "displayName": "Library (Qwik)", "priority": -1, "docs": [ "https://qwik.dev/docs/getting-started/" diff --git a/starters/apps/basic/package.json b/starters/apps/playground/package.json similarity index 61% rename from starters/apps/basic/package.json rename to starters/apps/playground/package.json index 4023e181cbe..cea100fc5ec 100644 --- a/starters/apps/basic/package.json +++ b/starters/apps/playground/package.json @@ -1,10 +1,10 @@ { "name": "qwik-basic-starter", - "description": "Demo App with Routing built-in (recommended)", + "description": "Demo app with sample routes", "type": "module", "__qwik__": { "priority": 2, - "displayName": "Basic App (Qwik City + Qwik)", + "displayName": "Playground App (Qwik City + Qwik)", "qwikCity": true, "docs": [ "https://qwik.dev/docs/getting-started/" diff --git a/starters/apps/basic/public/favicon.svg b/starters/apps/playground/public/favicon.svg similarity index 100% rename from starters/apps/basic/public/favicon.svg rename to starters/apps/playground/public/favicon.svg diff --git a/starters/apps/basic/public/fonts/poppins-400.woff2 b/starters/apps/playground/public/fonts/poppins-400.woff2 similarity index 100% rename from starters/apps/basic/public/fonts/poppins-400.woff2 rename to starters/apps/playground/public/fonts/poppins-400.woff2 diff --git a/starters/apps/basic/public/fonts/poppins-500.woff2 b/starters/apps/playground/public/fonts/poppins-500.woff2 similarity index 100% rename from starters/apps/basic/public/fonts/poppins-500.woff2 rename to starters/apps/playground/public/fonts/poppins-500.woff2 diff --git a/starters/apps/basic/public/fonts/poppins-700.woff2 b/starters/apps/playground/public/fonts/poppins-700.woff2 similarity index 100% rename from starters/apps/basic/public/fonts/poppins-700.woff2 rename to starters/apps/playground/public/fonts/poppins-700.woff2 diff --git a/starters/apps/basic/public/manifest.json b/starters/apps/playground/public/manifest.json similarity index 100% rename from starters/apps/basic/public/manifest.json rename to starters/apps/playground/public/manifest.json diff --git a/starters/apps/basic/public/robots.txt b/starters/apps/playground/public/robots.txt similarity index 100% rename from starters/apps/basic/public/robots.txt rename to starters/apps/playground/public/robots.txt diff --git a/starters/apps/basic/src/components/router-head/router-head.tsx b/starters/apps/playground/src/components/router-head/router-head.tsx similarity index 100% rename from starters/apps/basic/src/components/router-head/router-head.tsx rename to starters/apps/playground/src/components/router-head/router-head.tsx diff --git a/starters/apps/basic/src/components/starter/counter/counter.module.css b/starters/apps/playground/src/components/starter/counter/counter.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/counter/counter.module.css rename to starters/apps/playground/src/components/starter/counter/counter.module.css diff --git a/starters/apps/basic/src/components/starter/counter/counter.tsx b/starters/apps/playground/src/components/starter/counter/counter.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/counter/counter.tsx rename to starters/apps/playground/src/components/starter/counter/counter.tsx diff --git a/starters/apps/basic/src/components/starter/footer/footer.module.css b/starters/apps/playground/src/components/starter/footer/footer.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/footer/footer.module.css rename to starters/apps/playground/src/components/starter/footer/footer.module.css diff --git a/starters/apps/basic/src/components/starter/footer/footer.tsx b/starters/apps/playground/src/components/starter/footer/footer.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/footer/footer.tsx rename to starters/apps/playground/src/components/starter/footer/footer.tsx diff --git a/starters/apps/basic/src/components/starter/gauge/gauge.module.css b/starters/apps/playground/src/components/starter/gauge/gauge.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/gauge/gauge.module.css rename to starters/apps/playground/src/components/starter/gauge/gauge.module.css diff --git a/starters/apps/basic/src/components/starter/gauge/index.tsx b/starters/apps/playground/src/components/starter/gauge/index.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/gauge/index.tsx rename to starters/apps/playground/src/components/starter/gauge/index.tsx diff --git a/starters/apps/basic/src/components/starter/header/header.module.css b/starters/apps/playground/src/components/starter/header/header.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/header/header.module.css rename to starters/apps/playground/src/components/starter/header/header.module.css diff --git a/starters/apps/basic/src/components/starter/header/header.tsx b/starters/apps/playground/src/components/starter/header/header.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/header/header.tsx rename to starters/apps/playground/src/components/starter/header/header.tsx diff --git a/starters/apps/basic/src/components/starter/hero/hero.module.css b/starters/apps/playground/src/components/starter/hero/hero.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/hero/hero.module.css rename to starters/apps/playground/src/components/starter/hero/hero.module.css diff --git a/starters/apps/basic/src/components/starter/hero/hero.tsx b/starters/apps/playground/src/components/starter/hero/hero.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/hero/hero.tsx rename to starters/apps/playground/src/components/starter/hero/hero.tsx diff --git a/starters/apps/basic/src/components/starter/icons/qwik.tsx b/starters/apps/playground/src/components/starter/icons/qwik.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/icons/qwik.tsx rename to starters/apps/playground/src/components/starter/icons/qwik.tsx diff --git a/starters/apps/basic/src/components/starter/infobox/infobox.module.css b/starters/apps/playground/src/components/starter/infobox/infobox.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/infobox/infobox.module.css rename to starters/apps/playground/src/components/starter/infobox/infobox.module.css diff --git a/starters/apps/basic/src/components/starter/infobox/infobox.tsx b/starters/apps/playground/src/components/starter/infobox/infobox.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/infobox/infobox.tsx rename to starters/apps/playground/src/components/starter/infobox/infobox.tsx diff --git a/starters/apps/basic/src/components/starter/next-steps/next-steps.module.css b/starters/apps/playground/src/components/starter/next-steps/next-steps.module.css similarity index 100% rename from starters/apps/basic/src/components/starter/next-steps/next-steps.module.css rename to starters/apps/playground/src/components/starter/next-steps/next-steps.module.css diff --git a/starters/apps/basic/src/components/starter/next-steps/next-steps.tsx b/starters/apps/playground/src/components/starter/next-steps/next-steps.tsx similarity index 100% rename from starters/apps/basic/src/components/starter/next-steps/next-steps.tsx rename to starters/apps/playground/src/components/starter/next-steps/next-steps.tsx diff --git a/starters/apps/basic/src/global.css b/starters/apps/playground/src/global.css similarity index 100% rename from starters/apps/basic/src/global.css rename to starters/apps/playground/src/global.css diff --git a/starters/apps/basic/src/media/thunder.png b/starters/apps/playground/src/media/thunder.png similarity index 100% rename from starters/apps/basic/src/media/thunder.png rename to starters/apps/playground/src/media/thunder.png diff --git a/starters/apps/basic/src/root.tsx b/starters/apps/playground/src/root.tsx similarity index 100% rename from starters/apps/basic/src/root.tsx rename to starters/apps/playground/src/root.tsx diff --git a/starters/apps/basic/src/routes/demo/flower/flower.css b/starters/apps/playground/src/routes/demo/flower/flower.css similarity index 100% rename from starters/apps/basic/src/routes/demo/flower/flower.css rename to starters/apps/playground/src/routes/demo/flower/flower.css diff --git a/starters/apps/basic/src/routes/demo/flower/index.tsx b/starters/apps/playground/src/routes/demo/flower/index.tsx similarity index 100% rename from starters/apps/basic/src/routes/demo/flower/index.tsx rename to starters/apps/playground/src/routes/demo/flower/index.tsx diff --git a/starters/apps/basic/src/routes/demo/todolist/index.tsx b/starters/apps/playground/src/routes/demo/todolist/index.tsx similarity index 100% rename from starters/apps/basic/src/routes/demo/todolist/index.tsx rename to starters/apps/playground/src/routes/demo/todolist/index.tsx diff --git a/starters/apps/basic/src/routes/demo/todolist/todolist.module.css b/starters/apps/playground/src/routes/demo/todolist/todolist.module.css similarity index 100% rename from starters/apps/basic/src/routes/demo/todolist/todolist.module.css rename to starters/apps/playground/src/routes/demo/todolist/todolist.module.css diff --git a/starters/apps/basic/src/routes/index.tsx b/starters/apps/playground/src/routes/index.tsx similarity index 100% rename from starters/apps/basic/src/routes/index.tsx rename to starters/apps/playground/src/routes/index.tsx diff --git a/starters/apps/basic/src/routes/layout.tsx b/starters/apps/playground/src/routes/layout.tsx similarity index 100% rename from starters/apps/basic/src/routes/layout.tsx rename to starters/apps/playground/src/routes/layout.tsx diff --git a/starters/apps/basic/src/routes/service-worker.ts b/starters/apps/playground/src/routes/service-worker.ts similarity index 100% rename from starters/apps/basic/src/routes/service-worker.ts rename to starters/apps/playground/src/routes/service-worker.ts diff --git a/starters/apps/basic/src/routes/styles.css b/starters/apps/playground/src/routes/styles.css similarity index 100% rename from starters/apps/basic/src/routes/styles.css rename to starters/apps/playground/src/routes/styles.css diff --git a/starters/apps/site-with-visual-cms/.env b/starters/apps/site-with-visual-cms/.env deleted file mode 100644 index 2b548ab08df..00000000000 --- a/starters/apps/site-with-visual-cms/.env +++ /dev/null @@ -1,2 +0,0 @@ -# https://www.builder.io/c/docs/using-your-api-key -PUBLIC_BUILDER_API_KEY=YOUR_API_KEY \ No newline at end of file diff --git a/starters/apps/site-with-visual-cms/README.md b/starters/apps/site-with-visual-cms/README.md deleted file mode 100644 index 54491ea64ed..00000000000 --- a/starters/apps/site-with-visual-cms/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Builder.io + Qwik - -An example of using [Builder.io's](https://www.builder.io/) visual editor with Qwik. - -See the catchall route at [src/routes/[...index]/index.tsx](src/routes/[...index]/index.tsx) for the integration code. - -Registered components can be found in [src/components/builder-registry.ts](src/components/builder-registry.ts) - -### Docs - -See our full integration guides [here](https://www.builder.io/c/docs/developers) - -Also, when you push your integration to production, go back and update your preview URL to your production URL so now anyone on your team can visuall create content in your Qwik app! - -Also, to integrate structured data, see [this guide](https://www.builder.io/c/docs/integrate-cms-data) diff --git a/starters/apps/site-with-visual-cms/package.json b/starters/apps/site-with-visual-cms/package.json deleted file mode 100644 index 84cdbb61301..00000000000 --- a/starters/apps/site-with-visual-cms/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "qwik-site-with-visual-cms", - "description": "Site integrated with Builder.io for visual editing", - "devDependencies": { - "@builder.io/dev-tools": "^0.0.7", - "@builder.io/sdk-qwik": "^0.4.1" - }, - "type": "module", - "__qwik__": { - "priority": 2, - "docs": [ - "https://www.builder.io/c/docs/quickstart" - ], - "displayName": "Site with Visual CMS (Qwik City + Qwik)", - "qwikCity": true - } -} diff --git a/starters/apps/site-with-visual-cms/public/favicon.svg b/starters/apps/site-with-visual-cms/public/favicon.svg deleted file mode 100644 index 0ded7c138b6..00000000000 --- a/starters/apps/site-with-visual-cms/public/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/starters/apps/site-with-visual-cms/public/manifest.json b/starters/apps/site-with-visual-cms/public/manifest.json deleted file mode 100644 index c18e75f72a5..00000000000 --- a/starters/apps/site-with-visual-cms/public/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/web-manifest-combined.json", - "name": "qwik-project-name", - "short_name": "Welcome to Qwik", - "start_url": ".", - "display": "standalone", - "background_color": "#fff", - "description": "A Qwik project app." -} diff --git a/starters/apps/site-with-visual-cms/public/robots.txt b/starters/apps/site-with-visual-cms/public/robots.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/starters/apps/site-with-visual-cms/src/components/builder-registry.ts b/starters/apps/site-with-visual-cms/src/components/builder-registry.ts deleted file mode 100644 index d9f7abb5f36..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/builder-registry.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { RegisteredComponent } from "@builder.io/sdk-qwik"; -import Counter from "./counter/counter"; - -/** - * This array is used to integrate custom components within Builder. - * https://www.builder.io/c/docs/custom-components-intro - * - * These components will be found the "Custom Components" - * section of Builder's visual editor. - * You can also turn on "components only mode" to limit - * editing to only these components. - * https://www.builder.io/c/docs/guides/components-only-mode - */ -export const CUSTOM_COMPONENTS: RegisteredComponent[] = [ - { - component: Counter, - name: "Counter", - inputs: [ - { - name: "initialValue", - type: "number", - }, - ], - }, -]; diff --git a/starters/apps/site-with-visual-cms/src/components/counter/counter.module.css b/starters/apps/site-with-visual-cms/src/components/counter/counter.module.css deleted file mode 100644 index 99168f7ab2a..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/counter/counter.module.css +++ /dev/null @@ -1,23 +0,0 @@ -.wrapper { - display: flex; - align-items: center; - justify-content: center; - gap: 20px; -} - -.button { - background: var(--qwik-dirty-black); - border: none; - border-radius: 8px; - color: white; - cursor: pointer; - padding: 10px; - text-align: center; - font-weight: 700; - font-size: 2em; - min-width: 50px; -} - -.button:hover { - background: var(--qwik-dark-blue); -} diff --git a/starters/apps/site-with-visual-cms/src/components/counter/counter.tsx b/starters/apps/site-with-visual-cms/src/components/counter/counter.tsx deleted file mode 100644 index 35a326df519..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/counter/counter.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { component$, useSignal, $ } from "@builder.io/qwik"; -import styles from "./counter.module.css"; -import Gauge from "../gauge"; - -export default component$((props: { initialValue: number }) => { - const count = useSignal(props.initialValue || 99); - - const setCount = $((newValue: number) => { - if (newValue >= 0 && newValue <= 100) { - count.value = newValue; - - if (newValue === 100) { - celebrate(); - } - } - }); - - return ( -
- - - -
- ); -}); - -export const celebrate = $(async () => { - const defaults = { - spread: 360, - ticks: 70, - gravity: 0, - decay: 0.95, - startVelocity: 30, - colors: ["006ce9", "ac7ff4", "18b6f6", "713fc2", "ffffff"], - origin: { - x: 0.5, - y: 0.35, - }, - }; - - function loadConfetti() { - return new Promise<(opts: any) => void>((resolve, reject) => { - if ((globalThis as any).confetti) { - return resolve((globalThis as any).confetti as any); - } - const script = document.createElement("script"); - script.src = - "https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js"; - script.onload = () => resolve((globalThis as any).confetti as any); - script.onerror = reject; - document.head.appendChild(script); - script.remove(); - }); - } - - const confetti = await loadConfetti(); - - function shoot() { - confetti({ - ...defaults, - particleCount: 80, - scalar: 1.2, - }); - - confetti({ - ...defaults, - particleCount: 60, - scalar: 0.75, - }); - } - - setTimeout(shoot, 0); - setTimeout(shoot, 100); - setTimeout(shoot, 200); - setTimeout(shoot, 300); - setTimeout(shoot, 400); -}); diff --git a/starters/apps/site-with-visual-cms/src/components/footer/footer.module.css b/starters/apps/site-with-visual-cms/src/components/footer/footer.module.css deleted file mode 100644 index 1e788a762e5..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/footer/footer.module.css +++ /dev/null @@ -1,17 +0,0 @@ -.wrapper { - margin: 0 auto; - padding: 30px 40px; -} - -.anchor { - color: white; - display: block; - font-size: 0.8rem; - text-align: center; - text-decoration: none; - line-height: 1.5; -} - -.anchor:hover { - color: var(--qwik-light-blue); -} diff --git a/starters/apps/site-with-visual-cms/src/components/footer/footer.tsx b/starters/apps/site-with-visual-cms/src/components/footer/footer.tsx deleted file mode 100644 index d760fdef2e8..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/footer/footer.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { component$ } from "@builder.io/qwik"; -import styles from "./footer.module.css"; - -export default component$(() => { - return ( - - ); -}); diff --git a/starters/apps/site-with-visual-cms/src/components/gauge/gauge.module.css b/starters/apps/site-with-visual-cms/src/components/gauge/gauge.module.css deleted file mode 100644 index 5368dc8ab30..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/gauge/gauge.module.css +++ /dev/null @@ -1,22 +0,0 @@ -.wrapper { - position: relative; -} - -.gauge { - width: 100%; - min-width: 100px; -} - -.value { - position: absolute; - top: 50%; - left: 50%; - color: white; - font-size: 3rem; - transform: translate(-50%, -50%); - text-align: center; -} - -circle { - transition: all 500ms ease-in-out; -} diff --git a/starters/apps/site-with-visual-cms/src/components/gauge/index.tsx b/starters/apps/site-with-visual-cms/src/components/gauge/index.tsx deleted file mode 100644 index b010a66426e..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/gauge/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { component$ } from "@builder.io/qwik"; -import styles from "./gauge.module.css"; - -export default component$(({ value = 50 }: { value?: number }) => { - const safeValue = value < 0 || value > 100 ? 50 : value; - - return ( -
- - - - - - - - - - - {safeValue} -
- ); -}); diff --git a/starters/apps/site-with-visual-cms/src/components/header/header.module.css b/starters/apps/site-with-visual-cms/src/components/header/header.module.css deleted file mode 100644 index 280fca9a59d..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/header/header.module.css +++ /dev/null @@ -1,50 +0,0 @@ -.wrapper { - display: flex; - align-items: center; - justify-content: space-between; - margin: 0 auto; - padding: 30px 40px; -} - -.logo { - display: inline-block; -} -.logo a { - display: block; -} - -.header { - background-color: var(--header-background); -} - -.header ul { - margin: 0; - padding: 0; - list-style: none; - display: flex; - gap: 30px; -} - -.header li { - display: none; - margin: 0; - padding: 0; - font-size: 0.8rem; -} - -.header li a { - color: white; - display: inline-block; - padding: 0; - text-decoration: none; -} - -.header li a:hover { - color: var(--qwik-light-blue); -} - -@media (min-width: 450px) { - .header li { - display: inline-block; - } -} diff --git a/starters/apps/site-with-visual-cms/src/components/header/header.tsx b/starters/apps/site-with-visual-cms/src/components/header/header.tsx deleted file mode 100644 index df7ab23510e..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/header/header.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { component$ } from "@builder.io/qwik"; -import { QwikLogo } from "../icons/qwik"; -import styles from "./header.module.css"; - -export default component$(() => { - return ( -
- -
- ); -}); diff --git a/starters/apps/site-with-visual-cms/src/components/icons/qwik.tsx b/starters/apps/site-with-visual-cms/src/components/icons/qwik.tsx deleted file mode 100644 index 4f94b0883bb..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/icons/qwik.tsx +++ /dev/null @@ -1,44 +0,0 @@ -export const QwikLogo = ({ - width = 100, - height = 35, -}: { - width?: number; - height?: number; -}) => ( - - - - - - - - - -); diff --git a/starters/apps/site-with-visual-cms/src/components/router-head/router-head.tsx b/starters/apps/site-with-visual-cms/src/components/router-head/router-head.tsx deleted file mode 100644 index 127f8cae9f9..00000000000 --- a/starters/apps/site-with-visual-cms/src/components/router-head/router-head.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { component$ } from "@builder.io/qwik"; -import { useDocumentHead, useLocation } from "@builder.io/qwik-city"; - -/** - * The RouterHead component is placed inside of the document `` element. - */ -export const RouterHead = component$(() => { - const head = useDocumentHead(); - const loc = useLocation(); - - return ( - <> - {head.title} - - - - - - {head.meta.map((m) => ( - - ))} - - {head.links.map((l) => ( - - ))} - - {head.styles.map((s) => ( -