diff --git a/.changeset/all-cloths-hammer.md b/.changeset/all-cloths-hammer.md new file mode 100644 index 00000000000..4a2db064fba --- /dev/null +++ b/.changeset/all-cloths-hammer.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': minor +--- + +FEAT: the QRL segment mapping during Vite dev mode now happens in core and does not require providing a separate `symbolMapper` function any more. diff --git a/.changeset/green-drinks-strive.md b/.changeset/green-drinks-strive.md new file mode 100644 index 00000000000..b124d7097ea --- /dev/null +++ b/.changeset/green-drinks-strive.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/router': minor +--- + +FEAT: qwikRouter middleware no longer needs qwikRouterConfig, it handles it internally diff --git a/.changeset/large-houses-watch.md b/.changeset/large-houses-watch.md new file mode 100644 index 00000000000..3471c878d4a --- /dev/null +++ b/.changeset/large-houses-watch.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': patch +--- + +FIX: the `srcInput` option to `qwikVite` is deprecated because it's unused. diff --git a/.changeset/many-tips-win.md b/.changeset/many-tips-win.md new file mode 100644 index 00000000000..e52f53a2c14 --- /dev/null +++ b/.changeset/many-tips-win.md @@ -0,0 +1,6 @@ +--- +'@qwik.dev/core': minor +'@qwik.dev/router': patch +--- + +FEAT: Server output chunk files are now under their own build/ subdir, like the client build. This makes it easier to override the chunk filenames. This is possible because the Router metadata files are now an earlier part of the build process. diff --git a/.changeset/short-suits-bet.md b/.changeset/short-suits-bet.md new file mode 100644 index 00000000000..62d98fa4ec1 --- /dev/null +++ b/.changeset/short-suits-bet.md @@ -0,0 +1,5 @@ +--- +'@qwik.dev/core': minor +--- + +FIX: `qwikVite` has better vite config handling around input files, and no longer writes the q-manifest file to a temp dir. diff --git a/e2e/adapters-e2e/adapters/express/vite.config.ts b/e2e/adapters-e2e/adapters/express/vite.config.ts index 22435517fb0..5c0ac664b36 100644 --- a/e2e/adapters-e2e/adapters/express/vite.config.ts +++ b/e2e/adapters-e2e/adapters/express/vite.config.ts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ['src/entry.express.tsx', '@qwik-router-config'], + input: ['src/entry.express.tsx'], }, }, plugins: [nodeServerAdapter({ name: 'express' })], diff --git a/e2e/adapters-e2e/src/entry.express.tsx b/e2e/adapters-e2e/src/entry.express.tsx index b32a8abc2af..3cbf0571c3f 100644 --- a/e2e/adapters-e2e/src/entry.express.tsx +++ b/e2e/adapters-e2e/src/entry.express.tsx @@ -9,7 +9,6 @@ */ import { createQwikRouter } from '@qwik.dev/router/middleware/node'; import 'dotenv/config'; -import qwikRouterConfig from '@qwik-router-config'; import render from './entry.ssr'; import express from 'express'; import { fileURLToPath } from 'node:url'; @@ -25,7 +24,6 @@ const PORT = process.env.PORT ?? 3000; // Create the Qwik City Node middleware const { router, notFound } = createQwikRouter({ render, - qwikRouterConfig, // getOrigin(req) { // // If deploying under a proxy, you may need to build the origin from the request headers // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto diff --git a/e2e/adapters-e2e/src/entry.preview.tsx b/e2e/adapters-e2e/src/entry.preview.tsx index 24cd462afb0..35a2b15c5da 100644 --- a/e2e/adapters-e2e/src/entry.preview.tsx +++ b/e2e/adapters-e2e/src/entry.preview.tsx @@ -11,9 +11,8 @@ * */ import { createQwikRouter } from '@qwik.dev/router/middleware/node'; -import qwikRouterConfig from '@qwik-router-config'; // make sure qwikCityPlan is imported before entry import render from './entry.ssr'; /** The default export is the QwikCity adapter used by Vite preview. */ -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/e2e/adapters-e2e/tests/express.spec.ts b/e2e/adapters-e2e/tests/express.spec.ts index e1d58b23823..90dead2f81e 100644 --- a/e2e/adapters-e2e/tests/express.spec.ts +++ b/e2e/adapters-e2e/tests/express.spec.ts @@ -1,5 +1,12 @@ import { expect, test } from '@playwright/test'; +test.beforeEach(async ({ page }) => { + page.on('console', (msg) => { + // eslint-disable-next-line no-console + console.log(`[browser ${msg.type()}] ${msg.text()}`); + }); +}); + test.describe('Verifying Express Adapter', () => { test('should ignore unknown qdata', async ({ page, request }) => { page.goto('/'); diff --git a/packages/docs/adapters/cloudflare-pages/vite.config.ts b/packages/docs/adapters/cloudflare-pages/vite.config.ts index 137e1c24cf3..09178a42746 100644 --- a/packages/docs/adapters/cloudflare-pages/vite.config.ts +++ b/packages/docs/adapters/cloudflare-pages/vite.config.ts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ['src/entry.cloudflare-pages.tsx', '@qwik-router-config'], + input: ['src/entry.cloudflare-pages.tsx'], }, minify: false, }, @@ -18,7 +18,7 @@ export default extendConfig(baseConfig, () => { exclude: ['/demo/*', '/shop/*'], origin: (process.env.CF_PAGES_BRANCH !== 'main' ? process.env.CF_PAGES_URL : null) ?? - 'https://qwik.builder.io', + 'https://qwik.dev', }, }), ], diff --git a/packages/docs/public/_routes.json b/packages/docs/public/_routes.json index f93db4d2d78..6acffb3170a 100644 --- a/packages/docs/public/_routes.json +++ b/packages/docs/public/_routes.json @@ -1,23 +1,5 @@ { "version": 1, - "include": ["/*"], - "exclude": [ - "/chat", - "/examples", - "/guide", - "/tutorial", - "/tutorials", - "/tutorial/hooks/use-client-effect/", - "/docs/overview/", - "/docs/cheat/qwik-react/", - "/docs/cheat/best-practices/", - "/docs/cheat/serialization/", - "/docs/components/lifecycle/", - "/docs/components/projection/", - "/docs/components/resource/", - "/qwikcity/*", - "/integrations/*", - "/deployments/*", - "/repl/*" - ] + "include": ["/playground/*", "/tutorial/*", "/demo/*"], + "exclude": [] } diff --git a/packages/docs/src/entry.cloudflare-pages.tsx b/packages/docs/src/entry.cloudflare-pages.tsx index c9b4f145629..f5fa7250f77 100644 --- a/packages/docs/src/entry.cloudflare-pages.tsx +++ b/packages/docs/src/entry.cloudflare-pages.tsx @@ -1,7 +1,6 @@ -import qwikRouterConfig from '@qwik-router-config'; import { createQwikRouter } from '@qwik.dev/router/middleware/cloudflare-pages'; import render from './entry.ssr'; -const fetch = createQwikRouter({ render, qwikRouterConfig }); +const fetch = createQwikRouter({ render }); export { fetch }; diff --git a/packages/docs/src/entry.preview.tsx b/packages/docs/src/entry.preview.tsx index 01f08eb6761..3c175fcf955 100644 --- a/packages/docs/src/entry.preview.tsx +++ b/packages/docs/src/entry.preview.tsx @@ -1,6 +1,5 @@ -import qwikRouterConfig from '@qwik-router-config'; import { createQwikRouter } from '@qwik.dev/router/middleware/node'; import render from './entry.ssr'; /** The default export is the QwikRouter adapter used by Vite preview. */ -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/packages/docs/src/routes/api/index.tsx b/packages/docs/src/routes/api/index.tsx index a75842dc2fd..be0f5e1a765 100644 --- a/packages/docs/src/routes/api/index.tsx +++ b/packages/docs/src/routes/api/index.tsx @@ -10,13 +10,13 @@ import qwikRouterMiddlewareNetlifyEdgeApiData from './qwik-router-middleware-net import qwikRouterMiddlewareNodeApiData from './qwik-router-middleware-node/api.json'; import qwikRouterMiddlewareRequestHandlerApiData from './qwik-router-middleware-request-handler/api.json'; import qwikRouterMiddlewareVercelEdgeApiData from './qwik-router-middleware-vercel-edge/api.json'; -import qwikRouterStaticApiData from './qwik-router-static/api.json'; +import qwikRouterSsgApiData from './qwik-router-ssg/api.json'; import qwikRouterViteAzureSwaApiData from './qwik-router-vite-azure-swa/api.json'; import qwikRouterViteCloudRunApiData from './qwik-router-vite-cloud-run/api.json'; import qwikRouterViteCloudflarePagesApiData from './qwik-router-vite-cloudflare-pages/api.json'; import qwikRouterViteNetlifyEdgeApiData from './qwik-router-vite-netlify-edge/api.json'; import qwikRouterViteNodeServerApiData from './qwik-router-vite-node-server/api.json'; -import qwikRouterViteStaticApiData from './qwik-router-vite-static/api.json'; +import qwikRouterViteSsgApiData from './qwik-router-vite-ssg/api.json'; import qwikRouterViteVercelApiData from './qwik-router-vite-vercel/api.json'; import qwikRouterApiData from './qwik-router/api.json'; import qwikServerApiData from './qwik-server/api.json'; @@ -35,13 +35,13 @@ const apiData = { 'qwik-router-middleware-request-handler': qwikRouterMiddlewareRequestHandlerApiData, 'qwik-router-middleware-vercel-edge': qwikRouterMiddlewareVercelEdgeApiData, 'qwik-router-middleware-firebase': qwikRouterMiddlewareFirebaseApiData, - 'qwik-router-static': qwikRouterStaticApiData, + 'qwik-router-ssg': qwikRouterSsgApiData, 'qwik-router-vite-azure-swa': qwikRouterViteAzureSwaApiData, 'qwik-router-vite-cloud-run': qwikRouterViteCloudRunApiData, 'qwik-router-vite-cloudflare-pages': qwikRouterViteCloudflarePagesApiData, 'qwik-router-vite-node-server': qwikRouterViteNodeServerApiData, 'qwik-router-vite-netlify-edge': qwikRouterViteNetlifyEdgeApiData, - 'qwik-router-vite-static': qwikRouterViteStaticApiData, + 'qwik-router-vite-ssg': qwikRouterViteSsgApiData, 'qwik-router-vite-vercel': qwikRouterViteVercelApiData, 'qwik-optimizer': qwikOptimizerApiData, 'qwik-server': qwikServerApiData, @@ -126,7 +126,7 @@ export const ApiMemberWrapper = component$(({ id, data, filters }: any) => {

isCollapsed.value = !isCollapsed.value } + onClick$={() => isCollapsed.value = !isCollapsed.value } > {data.id}

diff --git a/packages/docs/src/routes/api/qwik-optimizer/api.json b/packages/docs/src/routes/api/qwik-optimizer/api.json index 14d8b4bd35b..76b42a1b712 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/api.json +++ b/packages/docs/src/routes/api/qwik-optimizer/api.json @@ -147,7 +147,7 @@ } ], "kind": "Enum", - "content": "Use `__EXPERIMENTAL__.x` to check if feature `x` is enabled. It will be replaced with `true` or `false` via an exact string replacement.\n\nAdd experimental features to this enum definition.\n\n\n```typescript\nexport declare enum ExperimentalFeatures \n```\n\n\n\n\n\n\n\n\n\n
\n\nMember\n\n\n\n\nValue\n\n\n\n\nDescription\n\n\n
\n\nenableRequestRewrite\n\n\n\n\n`\"enableRequestRewrite\"`\n\n\n\n\nEnable request.rewrite()\n\n\n
\n\ninsights\n\n\n\n\n`\"insights\"`\n\n\n\n\nEnable the ability to use the Qwik Insights vite plugin and component\n\n\n
\n\nnoSPA\n\n\n\n\n`\"noSPA\"`\n\n\n\n\nDisable SPA navigation handler in Qwik Router\n\n\n
\n\npreventNavigate\n\n\n\n\n`\"preventNavigate\"`\n\n\n\n\nEnable the usePreventNavigate hook\n\n\n
\n\nvalibot\n\n\n\n\n`\"valibot\"`\n\n\n\n\nEnable the Valibot form validation\n\n\n
\n\nwebWorker\n\n\n\n\n`\"webWorker\"`\n\n\n\n\nEnable worker$\n\n\n
", + "content": "Use `__EXPERIMENTAL__.x` to check if feature `x` is enabled. It will be replaced with `true` or `false` via an exact string replacement.\n\nAdd experimental features to this enum definition.\n\n\n```typescript\nexport declare enum ExperimentalFeatures \n```\n\n\n\n\n\n\n\n\n\n
\n\nMember\n\n\n\n\nValue\n\n\n\n\nDescription\n\n\n
\n\nenableRequestRewrite\n\n\n\n\n`\"enableRequestRewrite\"`\n\n\n\n\nEnable request.rewrite()\n\n\n
\n\ninsights\n\n\n\n\n`\"insights\"`\n\n\n\n\nEnable the ability to use the Qwik Insights vite plugin and `` component\n\n\n
\n\nnoSPA\n\n\n\n\n`\"noSPA\"`\n\n\n\n\nDisable SPA navigation handler in Qwik Router\n\n\n
\n\npreventNavigate\n\n\n\n\n`\"preventNavigate\"`\n\n\n\n\nEnable the usePreventNavigate hook\n\n\n
\n\nvalibot\n\n\n\n\n`\"valibot\"`\n\n\n\n\nEnable the Valibot form validation\n\n\n
\n\nwebWorker\n\n\n\n\n`\"webWorker\"`\n\n\n\n\nEnable worker$\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/plugins/plugin.ts", "mdFile": "core.experimentalfeatures.md" }, @@ -754,8 +754,8 @@ } ], "kind": "Variable", - "content": "> This API is provided as a beta preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nFor a given symbol (QRL such as `onKeydown$`) the server needs to know which bundle the symbol is in.\n\nNormally this is provided by Qwik's `q-manifest` . But `q-manifest` only exists after a full client build.\n\nThis would be a problem in dev mode. So in dev mode the symbol is mapped to the expected URL using the symbolMapper function below. For Vite the given path is fixed for a given symbol.\n\n\n```typescript\nsymbolMapper: ReturnType\n```", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts", + "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\n> Warning: This API is now obsolete.\n> \n> No longer needed, it is automatic now\n> \n\n\n```typescript\nsymbolMapper: undefined\n```", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/index.ts", "mdFile": "core.symbolmapper.md" }, { @@ -768,7 +768,7 @@ } ], "kind": "TypeAlias", - "content": "> This API is provided as a beta preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nFor a given symbol (QRL such as `onKeydown$`) the server needs to know which bundle the symbol is in.\n\nNormally this is provided by Qwik's `q-manifest` . But `q-manifest` only exists after a full client build.\n\nThis would be a problem in dev mode. So in dev mode the symbol is mapped to the expected URL using the symbolMapper function below. For Vite the given path is fixed for a given symbol.\n\n\n```typescript\nsymbolMapper: ReturnType\n```", + "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\n> Warning: This API is now obsolete.\n> \n> No longer needed, it is automatic now\n> \n\n\n```typescript\nsymbolMapper: undefined\n```", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/types.ts", "mdFile": "core.symbolmapper.md" }, diff --git a/packages/docs/src/routes/api/qwik-optimizer/index.mdx b/packages/docs/src/routes/api/qwik-optimizer/index.mdx index c0b781b77b7..74ba70dbf46 100644 --- a/packages/docs/src/routes/api/qwik-optimizer/index.mdx +++ b/packages/docs/src/routes/api/qwik-optimizer/index.mdx @@ -390,7 +390,7 @@ insights -Enable the ability to use the Qwik Insights vite plugin and component +Enable the ability to use the Qwik Insights vite plugin and `` component @@ -3090,32 +3090,28 @@ export type SourceMapsOption = "external" | "inline" | undefined | null; ## symbolMapper -> This API is provided as a beta preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> 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. -For a given symbol (QRL such as `onKeydown$`) the server needs to know which bundle the symbol is in. - -Normally this is provided by Qwik's `q-manifest` . But `q-manifest` only exists after a full client build. - -This would be a problem in dev mode. So in dev mode the symbol is mapped to the expected URL using the symbolMapper function below. For Vite the given path is fixed for a given symbol. +> Warning: This API is now obsolete. +> +> No longer needed, it is automatic now ```typescript -symbolMapper: ReturnType; +symbolMapper: undefined; ``` -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/index.ts) ## SymbolMapper -> This API is provided as a beta preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. - -For a given symbol (QRL such as `onKeydown$`) the server needs to know which bundle the symbol is in. - -Normally this is provided by Qwik's `q-manifest` . But `q-manifest` only exists after a full client build. +> 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. -This would be a problem in dev mode. So in dev mode the symbol is mapped to the expected URL using the symbolMapper function below. For Vite the given path is fixed for a given symbol. +> Warning: This API is now obsolete. +> +> No longer needed, it is automatic now ```typescript -symbolMapper: ReturnType; +symbolMapper: undefined; ``` [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/optimizer/src/types.ts) diff --git a/packages/docs/src/routes/api/qwik-router-ssg/api.json b/packages/docs/src/routes/api/qwik-router-ssg/api.json new file mode 100644 index 00000000000..9baf9834d6a --- /dev/null +++ b/packages/docs/src/routes/api/qwik-router-ssg/api.json @@ -0,0 +1,62 @@ +{ + "id": "qwik-router-ssg", + "package": "@qwik.dev/qwik-router/ssg", + "members": [ + { + "name": "generate", + "id": "generate", + "hierarchy": [ + { + "name": "generate", + "id": "generate" + } + ], + "kind": "Function", + "content": "Use this function when SSG should be generated from another module, such as a Vite plugin. This function's should be passed the paths of the entry module and Qwik Router Plan.\n\n\n```typescript\nexport declare function generate(opts: SsgOptions): Promise;\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[SsgOptions](#staticgenerateoptions)\n\n\n\n\n\n
\n**Returns:**\n\nPromise<[SsgResult](#staticgenerateresult)>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/index.ts", + "mdFile": "router.generate.md" + }, + { + "name": "SsgRenderOptions", + "id": "ssgrenderoptions", + "hierarchy": [ + { + "name": "SsgRenderOptions", + "id": "ssgrenderoptions" + } + ], + "kind": "Interface", + "content": "```typescript\nexport interface SsgRenderOptions extends RenderOptions \n```\n**Extends:** RenderOptions\n\n\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[emit404Pages?](./router.ssgrenderoptions.emit404pages.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the static build should not write custom or default `404.html` pages. Defaults to `true`.\n\n\n
\n\n[emitData?](./router.ssgrenderoptions.emitdata.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the generated `q-data.json` data files should not be written to disk. Defaults to `true`.\n\n\n
\n\n[emitHtml?](./router.ssgrenderoptions.emithtml.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the generated static HTML files should not be written to disk. Setting to `false` is useful if the SSG should only write the `q-data.json` files to disk. Defaults to `true`.\n\n\n
\n\n[exclude?](./router.ssgrenderoptions.exclude.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n_(Optional)_ Defines file system routes relative to the source `routes` directory that should not be static generated. Accepts wildcard behavior. This should not include the \"base\" pathname. `exclude` always takes priority over `include`.\n\n\n
\n\n[include?](./router.ssgrenderoptions.include.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n_(Optional)_ Defines file system routes relative to the source `routes` directory that should be static generated. Accepts wildcard behavior. This should not include the \"base\" pathname. If not provided, all routes will be static generated. `exclude` always takes priority over `include`.\n\n\n
\n\n[log?](./router.ssgrenderoptions.log.md)\n\n\n\n\n\n\n\n'debug'\n\n\n\n\n_(Optional)_ Log level.\n\n\n
\n\n[maxTasksPerWorker?](./router.ssgrenderoptions.maxtasksperworker.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n_(Optional)_ Maximum number of tasks to be running at one time per worker. Defaults to `20`.\n\n\n
\n\n[maxWorkers?](./router.ssgrenderoptions.maxworkers.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n_(Optional)_ Maximum number of workers to use while generating the static pages. Defaults to the number of CPUs available.\n\n\n
\n\n[origin](./router.ssgrenderoptions.origin.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nThe URL `origin`, which is a combination of the scheme (protocol) and hostname (domain). For example, `https://qwik.dev` has the protocol `https://` and domain `qwik.dev`. However, the `origin` does not include a `pathname`.\n\nThe `origin` is used to provide a full URL during Static Site Generation (SSG), and to simulate a complete URL rather than just the `pathname`. For example, in order to render a correct canonical tag URL or URLs within the `sitemap.xml`, the `origin` must be provided too.\n\nIf the site also starts with a pathname other than `/`, please use the `basePathname` option in the Qwik Router config options.\n\n\n
\n\n[outDir](./router.ssgrenderoptions.outdir.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nFile system directory where the static files should be written.\n\n\n
\n\n[sitemapOutFile?](./router.ssgrenderoptions.sitemapoutfile.md)\n\n\n\n\n\n\n\nstring \\| null\n\n\n\n\n_(Optional)_ File system path to write the `sitemap.xml` to. Defaults to `sitemap.xml` and written to the root of the `outDir`. Setting to `null` will prevent the sitemap from being created.\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts", + "mdFile": "router.ssgrenderoptions.md" + }, + { + "name": "StaticGenerateOptions", + "id": "staticgenerateoptions", + "hierarchy": [ + { + "name": "StaticGenerateOptions", + "id": "staticgenerateoptions" + } + ], + "kind": "Interface", + "content": "```typescript\nexport interface SsgOptions extends SsgRenderOptions \n```\n**Extends:** [SsgRenderOptions](#ssgrenderoptions)\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[basePathname?](./router.staticgenerateoptions.basepathname.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_ Defaults to `/`\n\n\n
\n\n[qwikCityPlanModulePath?](./router.staticgenerateoptions.qwikcityplanmodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_\n\n\n
\n\n[qwikRouterConfigModulePath](./router.staticgenerateoptions.qwikrouterconfigmodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nPath to the Qwik Router Config module exporting the default `@qwik-router-config`.\n\n\n
\n\n[renderModulePath](./router.staticgenerateoptions.rendermodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nPath to the SSR module exporting the default render function. In most cases it'll be `./src/entry.ssr.tsx`.\n\n\n
\n\n[rootDir?](./router.staticgenerateoptions.rootdir.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts", + "mdFile": "router.staticgenerateoptions.md" + }, + { + "name": "StaticGenerateResult", + "id": "staticgenerateresult", + "hierarchy": [ + { + "name": "StaticGenerateResult", + "id": "staticgenerateresult" + } + ], + "kind": "Interface", + "content": "```typescript\nexport interface SsgResult \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[duration](./router.staticgenerateresult.duration.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[errors](./router.staticgenerateresult.errors.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[rendered](./router.staticgenerateresult.rendered.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[staticPaths](./router.staticgenerateresult.staticpaths.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n\n
", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts", + "mdFile": "router.staticgenerateresult.md" + } + ] +} \ No newline at end of file diff --git a/packages/docs/src/routes/api/qwik-router-static/index.mdx b/packages/docs/src/routes/api/qwik-router-ssg/index.mdx similarity index 78% rename from packages/docs/src/routes/api/qwik-router-static/index.mdx rename to packages/docs/src/routes/api/qwik-router-ssg/index.mdx index a6b743667ea..646e005aa65 100644 --- a/packages/docs/src/routes/api/qwik-router-static/index.mdx +++ b/packages/docs/src/routes/api/qwik-router-ssg/index.mdx @@ -1,17 +1,15 @@ --- -title: \@qwik.dev/qwik-router/static API Reference +title: \@qwik.dev/qwik-router/ssg API Reference --- -# [API](/api) › @qwik.dev/qwik-router/static +# [API](/api) › @qwik.dev/qwik-router/ssg ## generate Use this function when SSG should be generated from another module, such as a Vite plugin. This function's should be passed the paths of the entry module and Qwik Router Plan. ```typescript -export declare function generate( - opts: StaticGenerateOptions, -): Promise; +export declare function generate(opts: SsgOptions): Promise; ```
@@ -33,7 +31,7 @@ opts -[StaticGenerateOptions](#staticgenerateoptions) +[SsgOptions](#staticgenerateoptions) @@ -41,17 +39,17 @@ opts
**Returns:** -Promise<[StaticGenerateResult](#staticgenerateresult)> +Promise<[SsgResult](#staticgenerateresult)> -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/index.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/index.ts) -## StaticGenerateOptions +## SsgRenderOptions ```typescript -export interface StaticGenerateOptions extends StaticGenerateRenderOptions +export interface SsgRenderOptions extends RenderOptions ``` -**Extends:** [StaticGenerateRenderOptions](#staticgeneraterenderoptions) +**Extends:** RenderOptions -
@@ -72,231 +70,235 @@ Description
-[basePathname?](./router.staticgenerateoptions.basepathname.md) +[emit404Pages?](./router.ssgrenderoptions.emit404pages.md) -string +boolean -_(Optional)_ Defaults to `/` +_(Optional)_ Set to `false` if the static build should not write custom or default `404.html` pages. Defaults to `true`.
-[qwikCityPlanModulePath?](./router.staticgenerateoptions.qwikcityplanmodulepath.md) +[emitData?](./router.ssgrenderoptions.emitdata.md) -string +boolean -_(Optional)_ +_(Optional)_ Set to `false` if the generated `q-data.json` data files should not be written to disk. Defaults to `true`.
-[qwikRouterConfigModulePath](./router.staticgenerateoptions.qwikrouterconfigmodulepath.md) +[emitHtml?](./router.ssgrenderoptions.emithtml.md) -string +boolean -Path to the Qwik Router Config module exporting the default `@qwik-router-config`. +_(Optional)_ Set to `false` if the generated static HTML files should not be written to disk. Setting to `false` is useful if the SSG should only write the `q-data.json` files to disk. Defaults to `true`.
-[renderModulePath](./router.staticgenerateoptions.rendermodulepath.md) +[exclude?](./router.ssgrenderoptions.exclude.md) -string +string[] -Path to the SSR module exporting the default render function. In most cases it'll be `./src/entry.ssr.tsx`. +_(Optional)_ Defines file system routes relative to the source `routes` directory that should not be static generated. Accepts wildcard behavior. This should not include the "base" pathname. `exclude` always takes priority over `include`.
-[rootDir?](./router.staticgenerateoptions.rootdir.md) +[include?](./router.ssgrenderoptions.include.md) -string +string[] -_(Optional)_ +_(Optional)_ Defines file system routes relative to the source `routes` directory that should be static generated. Accepts wildcard behavior. This should not include the "base" pathname. If not provided, all routes will be static generated. `exclude` always takes priority over `include`.
- -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts) - -## StaticGenerateRenderOptions - -```typescript -export interface StaticGenerateRenderOptions extends RenderOptions -``` - -**Extends:** RenderOptions - - - + -
- -Property +
- +[log?](./router.ssgrenderoptions.log.md) -Modifiers + - + -Type +'debug' - + -Description +_(Optional)_ Log level. -
+
-[emit404Pages?](./router.staticgeneraterenderoptions.emit404pages.md) +[maxTasksPerWorker?](./router.ssgrenderoptions.maxtasksperworker.md) -boolean +number -_(Optional)_ Set to `false` if the static build should not write custom or default `404.html` pages. Defaults to `true`. +_(Optional)_ Maximum number of tasks to be running at one time per worker. Defaults to `20`.
-[emitData?](./router.staticgeneraterenderoptions.emitdata.md) +[maxWorkers?](./router.ssgrenderoptions.maxworkers.md) -boolean +number -_(Optional)_ Set to `false` if the generated `q-data.json` data files should not be written to disk. Defaults to `true`. +_(Optional)_ Maximum number of workers to use while generating the static pages. Defaults to the number of CPUs available.
-[emitHtml?](./router.staticgeneraterenderoptions.emithtml.md) +[origin](./router.ssgrenderoptions.origin.md) -boolean +string -_(Optional)_ Set to `false` if the generated static HTML files should not be written to disk. Setting to `false` is useful if the SSG should only write the `q-data.json` files to disk. Defaults to `true`. +The URL `origin`, which is a combination of the scheme (protocol) and hostname (domain). For example, `https://qwik.dev` has the protocol `https://` and domain `qwik.dev`. However, the `origin` does not include a `pathname`. + +The `origin` is used to provide a full URL during Static Site Generation (SSG), and to simulate a complete URL rather than just the `pathname`. For example, in order to render a correct canonical tag URL or URLs within the `sitemap.xml`, the `origin` must be provided too. + +If the site also starts with a pathname other than `/`, please use the `basePathname` option in the Qwik Router config options.
-[exclude?](./router.staticgeneraterenderoptions.exclude.md) +[outDir](./router.ssgrenderoptions.outdir.md) -string[] +string -_(Optional)_ Defines file system routes relative to the source `routes` directory that should not be static generated. Accepts wildcard behavior. This should not include the "base" pathname. `exclude` always takes priority over `include`. +File system directory where the static files should be written.
-[include?](./router.staticgeneraterenderoptions.include.md) +[sitemapOutFile?](./router.ssgrenderoptions.sitemapoutfile.md) -string[] +string \| null -_(Optional)_ Defines file system routes relative to the source `routes` directory that should be static generated. Accepts wildcard behavior. This should not include the "base" pathname. If not provided, all routes will be static generated. `exclude` always takes priority over `include`. +_(Optional)_ File system path to write the `sitemap.xml` to. Defaults to `sitemap.xml` and written to the root of the `outDir`. Setting to `null` will prevent the sitemap from being created.
+
-[log?](./router.staticgeneraterenderoptions.log.md) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts) - +## StaticGenerateOptions - +```typescript +export interface SsgOptions extends SsgRenderOptions +``` -'debug' +**Extends:** [SsgRenderOptions](#ssgrenderoptions) - + - +
-_(Optional)_ Log level. +Property -
+ + +Modifiers + + + +Type + + + +Description -[maxTasksPerWorker?](./router.staticgeneraterenderoptions.maxtasksperworker.md) +
+ +[basePathname?](./router.staticgenerateoptions.basepathname.md) -number +string -_(Optional)_ Maximum number of tasks to be running at one time per worker. Defaults to `20`. +_(Optional)_ Defaults to `/`
-[maxWorkers?](./router.staticgeneraterenderoptions.maxworkers.md) +[qwikCityPlanModulePath?](./router.staticgenerateoptions.qwikcityplanmodulepath.md) -number +string -_(Optional)_ Maximum number of workers to use while generating the static pages. Defaults to the number of CPUs available. +_(Optional)_
-[origin](./router.staticgeneraterenderoptions.origin.md) +[qwikRouterConfigModulePath](./router.staticgenerateoptions.qwikrouterconfigmodulepath.md) @@ -306,16 +308,12 @@ string -The URL `origin`, which is a combination of the scheme (protocol) and hostname (domain). For example, `https://qwik.dev` has the protocol `https://` and domain `qwik.dev`. However, the `origin` does not include a `pathname`. - -The `origin` is used to provide a full URL during Static Site Generation (SSG), and to simulate a complete URL rather than just the `pathname`. For example, in order to render a correct canonical tag URL or URLs within the `sitemap.xml`, the `origin` must be provided too. - -If the site also starts with a pathname other than `/`, please use the `basePathname` option in the Qwik Router config options. +Path to the Qwik Router Config module exporting the default `@qwik-router-config`.
-[outDir](./router.staticgeneraterenderoptions.outdir.md) +[renderModulePath](./router.staticgenerateoptions.rendermodulepath.md) @@ -325,32 +323,32 @@ string -File system directory where the static files should be written. +Path to the SSR module exporting the default render function. In most cases it'll be `./src/entry.ssr.tsx`.
-[sitemapOutFile?](./router.staticgeneraterenderoptions.sitemapoutfile.md) +[rootDir?](./router.staticgenerateoptions.rootdir.md) -string \| null +string -_(Optional)_ File system path to write the `sitemap.xml` to. Defaults to `sitemap.xml` and written to the root of the `outDir`. Setting to `null` will prevent the sitemap from being created. +_(Optional)_
-[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts) ## StaticGenerateResult ```typescript -export interface StaticGenerateResult +export interface SsgResult ```
@@ -424,4 +422,4 @@ string[]
-[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts) +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/ssg/types.ts) diff --git a/packages/docs/src/routes/api/qwik-router-static/api.json b/packages/docs/src/routes/api/qwik-router-static/api.json deleted file mode 100644 index d337e378ae1..00000000000 --- a/packages/docs/src/routes/api/qwik-router-static/api.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "id": "qwik-router-static", - "package": "@qwik.dev/qwik-router/static", - "members": [ - { - "name": "generate", - "id": "generate", - "hierarchy": [ - { - "name": "generate", - "id": "generate" - } - ], - "kind": "Function", - "content": "Use this function when SSG should be generated from another module, such as a Vite plugin. This function's should be passed the paths of the entry module and Qwik Router Plan.\n\n\n```typescript\nexport declare function generate(opts: StaticGenerateOptions): Promise;\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[StaticGenerateOptions](#staticgenerateoptions)\n\n\n\n\n\n
\n**Returns:**\n\nPromise<[StaticGenerateResult](#staticgenerateresult)>", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/index.ts", - "mdFile": "router.generate.md" - }, - { - "name": "StaticGenerateOptions", - "id": "staticgenerateoptions", - "hierarchy": [ - { - "name": "StaticGenerateOptions", - "id": "staticgenerateoptions" - } - ], - "kind": "Interface", - "content": "```typescript\nexport interface StaticGenerateOptions extends StaticGenerateRenderOptions \n```\n**Extends:** [StaticGenerateRenderOptions](#staticgeneraterenderoptions)\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[basePathname?](./router.staticgenerateoptions.basepathname.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_ Defaults to `/`\n\n\n
\n\n[qwikCityPlanModulePath?](./router.staticgenerateoptions.qwikcityplanmodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_\n\n\n
\n\n[qwikRouterConfigModulePath](./router.staticgenerateoptions.qwikrouterconfigmodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nPath to the Qwik Router Config module exporting the default `@qwik-router-config`.\n\n\n
\n\n[renderModulePath](./router.staticgenerateoptions.rendermodulepath.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nPath to the SSR module exporting the default render function. In most cases it'll be `./src/entry.ssr.tsx`.\n\n\n
\n\n[rootDir?](./router.staticgenerateoptions.rootdir.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\n_(Optional)_\n\n\n
", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts", - "mdFile": "router.staticgenerateoptions.md" - }, - { - "name": "StaticGenerateRenderOptions", - "id": "staticgeneraterenderoptions", - "hierarchy": [ - { - "name": "StaticGenerateRenderOptions", - "id": "staticgeneraterenderoptions" - } - ], - "kind": "Interface", - "content": "```typescript\nexport interface StaticGenerateRenderOptions extends RenderOptions \n```\n**Extends:** RenderOptions\n\n\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[emit404Pages?](./router.staticgeneraterenderoptions.emit404pages.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the static build should not write custom or default `404.html` pages. Defaults to `true`.\n\n\n
\n\n[emitData?](./router.staticgeneraterenderoptions.emitdata.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the generated `q-data.json` data files should not be written to disk. Defaults to `true`.\n\n\n
\n\n[emitHtml?](./router.staticgeneraterenderoptions.emithtml.md)\n\n\n\n\n\n\n\nboolean\n\n\n\n\n_(Optional)_ Set to `false` if the generated static HTML files should not be written to disk. Setting to `false` is useful if the SSG should only write the `q-data.json` files to disk. Defaults to `true`.\n\n\n
\n\n[exclude?](./router.staticgeneraterenderoptions.exclude.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n_(Optional)_ Defines file system routes relative to the source `routes` directory that should not be static generated. Accepts wildcard behavior. This should not include the \"base\" pathname. `exclude` always takes priority over `include`.\n\n\n
\n\n[include?](./router.staticgeneraterenderoptions.include.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n_(Optional)_ Defines file system routes relative to the source `routes` directory that should be static generated. Accepts wildcard behavior. This should not include the \"base\" pathname. If not provided, all routes will be static generated. `exclude` always takes priority over `include`.\n\n\n
\n\n[log?](./router.staticgeneraterenderoptions.log.md)\n\n\n\n\n\n\n\n'debug'\n\n\n\n\n_(Optional)_ Log level.\n\n\n
\n\n[maxTasksPerWorker?](./router.staticgeneraterenderoptions.maxtasksperworker.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n_(Optional)_ Maximum number of tasks to be running at one time per worker. Defaults to `20`.\n\n\n
\n\n[maxWorkers?](./router.staticgeneraterenderoptions.maxworkers.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n_(Optional)_ Maximum number of workers to use while generating the static pages. Defaults to the number of CPUs available.\n\n\n
\n\n[origin](./router.staticgeneraterenderoptions.origin.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nThe URL `origin`, which is a combination of the scheme (protocol) and hostname (domain). For example, `https://qwik.dev` has the protocol `https://` and domain `qwik.dev`. However, the `origin` does not include a `pathname`.\n\nThe `origin` is used to provide a full URL during Static Site Generation (SSG), and to simulate a complete URL rather than just the `pathname`. For example, in order to render a correct canonical tag URL or URLs within the `sitemap.xml`, the `origin` must be provided too.\n\nIf the site also starts with a pathname other than `/`, please use the `basePathname` option in the Qwik Router config options.\n\n\n
\n\n[outDir](./router.staticgeneraterenderoptions.outdir.md)\n\n\n\n\n\n\n\nstring\n\n\n\n\nFile system directory where the static files should be written.\n\n\n
\n\n[sitemapOutFile?](./router.staticgeneraterenderoptions.sitemapoutfile.md)\n\n\n\n\n\n\n\nstring \\| null\n\n\n\n\n_(Optional)_ File system path to write the `sitemap.xml` to. Defaults to `sitemap.xml` and written to the root of the `outDir`. Setting to `null` will prevent the sitemap from being created.\n\n\n
", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts", - "mdFile": "router.staticgeneraterenderoptions.md" - }, - { - "name": "StaticGenerateResult", - "id": "staticgenerateresult", - "hierarchy": [ - { - "name": "StaticGenerateResult", - "id": "staticgenerateresult" - } - ], - "kind": "Interface", - "content": "```typescript\nexport interface StaticGenerateResult \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[duration](./router.staticgenerateresult.duration.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[errors](./router.staticgenerateresult.errors.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[rendered](./router.staticgenerateresult.rendered.md)\n\n\n\n\n\n\n\nnumber\n\n\n\n\n\n
\n\n[staticPaths](./router.staticgenerateresult.staticpaths.md)\n\n\n\n\n\n\n\nstring\\[\\]\n\n\n\n\n\n
", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/static/types.ts", - "mdFile": "router.staticgenerateresult.md" - } - ] -} \ No newline at end of file diff --git a/packages/docs/src/routes/api/qwik-router-vite-ssg/api.json b/packages/docs/src/routes/api/qwik-router-vite-ssg/api.json new file mode 100644 index 00000000000..ef150e5a758 --- /dev/null +++ b/packages/docs/src/routes/api/qwik-router-vite-ssg/api.json @@ -0,0 +1,48 @@ +{ + "id": "qwik-router-vite-ssg", + "package": "@qwik.dev/qwik-router/vite/ssg", + "members": [ + { + "name": "ssgAdapter", + "id": "ssgadapter", + "hierarchy": [ + { + "name": "ssgAdapter", + "id": "ssgadapter" + } + ], + "kind": "Function", + "content": "```typescript\nexport declare function ssgAdapter(opts: SsgAdapterOptions): any;\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[SsgAdapterOptions](#ssgadapteroptions)\n\n\n\n\n\n
\n**Returns:**\n\nany", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts", + "mdFile": "router.ssgadapter.md" + }, + { + "name": "SsgAdapterOptions", + "id": "ssgadapteroptions", + "hierarchy": [ + { + "name": "SsgAdapterOptions", + "id": "ssgadapteroptions" + } + ], + "kind": "Interface", + "content": "```typescript\nexport interface SsgAdapterOptions extends Omit \n```\n**Extends:** Omit<SsgRenderOptions, 'outDir'>", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts", + "mdFile": "router.ssgadapteroptions.md" + }, + { + "name": "staticAdapter", + "id": "staticadapter", + "hierarchy": [ + { + "name": "staticAdapter", + "id": "staticadapter" + } + ], + "kind": "Variable", + "content": "> Warning: This API is now obsolete.\n> \n> Use `ssgAdapter` instead.\n> \n\n\n```typescript\nstaticAdapter: typeof ssgAdapter\n```", + "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts", + "mdFile": "router.staticadapter.md" + } + ] +} \ No newline at end of file diff --git a/packages/docs/src/routes/api/qwik-router-vite-ssg/index.mdx b/packages/docs/src/routes/api/qwik-router-vite-ssg/index.mdx new file mode 100644 index 00000000000..1a1ea5463a5 --- /dev/null +++ b/packages/docs/src/routes/api/qwik-router-vite-ssg/index.mdx @@ -0,0 +1,64 @@ +--- +title: \@qwik.dev/qwik-router/vite/ssg API Reference +--- + +# [API](/api) › @qwik.dev/qwik-router/vite/ssg + +## ssgAdapter + +```typescript +export declare function ssgAdapter(opts: SsgAdapterOptions): any; +``` + + + +
+ +Parameter + + + +Type + + + +Description + +
+ +opts + + + +[SsgAdapterOptions](#ssgadapteroptions) + + + +
+**Returns:** + +any + +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts) + +## SsgAdapterOptions + +```typescript +export interface SsgAdapterOptions extends Omit +``` + +**Extends:** Omit<SsgRenderOptions, 'outDir'> + +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts) + +## staticAdapter + +> Warning: This API is now obsolete. +> +> Use `ssgAdapter` instead. + +```typescript +staticAdapter: typeof ssgAdapter; +``` + +[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/ssg/vite/index.ts) diff --git a/packages/docs/src/routes/api/qwik-router-vite-static/api.json b/packages/docs/src/routes/api/qwik-router-vite-static/api.json deleted file mode 100644 index 33204188132..00000000000 --- a/packages/docs/src/routes/api/qwik-router-vite-static/api.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id": "qwik-router-vite-static", - "package": "@qwik.dev/qwik-router/vite/static", - "members": [ - { - "name": "staticAdapter", - "id": "staticadapter", - "hierarchy": [ - { - "name": "staticAdapter", - "id": "staticadapter" - } - ], - "kind": "Function", - "content": "```typescript\nexport declare function staticAdapter(opts: StaticGenerateAdapterOptions): any;\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[StaticGenerateAdapterOptions](#staticgenerateadapteroptions)\n\n\n\n\n\n
\n**Returns:**\n\nany", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/static/vite/index.ts", - "mdFile": "router.staticadapter.md" - }, - { - "name": "StaticGenerateAdapterOptions", - "id": "staticgenerateadapteroptions", - "hierarchy": [ - { - "name": "StaticGenerateAdapterOptions", - "id": "staticgenerateadapteroptions" - } - ], - "kind": "Interface", - "content": "```typescript\nexport interface StaticGenerateAdapterOptions extends Omit \n```\n**Extends:** Omit<StaticGenerateRenderOptions, 'outDir'>", - "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/static/vite/index.ts", - "mdFile": "router.staticgenerateadapteroptions.md" - } - ] -} \ No newline at end of file diff --git a/packages/docs/src/routes/api/qwik-router-vite-static/index.mdx b/packages/docs/src/routes/api/qwik-router-vite-static/index.mdx deleted file mode 100644 index 8b54b776f24..00000000000 --- a/packages/docs/src/routes/api/qwik-router-vite-static/index.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: \@qwik.dev/qwik-router/vite/static API Reference ---- - -# [API](/api) › @qwik.dev/qwik-router/vite/static - -## staticAdapter - -```typescript -export declare function staticAdapter(opts: StaticGenerateAdapterOptions): any; -``` - - - -
- -Parameter - - - -Type - - - -Description - -
- -opts - - - -[StaticGenerateAdapterOptions](#staticgenerateadapteroptions) - - - -
-**Returns:** - -any - -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/static/vite/index.ts) - -## StaticGenerateAdapterOptions - -```typescript -export interface StaticGenerateAdapterOptions extends Omit -``` - -**Extends:** Omit<StaticGenerateRenderOptions, 'outDir'> - -[Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik-router/src/adapters/static/vite/index.ts) diff --git a/packages/docs/src/routes/docs/(qwikrouter)/guides/static-site-generation/index.mdx b/packages/docs/src/routes/docs/(qwikrouter)/guides/static-site-generation/index.mdx index 7ff3e31a06f..ffa985ae199 100644 --- a/packages/docs/src/routes/docs/(qwikrouter)/guides/static-site-generation/index.mdx +++ b/packages/docs/src/routes/docs/(qwikrouter)/guides/static-site-generation/index.mdx @@ -72,7 +72,7 @@ Select `Adapter: Static site (.html files)`. Done! Running the above command will make the following changes to your project: - A `build.server` script will be automatically added to your `package.json` file. -- A `adapters/static/vite.config.mts` file will be created. +- A `adapters/ssg/vite.config.mts` file will be created. Your build files will be generated into the `dist` folder. @@ -103,7 +103,7 @@ bun run build.server ### SSG Config -The `adapters/static/vite.config.mts` file also includes the SSG config, which would be custom for each implementation. +The `adapters/ssg/vite.config.mts` file also includes the SSG config, which would be custom for each implementation. #### `origin` @@ -119,7 +119,7 @@ The `outDir` is a file system output directory where the static files should be ### Javascript Runtimes -For a Javascript project, it's quite common for the build's runtime to be built on top of [Node.js](https://nodejs.org/en/docs/). However, the core of Qwik Router static site generation isn't tied to using only Node.js, which is why the `qwikRouterGenerate()` function is imported from `@qwik.dev/router/static/node`. By scoping the generate function to a specific runtime, such as Node.js, this gives Qwik Router the flexibility to also generate SSG from other runtimes in the future, such as [Deno](https://deno.land/) or [Bun](https://bun.sh/). +For a Javascript project, it's quite common for the build's runtime to be built on top of [Node.js](https://nodejs.org/en/docs/). However, the core of Qwik Router static site generation isn't tied to using only Node.js, which is why the `qwikRouterGenerate()` function is imported from `@qwik.dev/router/ssg/node`. By scoping the generate function to a specific runtime, such as Node.js, this gives Qwik Router the flexibility to also generate SSG from other runtimes in the future, such as [Deno](https://deno.land/) or [Bun](https://bun.sh/). ## Dynamic SSG Routes diff --git a/packages/docs/src/routes/docs/deployments/cloudflare-pages/index.mdx b/packages/docs/src/routes/docs/deployments/cloudflare-pages/index.mdx index 9988165ac67..4f60e4e3ccc 100644 --- a/packages/docs/src/routes/docs/deployments/cloudflare-pages/index.mdx +++ b/packages/docs/src/routes/docs/deployments/cloudflare-pages/index.mdx @@ -152,7 +152,11 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ['src/entry.cloudflare-pages.tsx', '@qwik-router-config'], + input: [ + 'src/entry.cloudflare-pages.tsx', + // This ensures that the SSG can read the routes from the config + '@qwik-router-config', + ], } }, plugins: [ @@ -204,10 +208,9 @@ import { createQwikRouter, type PlatformCloudflarePages, } from '@qwik.dev/router/middleware/cloudflare-pages'; -import qwikRouterConfig from '@qwik-router-config'; import render from './entry.ssr'; -const fetch = createQwikRouter({ render, qwikRouterConfig }); +const fetch = createQwikRouter({ render }); export { fetch }; ``` diff --git a/packages/docs/src/routes/docs/deployments/netlify-edge/index.mdx b/packages/docs/src/routes/docs/deployments/netlify-edge/index.mdx index bc62d20c2ed..6a3c86a288c 100644 --- a/packages/docs/src/routes/docs/deployments/netlify-edge/index.mdx +++ b/packages/docs/src/routes/docs/deployments/netlify-edge/index.mdx @@ -130,10 +130,9 @@ When the `netlify-edge` adapter is added, a new entry file will be created at `s ```tsx title="src/entry.netlify-edge.tsx" import { createQwikRouter } from '@qwik.dev/router/middleware/netlify-edge'; -import qwikRouterConfig from '@qwik-router-config'; import render from './entry.ssr'; -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); ``` The compiled middleware will be built in the `.netlify/edge-functions` directory. diff --git a/packages/docs/src/routes/docs/deployments/node/index.mdx b/packages/docs/src/routes/docs/deployments/node/index.mdx index 90d8fb42dad..af89b389e8d 100644 --- a/packages/docs/src/routes/docs/deployments/node/index.mdx +++ b/packages/docs/src/routes/docs/deployments/node/index.mdx @@ -164,8 +164,6 @@ If you want to disable CSRF protection, you can set `checkOrigin: false` in the // ... const { router, notFound, staticFile } = createQwikRouter({ render, - qwikRouterConfig, - manifest, checkOrigin: false, }); // ... diff --git a/packages/docs/src/routes/docs/deployments/static/index.mdx b/packages/docs/src/routes/docs/deployments/static/index.mdx index f62e6bc25d9..c268d0285c1 100644 --- a/packages/docs/src/routes/docs/deployments/static/index.mdx +++ b/packages/docs/src/routes/docs/deployments/static/index.mdx @@ -15,36 +15,36 @@ Qwik's Static Site adapter helps to generate static html files which can be easi ## Installation -To integrate the `static-site` adapter, use the `add` command: +To integrate the `ssg` adapter, use the `add` command: ```shell -pnpm run qwik add static +pnpm run qwik add ssg ``` ```shell -npm run qwik add static +npm run qwik add ssg ``` ```shell -yarn run qwik add static +yarn run qwik add ssg ``` ```shell -bun run qwik add static +bun run qwik add ssg ``` -Above command will create a directory at project root named `adapters/static/vite.config.mts` with the below code. +Above command will create a directory at project root named `adapters/ssg/vite.config.mts` with the below code. -```ts title="adapters/static/vite.config.mts" -import { staticAdapter } from "@qwik.dev/router/adapters/static/vite"; +```ts title="adapters/ssg/vite.config.mts" +import { ssgAdapter } from "@qwik.dev/router/adapters/ssg/vite"; import { extendConfig } from '@qwik.dev/router'; import baseConfig from '../../vite.config.mts'; @@ -57,7 +57,7 @@ export default extendConfig(baseConfig, () => { }, }, plugins: [ - staticAdapter({ + ssgAdapter({ origin: 'https://yoursite.qwik.dev', }), ], diff --git a/packages/docs/src/routes/docs/deployments/vercel-edge/index.mdx b/packages/docs/src/routes/docs/deployments/vercel-edge/index.mdx index cd83b6b23cd..aa4f253f660 100644 --- a/packages/docs/src/routes/docs/deployments/vercel-edge/index.mdx +++ b/packages/docs/src/routes/docs/deployments/vercel-edge/index.mdx @@ -136,10 +136,9 @@ When the `vercel-edge` adapter is added, a new entry file will be created at `sr ```tsx title="src/entry.vercel-edge.tsx" import { createQwikRouter } from '@qwik.dev/router/middleware/vercel-edge'; -import qwikRouterConfig from '@qwik-router-config'; import render from './entry.ssr'; -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); ``` The compiled middleware will be built in the `.vercel/output` directory. diff --git a/packages/docs/src/routes/docs/integrations/react/index.mdx b/packages/docs/src/routes/docs/integrations/react/index.mdx index 771cdb95806..50f7e2bd993 100644 --- a/packages/docs/src/routes/docs/integrations/react/index.mdx +++ b/packages/docs/src/routes/docs/integrations/react/index.mdx @@ -909,6 +909,4 @@ The host element is not part of React, meaning that hydration is not necessary t This will effectively allow you to respond to a click in a [MUI button](https://mui.com/material-ui/react-button/) without downloading a single byte of React code. -🧑‍💻Happy hacking!import PackageManagerTabs from '~/components/package-manager-tabs/index.tsx'; -import CodeSandbox, { CodeFile } from '../../../../components/code-sandbox/index.tsx'; - +🧑‍💻Happy hacking! diff --git a/packages/insights/adapters/netlify-edge/vite.config.ts b/packages/insights/adapters/netlify-edge/vite.config.ts index 8275b44631c..42f7ec4061a 100644 --- a/packages/insights/adapters/netlify-edge/vite.config.ts +++ b/packages/insights/adapters/netlify-edge/vite.config.ts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ['src/entry.netlify-edge.tsx', '@qwik-router-config'], + input: ['src/entry.netlify-edge.tsx'], }, outDir: '.netlify/edge-functions/entry.netlify-edge', }, diff --git a/packages/insights/src/entry.netlify-edge.tsx b/packages/insights/src/entry.netlify-edge.tsx index 5ae3c0bd069..f09fe49a1cc 100644 --- a/packages/insights/src/entry.netlify-edge.tsx +++ b/packages/insights/src/entry.netlify-edge.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/netlify-edge/ * */ -import qwikRouterConfig from '@qwik-router-config'; import { createQwikRouter, type PlatformNetlify } from '@qwik.dev/router/middleware/netlify-edge'; import render from './entry.ssr'; @@ -17,7 +16,6 @@ declare global { export default createQwikRouter({ render, - qwikRouterConfig, // disable CSRF protection because we get called from everywhere checkOrigin: false, }); diff --git a/packages/insights/src/entry.preview.tsx b/packages/insights/src/entry.preview.tsx index 2965805f8de..b260af58fd0 100644 --- a/packages/insights/src/entry.preview.tsx +++ b/packages/insights/src/entry.preview.tsx @@ -10,9 +10,8 @@ * - https://vitejs.dev/config/preview-options.html#preview-options * */ -import qwikRouterConfig from '@qwik-router-config'; import { createQwikRouter } from '@qwik.dev/router/middleware/node'; import render from './entry.ssr'; /** The default export is the QwikRouter adapter used by Vite preview. */ -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/packages/qwik-router/adapters/static/vite.d.ts b/packages/qwik-router/adapters/static/vite.d.ts index c62d0cab821..483e314fd23 100644 --- a/packages/qwik-router/adapters/static/vite.d.ts +++ b/packages/qwik-router/adapters/static/vite.d.ts @@ -1,2 +1,2 @@ // re-export for typescript in old resolution mode -export * from '../../lib/adapters/static/vite'; +export * from '../../lib/adapters/ssg/vite'; diff --git a/packages/qwik-router/global.d.ts b/packages/qwik-router/global.d.ts index 08bc95c6520..689e59a2843 100644 --- a/packages/qwik-router/global.d.ts +++ b/packages/qwik-router/global.d.ts @@ -21,3 +21,6 @@ declare var __EXPERIMENTAL__: { }; declare var __DEFAULT_LOADERS_SERIALIZATION_STRATEGY__: SerializationStrategy; + +/** Should routes not have a trailing slash? */ +declare var __NO_TRAILING_SLASH__: boolean; diff --git a/packages/qwik-router/package.json b/packages/qwik-router/package.json index 98b06e94d34..d1611fb8925 100644 --- a/packages/qwik-router/package.json +++ b/packages/qwik-router/package.json @@ -92,9 +92,14 @@ "require": "./lib/adapters/shared/vite/index.cjs" }, "./adapters/static/vite": { - "types": "./lib/adapters/static/vite/index.d.ts", - "import": "./lib/adapters/static/vite/index.mjs", - "require": "./lib/adapters/static/vite/index.cjs" + "types": "./lib/adapters/ssg/vite/index.d.ts", + "import": "./lib/adapters/ssg/vite/index.mjs", + "require": "./lib/adapters/ssg/vite/index.cjs" + }, + "./adapters/ssg/vite": { + "types": "./lib/adapters/ssg/vite/index.d.ts", + "import": "./lib/adapters/ssg/vite/index.mjs", + "require": "./lib/adapters/ssg/vite/index.cjs" }, "./adapters/vercel-edge/vite": { "types": "./lib/adapters/vercel-edge/vite/index.d.ts", @@ -144,9 +149,14 @@ "import": "./lib/middleware/vercel-edge/index.mjs" }, "./static": { - "types": "./lib/static/index.d.ts", - "import": "./lib/static/index.mjs", - "require": "./lib/static/index.cjs" + "types": "./lib/ssg/index.d.ts", + "import": "./lib/ssg/index.mjs", + "require": "./lib/ssg/index.cjs" + }, + "./ssg": { + "types": "./lib/ssg/index.d.ts", + "import": "./lib/ssg/index.mjs", + "require": "./lib/ssg/index.cjs" }, "./vite": { "types": "./lib/vite/index.d.ts", @@ -169,6 +179,7 @@ "README.md", "service-worker.d.ts", "static.d.ts", + "ssg.d.ts", "vite.d.ts" ], "homepage": "https://qwik.dev/", diff --git a/packages/qwik-router/src/adapters/azure-swa/adapter.azure-swa.api.md b/packages/qwik-router/src/adapters/azure-swa/adapter.azure-swa.api.md index 540bb1296d3..b9e11b5eda2 100644 --- a/packages/qwik-router/src/adapters/azure-swa/adapter.azure-swa.api.md +++ b/packages/qwik-router/src/adapters/azure-swa/adapter.azure-swa.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @public (undocumented) export function azureSwaAdapter(opts?: AzureSwaAdapterOptions): any; @@ -14,7 +14,7 @@ export function azureSwaAdapter(opts?: AzureSwaAdapterOptions): any; export interface AzureSwaAdapterOptions extends ServerAdapterOptions { } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/azure-swa/vite/index.ts b/packages/qwik-router/src/adapters/azure-swa/vite/index.ts index eee42333ab9..1b995d9de71 100644 --- a/packages/qwik-router/src/adapters/azure-swa/vite/index.ts +++ b/packages/qwik-router/src/adapters/azure-swa/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import fs from 'node:fs'; import { join } from 'node:path'; import { type ServerAdapterOptions, viteAdapter } from '../../shared/vite'; @@ -68,4 +68,4 @@ export function azureSwaAdapter(opts: AzureSwaAdapterOptions = {}): any { export interface AzureSwaAdapterOptions extends ServerAdapterOptions {} /** @public */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/bun-server/adapter.bun-server.api.md b/packages/qwik-router/src/adapters/bun-server/adapter.bun-server.api.md index 3dbadf68656..582e81feb48 100644 --- a/packages/qwik-router/src/adapters/bun-server/adapter.bun-server.api.md +++ b/packages/qwik-router/src/adapters/bun-server/adapter.bun-server.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @beta (undocumented) export function bunServerAdapter(opts?: bunServerAdapterOptions): any; @@ -16,7 +16,7 @@ export interface bunServerAdapterOptions extends ServerAdapterOptions { name?: string; } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/bun-server/vite/index.ts b/packages/qwik-router/src/adapters/bun-server/vite/index.ts index 396c9ae2098..6bcb325bb05 100644 --- a/packages/qwik-router/src/adapters/bun-server/vite/index.ts +++ b/packages/qwik-router/src/adapters/bun-server/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import { viteAdapter, type ServerAdapterOptions } from '../../shared/vite'; /** @beta */ @@ -30,4 +30,4 @@ export interface bunServerAdapterOptions extends ServerAdapterOptions { } /** @beta */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/cloud-run/adapter.cloud-run.api.md b/packages/qwik-router/src/adapters/cloud-run/adapter.cloud-run.api.md index a8f6055e910..c101f7732ed 100644 --- a/packages/qwik-router/src/adapters/cloud-run/adapter.cloud-run.api.md +++ b/packages/qwik-router/src/adapters/cloud-run/adapter.cloud-run.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @public (undocumented) export function cloudRunAdapter(opts?: CloudRunAdapterOptions): any; @@ -14,7 +14,7 @@ export function cloudRunAdapter(opts?: CloudRunAdapterOptions): any; export interface CloudRunAdapterOptions extends ServerAdapterOptions { } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/cloud-run/vite/index.ts b/packages/qwik-router/src/adapters/cloud-run/vite/index.ts index 785c04ea714..24c8126a0ff 100644 --- a/packages/qwik-router/src/adapters/cloud-run/vite/index.ts +++ b/packages/qwik-router/src/adapters/cloud-run/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import { type ServerAdapterOptions, viteAdapter } from '../../shared/vite'; /** @public */ @@ -24,4 +24,4 @@ export function cloudRunAdapter(opts: CloudRunAdapterOptions = {}): any { export interface CloudRunAdapterOptions extends ServerAdapterOptions {} /** @public */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/cloudflare-pages/adapter.cloudflare-pages.api.md b/packages/qwik-router/src/adapters/cloudflare-pages/adapter.cloudflare-pages.api.md index f0caf175cd5..6a97a6bcddf 100644 --- a/packages/qwik-router/src/adapters/cloudflare-pages/adapter.cloudflare-pages.api.md +++ b/packages/qwik-router/src/adapters/cloudflare-pages/adapter.cloudflare-pages.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @public (undocumented) export function cloudflarePagesAdapter(opts?: CloudflarePagesAdapterOptions): any; @@ -16,7 +16,7 @@ export interface CloudflarePagesAdapterOptions extends ServerAdapterOptions { staticPaths?: string[]; } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/cloudflare-pages/vite/index.ts b/packages/qwik-router/src/adapters/cloudflare-pages/vite/index.ts index 728e92452d1..ed0fed99c06 100644 --- a/packages/qwik-router/src/adapters/cloudflare-pages/vite/index.ts +++ b/packages/qwik-router/src/adapters/cloudflare-pages/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import fs from 'node:fs'; import { join, relative } from 'node:path'; import { normalizePathSlash } from '../../../utils/fs'; @@ -85,4 +85,4 @@ export interface CloudflarePagesAdapterOptions extends ServerAdapterOptions { } /** @public */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/deno-server/adapter.deno-server.api.md b/packages/qwik-router/src/adapters/deno-server/adapter.deno-server.api.md index efc7b7e1774..2f468113757 100644 --- a/packages/qwik-router/src/adapters/deno-server/adapter.deno-server.api.md +++ b/packages/qwik-router/src/adapters/deno-server/adapter.deno-server.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @beta (undocumented) export function denoServerAdapter(opts?: DenoServerAdapterOptions): any; @@ -16,7 +16,7 @@ export interface DenoServerAdapterOptions extends ServerAdapterOptions { name?: string; } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/deno-server/vite/index.ts b/packages/qwik-router/src/adapters/deno-server/vite/index.ts index 5f99f9f52a4..aba61a974b5 100644 --- a/packages/qwik-router/src/adapters/deno-server/vite/index.ts +++ b/packages/qwik-router/src/adapters/deno-server/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import { viteAdapter, type ServerAdapterOptions } from '../../shared/vite'; /** @beta */ @@ -42,4 +42,4 @@ export interface DenoServerAdapterOptions extends ServerAdapterOptions { } /** @beta */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/netlify-edge/adapter.netlify-edge.api.md b/packages/qwik-router/src/adapters/netlify-edge/adapter.netlify-edge.api.md index 79c02998108..21e8cb95a5e 100644 --- a/packages/qwik-router/src/adapters/netlify-edge/adapter.netlify-edge.api.md +++ b/packages/qwik-router/src/adapters/netlify-edge/adapter.netlify-edge.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @public (undocumented) export function netlifyEdgeAdapter(opts?: NetlifyEdgeAdapterOptions): any; @@ -17,7 +17,7 @@ export interface NetlifyEdgeAdapterOptions extends ServerAdapterOptions { staticPaths?: string[]; } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/netlify-edge/vite/index.ts b/packages/qwik-router/src/adapters/netlify-edge/vite/index.ts index 0b569f11563..0261b4fa485 100644 --- a/packages/qwik-router/src/adapters/netlify-edge/vite/index.ts +++ b/packages/qwik-router/src/adapters/netlify-edge/vite/index.ts @@ -1,5 +1,5 @@ import { basePathname } from '@qwik-router-config'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import fs, { existsSync } from 'node:fs'; import { join } from 'node:path'; import { getParentDir, type ServerAdapterOptions, viteAdapter } from '../../shared/vite'; @@ -131,4 +131,4 @@ export interface NetlifyEdgeAdapterOptions extends ServerAdapterOptions { } /** @public */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/node-server/adapter.node-server.api.md b/packages/qwik-router/src/adapters/node-server/adapter.node-server.api.md index 34785dc9d51..736c0f74d37 100644 --- a/packages/qwik-router/src/adapters/node-server/adapter.node-server.api.md +++ b/packages/qwik-router/src/adapters/node-server/adapter.node-server.api.md @@ -5,7 +5,7 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; // @beta (undocumented) export function nodeServerAdapter(opts?: NodeServerAdapterOptions): any; @@ -16,7 +16,7 @@ export interface NodeServerAdapterOptions extends ServerAdapterOptions { name?: string; } -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-router/src/adapters/node-server/vite/index.ts b/packages/qwik-router/src/adapters/node-server/vite/index.ts index 52e95b010e5..db733a4f024 100644 --- a/packages/qwik-router/src/adapters/node-server/vite/index.ts +++ b/packages/qwik-router/src/adapters/node-server/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import { viteAdapter, type ServerAdapterOptions } from '../../shared/vite'; /** @beta */ @@ -30,4 +30,4 @@ export interface NodeServerAdapterOptions extends ServerAdapterOptions { } /** @beta */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/adapters/shared/adapter.shared.api.md b/packages/qwik-router/src/adapters/shared/adapter.shared.api.md index 5737d80e817..49cb71e0d10 100644 --- a/packages/qwik-router/src/adapters/shared/adapter.shared.api.md +++ b/packages/qwik-router/src/adapters/shared/adapter.shared.api.md @@ -5,11 +5,11 @@ ```ts import type { Plugin as Plugin_2 } from 'vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import type { UserConfig } from 'vite'; // @public (undocumented) -export interface AdapterSSGOptions extends Omit { +export interface AdapterSSGOptions extends Omit { exclude?: string[]; include: string[]; origin?: string; diff --git a/packages/qwik-router/src/adapters/shared/vite/index.ts b/packages/qwik-router/src/adapters/shared/vite/index.ts index bd95d56afc7..c83c54b8dfd 100644 --- a/packages/qwik-router/src/adapters/shared/vite/index.ts +++ b/packages/qwik-router/src/adapters/shared/vite/index.ts @@ -1,5 +1,5 @@ import type { QwikVitePlugin } from '@qwik.dev/core/optimizer'; -import type { StaticGenerateOptions, StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { StaticGenerateOptions, SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import type { QwikRouterPlugin } from '@qwik.dev/router/vite'; import fs from 'node:fs'; import { basename, dirname, join, resolve } from 'node:path'; @@ -70,7 +70,22 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { } } }, - + buildStart() { + if (isSsrBuild && opts.ssg !== null) { + const { srcDir } = qwikVitePlugin!.api!.getOptions()!; + // TODO don't rely on entry points for SSG, somehow + this.emitFile({ + id: '@qwik-router-config', + type: 'chunk', + fileName: '@qwik-router-config.js', + }); + this.emitFile({ + id: `${srcDir}/entry.ssr`, + type: 'chunk', + fileName: 'entry.ssr.js', + }); + } + }, generateBundle(_, bundles) { if (isSsrBuild) { outputEntries.length = 0; @@ -87,18 +102,6 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { } } } - - if (!renderModulePath) { - throw new Error( - 'Unable to find "entry.ssr" entry point. Did you forget to add it to "build.rollupOptions.input"?' - ); - } - - if (!qwikRouterConfigModulePath) { - throw new Error( - 'Unable to find "@qwik-router-config" entry point. Did you forget to add it to "build.rollupOptions.input"?' - ); - } } }, @@ -139,14 +142,14 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) { } try { ssgOrigin = new URL(ssgOrigin).origin; - } catch (e) { + } catch { this.warn( `Invalid "origin" option: "${ssgOrigin}". Using default origin: "https://yoursite.qwik.dev"` ); ssgOrigin = `https://yoursite.qwik.dev`; } - const staticGenerate = await import('../../../static'); + const staticGenerate = await import('../../../ssg'); const generateOpts: StaticGenerateOptions = { maxWorkers: opts.maxWorkers, basePathname, @@ -262,7 +265,7 @@ export interface ServerAdapterOptions { } /** @public */ -export interface AdapterSSGOptions extends Omit { +export interface AdapterSSGOptions extends Omit { /** Defines routes that should be static generated. Accepts wildcard behavior. */ include: string[]; /** diff --git a/packages/qwik-router/src/adapters/ssg/adapter.ssg.api.md b/packages/qwik-router/src/adapters/ssg/adapter.ssg.api.md new file mode 100644 index 00000000000..0f8d9be1046 --- /dev/null +++ b/packages/qwik-router/src/adapters/ssg/adapter.ssg.api.md @@ -0,0 +1,21 @@ +## API Report File for "@qwik.dev/router" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { SsgRenderOptions } from '../../../ssg'; + +// @public (undocumented) +export function ssgAdapter(opts: SsgAdapterOptions): any; + +// @public (undocumented) +export interface SsgAdapterOptions extends Omit { +} + +// @public @deprecated (undocumented) +export const staticAdapter: typeof ssgAdapter; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-router/src/adapters/static/vite/api-extractor.json b/packages/qwik-router/src/adapters/ssg/vite/api-extractor.json similarity index 61% rename from packages/qwik-router/src/adapters/static/vite/api-extractor.json rename to packages/qwik-router/src/adapters/ssg/vite/api-extractor.json index c8679c637c1..82aaf6b93b8 100644 --- a/packages/qwik-router/src/adapters/static/vite/api-extractor.json +++ b/packages/qwik-router/src/adapters/ssg/vite/api-extractor.json @@ -1,20 +1,20 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", "extends": "../../../api-extractor.json", - "mainEntryPointFilePath": "/../../dist-dev/dts-out/packages/qwik-router/src/adapters/static/vite/index.d.ts", + "mainEntryPointFilePath": "/../../dist-dev/dts-out/packages/qwik-router/src/adapters/ssg/vite/index.d.ts", "newlineKind": "lf", "apiReport": { "enabled": true, - "reportFileName": "adapter.static", - "reportFolder": "/src/adapters/static/", - "reportTempFolder": "/../../dist-dev/api-extractor/qwik-router/adapters/static" + "reportFileName": "adapter.ssg", + "reportFolder": "/src/adapters/ssg/", + "reportTempFolder": "/../../dist-dev/api-extractor/qwik-router/adapters/ssg" }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/lib/adapters/static/vite/index.d.ts" + "untrimmedFilePath": "/lib/adapters/ssg/vite/index.d.ts" }, "docModel": { "enabled": true, - "apiJsonFilePath": "/../../dist-dev/api/qwik-router/vite/static/docs.api.json" + "apiJsonFilePath": "/../../dist-dev/api/qwik-router/vite/ssg/docs.api.json" } } diff --git a/packages/qwik-router/src/adapters/ssg/vite/index.ts b/packages/qwik-router/src/adapters/ssg/vite/index.ts new file mode 100644 index 00000000000..9ef64224a75 --- /dev/null +++ b/packages/qwik-router/src/adapters/ssg/vite/index.ts @@ -0,0 +1,20 @@ +import type { SsgRenderOptions } from '../../../ssg'; +import { viteAdapter } from '../../shared/vite'; + +/** @public */ +export function ssgAdapter(opts: SsgAdapterOptions): any { + return viteAdapter({ + name: 'static-site-generation', + origin: opts.origin, + ssg: { + include: ['/*'], + ...opts, + }, + }); +} + +/** @public @deprecated Use `ssgAdapter` instead. */ +export const staticAdapter = ssgAdapter; + +/** @public */ +export interface SsgAdapterOptions extends Omit {} diff --git a/packages/qwik-router/src/adapters/static/adapter.static.api.md b/packages/qwik-router/src/adapters/static/adapter.static.api.md deleted file mode 100644 index d724ba37fdc..00000000000 --- a/packages/qwik-router/src/adapters/static/adapter.static.api.md +++ /dev/null @@ -1,18 +0,0 @@ -## API Report File for "@qwik.dev/router" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import type { StaticGenerateRenderOptions } from '../../../static'; - -// @public (undocumented) -export function staticAdapter(opts: StaticGenerateAdapterOptions): any; - -// @public (undocumented) -export interface StaticGenerateAdapterOptions extends Omit { -} - -// (No @packageDocumentation comment for this package) - -``` diff --git a/packages/qwik-router/src/adapters/static/vite/index.ts b/packages/qwik-router/src/adapters/static/vite/index.ts deleted file mode 100644 index aafff639780..00000000000 --- a/packages/qwik-router/src/adapters/static/vite/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { StaticGenerateRenderOptions } from '../../../static'; -import { viteAdapter } from '../../shared/vite'; - -/** @public */ -export function staticAdapter(opts: StaticGenerateAdapterOptions): any { - return viteAdapter({ - name: 'static-generate', - origin: opts.origin, - ssg: { - include: ['/*'], - ...opts, - }, - }); -} - -/** @public */ -export interface StaticGenerateAdapterOptions extends Omit {} diff --git a/packages/qwik-router/src/adapters/vercel-edge/adapter.vercel-edge.api.md b/packages/qwik-router/src/adapters/vercel-edge/adapter.vercel-edge.api.md index 9873821540b..05ffc97ad50 100644 --- a/packages/qwik-router/src/adapters/vercel-edge/adapter.vercel-edge.api.md +++ b/packages/qwik-router/src/adapters/vercel-edge/adapter.vercel-edge.api.md @@ -5,9 +5,9 @@ ```ts import { ServerAdapterOptions } from '../../shared/vite'; -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; -export { StaticGenerateRenderOptions } +export { SsgRenderOptions } // @public (undocumented) export function vercelEdgeAdapter(opts?: VercelEdgeAdapterOptions): any; diff --git a/packages/qwik-router/src/adapters/vercel-edge/vite/index.ts b/packages/qwik-router/src/adapters/vercel-edge/vite/index.ts index 0d3b76b54f3..87d1df73646 100644 --- a/packages/qwik-router/src/adapters/vercel-edge/vite/index.ts +++ b/packages/qwik-router/src/adapters/vercel-edge/vite/index.ts @@ -1,4 +1,4 @@ -import type { StaticGenerateRenderOptions } from '@qwik.dev/router/static'; +import type { SsgRenderOptions } from 'packages/qwik-router/src/ssg'; import fs from 'node:fs'; import { dirname, join } from 'node:path'; import { getParentDir, type ServerAdapterOptions, viteAdapter } from '../../shared/vite'; @@ -140,4 +140,4 @@ export interface VercelEdgeAdapterOptions extends ServerAdapterOptions { } /** @public */ -export type { StaticGenerateRenderOptions }; +export type { SsgRenderOptions }; diff --git a/packages/qwik-router/src/buildtime/context.ts b/packages/qwik-router/src/buildtime/context.ts index 75d6448240c..4f2b0dbff46 100644 --- a/packages/qwik-router/src/buildtime/context.ts +++ b/packages/qwik-router/src/buildtime/context.ts @@ -92,10 +92,6 @@ function normalizeOptions( const url = new URL(opts.basePathname, 'https://qwik.dev/'); opts.basePathname = url.pathname; - if (typeof opts.trailingSlash !== 'boolean') { - opts.trailingSlash = true; - } - opts.mdx = opts.mdx || {}; opts.platform = opts.platform || {}; diff --git a/packages/qwik-router/src/buildtime/markdown/markdown-url.ts b/packages/qwik-router/src/buildtime/markdown/markdown-url.ts index d8cd091d7df..0b56ae83204 100644 --- a/packages/qwik-router/src/buildtime/markdown/markdown-url.ts +++ b/packages/qwik-router/src/buildtime/markdown/markdown-url.ts @@ -49,10 +49,10 @@ export function getMarkdownRelativeUrl( } } else if (extension === '') { if (url.endsWith('/')) { - if (!opts.trailingSlash) { + if (globalThis.__NO_TRAILING_SLASH__) { url = url.slice(0, -1); } - } else if (opts.trailingSlash) { + } else if (!globalThis.__NO_TRAILING_SLASH__) { url += '/'; } } diff --git a/packages/qwik-router/src/buildtime/markdown/markdown-url.unit.ts b/packages/qwik-router/src/buildtime/markdown/markdown-url.unit.ts index 7a5757e3295..a2a951831f6 100644 --- a/packages/qwik-router/src/buildtime/markdown/markdown-url.unit.ts +++ b/packages/qwik-router/src/buildtime/markdown/markdown-url.unit.ts @@ -66,10 +66,9 @@ const menuFilePath = join(routesDir, 'docs', 'menu.md'); expect: './getting-started.txt', }, ].forEach((t) => { - test(`getMarkdownRelativeUrl ${t.href}`, () => { + test(`getMarkdownRelativeUrl ${t.href} ${t.trailingSlash ? 'with' : 'without'} slash`, () => { const opts: NormalizedPluginOptions = { basePathname: '/', - trailingSlash: !!t.trailingSlash, routesDir, serverPluginsDir, mdxPlugins: { @@ -82,6 +81,7 @@ const menuFilePath = join(routesDir, 'docs', 'menu.md'); rewriteRoutes: [], defaultLoadersSerializationStrategy: 'never', }; + globalThis.__NO_TRAILING_SLASH__ = !t.trailingSlash; assert.equal(getMarkdownRelativeUrl(opts, menuFilePath, t.href), t.expect); }); }); diff --git a/packages/qwik-router/src/buildtime/routing/resolve-source-file.unit.ts b/packages/qwik-router/src/buildtime/routing/resolve-source-file.unit.ts index f699938adf6..13d1c3fbb33 100644 --- a/packages/qwik-router/src/buildtime/routing/resolve-source-file.unit.ts +++ b/packages/qwik-router/src/buildtime/routing/resolve-source-file.unit.ts @@ -40,7 +40,6 @@ test('resolveLayout', () => { routesDir: '', serverPluginsDir: '', basePathname: '/', - trailingSlash: false, mdxPlugins: { remarkGfm: true, rehypeSyntaxHighlight: true, diff --git a/packages/qwik-router/src/buildtime/runtime-generation/generate-qwik-router-config.ts b/packages/qwik-router/src/buildtime/runtime-generation/generate-qwik-router-config.ts index 5908eee1afe..efb8b41f908 100644 --- a/packages/qwik-router/src/buildtime/runtime-generation/generate-qwik-router-config.ts +++ b/packages/qwik-router/src/buildtime/runtime-generation/generate-qwik-router-config.ts @@ -24,7 +24,7 @@ export function generateQwikRouterConfig( createEntries(ctx, c); - c.push(`export const trailingSlash = ${JSON.stringify(!!ctx.opts.trailingSlash)};`); + c.push(`export const trailingSlash = ${JSON.stringify(!globalThis.__NO_TRAILING_SLASH__)};`); c.push(`export const basePathname = ${JSON.stringify(ctx.opts.basePathname)};`); diff --git a/packages/qwik-router/src/buildtime/types.ts b/packages/qwik-router/src/buildtime/types.ts index 7f6473a11d8..ad505d19ca7 100644 --- a/packages/qwik-router/src/buildtime/types.ts +++ b/packages/qwik-router/src/buildtime/types.ts @@ -145,7 +145,7 @@ export interface MdxPlugins { rehypeAutolinkHeadings: boolean; } -export interface NormalizedPluginOptions extends Required { +export interface NormalizedPluginOptions extends Omit, 'trailingSlash'> { assetsDir?: string; } diff --git a/packages/qwik-router/src/buildtime/vite/dev-server.ts b/packages/qwik-router/src/buildtime/vite/dev-server.ts index a8d5abcc502..4b43545c0f9 100644 --- a/packages/qwik-router/src/buildtime/vite/dev-server.ts +++ b/packages/qwik-router/src/buildtime/vite/dev-server.ts @@ -44,7 +44,7 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) { return { route, params }; } - if (ctx.opts.trailingSlash && !pathname.endsWith('/')) { + if (!globalThis.__NO_TRAILING_SLASH__ && !pathname.endsWith('/')) { params = matchRoute(route.pathname, pathname + '/'); if (params) { return { route, params }; @@ -129,7 +129,7 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) { return { serverPlugins, loadedRoute }; }; const resolveRoute = (routeModulePaths: WeakMap, string>, url: URL) => { - const matchPathname = getRouteMatchPathname(url.pathname, ctx.opts.trailingSlash); + const matchPathname = getRouteMatchPathname(url.pathname); routePs[matchPathname] ||= _resolveRoute(routeModulePaths, matchPathname).finally(() => { delete routePs[matchPathname]; }); @@ -251,7 +251,6 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) { loadedRoute, requestHandlers, rebuildRouteInfo, - ctx.opts.trailingSlash, ctx.opts.basePathname, qwikSerializer ); diff --git a/packages/qwik-router/src/buildtime/vite/plugin.ts b/packages/qwik-router/src/buildtime/vite/plugin.ts index c28e83a8fca..4696082ef7c 100644 --- a/packages/qwik-router/src/buildtime/vite/plugin.ts +++ b/packages/qwik-router/src/buildtime/vite/plugin.ts @@ -71,6 +71,9 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { type P = Plugin & { api: T }; + let didEmitStaticPaths = false; + let didEmitNotFoundPaths = false; + const plugin: P = { name: 'vite-plugin-qwik-router', enforce: 'pre', @@ -82,6 +85,7 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { 'globalThis.__DEFAULT_LOADERS_SERIALIZATION_STRATEGY__': JSON.stringify( userOpts?.defaultLoadersSerializationStrategy || 'never' ), + 'globalThis.__NO_TRAILING_SLASH__': JSON.stringify(userOpts?.trailingSlash === false), }, appType: 'custom', resolve: { @@ -147,6 +151,7 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { }, configureServer(server) { + // this callback is run after the vite middlewares are registered return () => { if (!ctx) { throw new Error('configureServer: Missing ctx from configResolved'); @@ -163,6 +168,8 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { }, buildStart() { + didEmitStaticPaths = false; + didEmitNotFoundPaths = false; resetBuildContext(ctx); }, @@ -182,17 +189,29 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { if (id === QWIK_ROUTER_SW_REGISTER) { return join(rootDir!, id); } - if (id === STATIC_PATHS_ID) { - return { - id: './' + RESOLVED_STATIC_PATHS_ID, - external: true, - }; + if (id.endsWith(STATIC_PATHS_ID)) { + const resolvedId = 'virtual:' + RESOLVED_STATIC_PATHS_ID; + if (!didEmitStaticPaths) { + this.emitFile({ + type: 'chunk', + fileName: RESOLVED_STATIC_PATHS_ID, + id, + }); + didEmitStaticPaths = true; + } + return { id: resolvedId }; } - if (id === NOT_FOUND_PATHS_ID) { - return { - id: './' + RESOLVED_NOT_FOUND_PATHS_ID, - external: true, - }; + if (id.endsWith(NOT_FOUND_PATHS_ID)) { + const resolvedId = 'virtual:' + RESOLVED_NOT_FOUND_PATHS_ID; + if (!didEmitNotFoundPaths) { + this.emitFile({ + type: 'chunk', + fileName: RESOLVED_NOT_FOUND_PATHS_ID, + id, + }); + didEmitNotFoundPaths = true; + } + return { id: resolvedId }; } return null; }, @@ -204,12 +223,11 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { return generateQwikRouterEntries(ctx); } const isSerializer = id.endsWith(QWIK_SERIALIZER); - const isRouterConfig = id.endsWith(QWIK_ROUTER_CONFIG_ID); - const isSwRegister = id.endsWith(QWIK_ROUTER_SW_REGISTER); - if (isSerializer) { return `export {_deserialize, _serialize, _verifySerializable} from '@qwik.dev/core'`; } + const isRouterConfig = id.endsWith(QWIK_ROUTER_CONFIG_ID); + const isSwRegister = id.endsWith(QWIK_ROUTER_SW_REGISTER); if (isRouterConfig || isSwRegister) { if (!ctx.isDevServer && ctx.isDirty) { await build(ctx); @@ -231,6 +249,15 @@ function qwikRouterPlugin(userOpts?: QwikRouterVitePluginOptions): any { } } } + // These files are overwritten in post-build.ts + const isStaticPaths = id.endsWith(RESOLVED_STATIC_PATHS_ID); + if (isStaticPaths) { + return `export const isStaticPath = () => false`; + } + const isNotFoundPathsId = id.endsWith(RESOLVED_NOT_FOUND_PATHS_ID); + if (isNotFoundPathsId) { + return `export const getNotFound = () => {}`; + } return null; }, diff --git a/packages/qwik-router/src/middleware/aws-lambda/index.ts b/packages/qwik-router/src/middleware/aws-lambda/index.ts index 681a4218245..9d86e7cdac6 100644 --- a/packages/qwik-router/src/middleware/aws-lambda/index.ts +++ b/packages/qwik-router/src/middleware/aws-lambda/index.ts @@ -7,18 +7,17 @@ import type { ServerRenderOptions } from '@qwik.dev/router/middleware/request-ha interface AwsOpt { render: Render; manifest?: QwikManifest; - qwikRouterConfig: QwikRouterConfig; - /** @deprecated Use `QwikRouterConfig` instead. Will be removed in V3 */ + /** @deprecated Not used */ + qwikRouterConfig?: QwikRouterConfig; + /** @deprecated Not used */ qwikCityPlan?: QwikCityPlan; } /** @public */ export function createQwikRouter(opts: AwsOpt) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } try { const { router, staticFile, notFound } = createQwikRouterNode({ @@ -37,7 +36,7 @@ export function createQwikRouter(opts: AwsOpt) { }); const fixPath = (pathT: string) => { - if (opts.qwikRouterConfig.trailingSlash) { + if (!globalThis.__NO_TRAILING_SLASH__) { const url = new URL(pathT, 'http://aws-qwik.local'); if (url.pathname.includes('.', url.pathname.lastIndexOf('/'))) { return pathT; diff --git a/packages/qwik-router/src/middleware/azure-swa/index.ts b/packages/qwik-router/src/middleware/azure-swa/index.ts index acfdbcf07cb..43ef55198fa 100644 --- a/packages/qwik-router/src/middleware/azure-swa/index.ts +++ b/packages/qwik-router/src/middleware/azure-swa/index.ts @@ -51,10 +51,8 @@ interface AzureCookie { /** @public */ export function createQwikRouter(opts: QwikRouterAzureOptions): AzureFunction { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const qwikSerializer: QwikSerializer = { _deserialize, diff --git a/packages/qwik-router/src/middleware/bun/index.ts b/packages/qwik-router/src/middleware/bun/index.ts index cedb50e3833..f945d6a342a 100644 --- a/packages/qwik-router/src/middleware/bun/index.ts +++ b/packages/qwik-router/src/middleware/bun/index.ts @@ -19,10 +19,8 @@ import { MIME_TYPES } from '../request-handler/mime-types'; /** @public */ export function createQwikRouter(opts: QwikRouterBunOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } // @qwik.dev/router/middleware/bun // still missing from bun: last check was bun version 1.1.8 @@ -129,7 +127,7 @@ export function createQwikRouter(opts: QwikRouterBunOptions) { let filePath: string; if (fileName.includes('.')) { filePath = join(staticFolder, pathname); - } else if (opts.qwikRouterConfig!.trailingSlash) { + } else if (!globalThis.__NO_TRAILING_SLASH__) { filePath = join(staticFolder, pathname + 'index.html'); } else { filePath = join(staticFolder, pathname, 'index.html'); diff --git a/packages/qwik-router/src/middleware/cloudflare-pages/index.ts b/packages/qwik-router/src/middleware/cloudflare-pages/index.ts index bcc5d92fa38..5b8bf8c2a89 100644 --- a/packages/qwik-router/src/middleware/cloudflare-pages/index.ts +++ b/packages/qwik-router/src/middleware/cloudflare-pages/index.ts @@ -17,17 +17,14 @@ import { /** @public */ export function createQwikRouter(opts: QwikRouterCloudflarePagesOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } try { // https://developers.cloudflare.com/workers/configuration/compatibility-dates/#streams-constructors // this will throw if CF compatibility_date < 2022-11-30 new globalThis.TextEncoderStream(); - } catch (e) { - // @ts-ignore + } catch { globalThis.TextEncoderStream = _TextEncoderStream_polyfill; } const qwikSerializer = { _deserialize, _serialize, _verifySerializable }; diff --git a/packages/qwik-router/src/middleware/deno/index.ts b/packages/qwik-router/src/middleware/deno/index.ts index 147e15951e0..98851461ab3 100644 --- a/packages/qwik-router/src/middleware/deno/index.ts +++ b/packages/qwik-router/src/middleware/deno/index.ts @@ -30,10 +30,8 @@ export interface ServeHandlerInfo { /** @public */ export function createQwikRouter(opts: QwikRouterDenoOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const qwikSerializer: QwikSerializer = { _deserialize, @@ -132,7 +130,7 @@ export function createQwikRouter(opts: QwikRouterDenoOptions) { let filePath: string; if (fileName.includes('.')) { filePath = join(staticFolder, pathname); - } else if (opts.qwikRouterConfig!.trailingSlash) { + } else if (!globalThis.__NO_TRAILING_SLASH__) { filePath = join(staticFolder, pathname + 'index.html'); } else { filePath = join(staticFolder, pathname, 'index.html'); diff --git a/packages/qwik-router/src/middleware/firebase/index.ts b/packages/qwik-router/src/middleware/firebase/index.ts index 8580e5ccbf0..d51b0e514ce 100755 --- a/packages/qwik-router/src/middleware/firebase/index.ts +++ b/packages/qwik-router/src/middleware/firebase/index.ts @@ -5,10 +5,8 @@ import type { ServerRenderOptions } from '@qwik.dev/router/middleware/request-ha /** @public */ export function createQwikRouter(opts: QwikRouterFirebaseOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const { staticFile, notFound, router } = createQwikRouterNode({ render: opts.render, diff --git a/packages/qwik-router/src/middleware/netlify-edge/index.ts b/packages/qwik-router/src/middleware/netlify-edge/index.ts index 758964073d2..00090db03a1 100644 --- a/packages/qwik-router/src/middleware/netlify-edge/index.ts +++ b/packages/qwik-router/src/middleware/netlify-edge/index.ts @@ -17,10 +17,8 @@ declare const Deno: any; /** @public */ export function createQwikRouter(opts: QwikRouterNetlifyOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const qwikSerializer: QwikSerializer = { _deserialize, diff --git a/packages/qwik-router/src/middleware/node/index.ts b/packages/qwik-router/src/middleware/node/index.ts index 87dca72cff6..377a2afac2d 100644 --- a/packages/qwik-router/src/middleware/node/index.ts +++ b/packages/qwik-router/src/middleware/node/index.ts @@ -18,10 +18,8 @@ import { computeOrigin, fromNodeHttp, getUrl } from './http'; /** @public */ export function createQwikRouter(opts: QwikRouterNodeRequestOptions | QwikCityNodeRequestOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const qwikSerializer: QwikSerializer = { @@ -108,7 +106,7 @@ export function createQwikRouter(opts: QwikRouterNodeRequestOptions | QwikCityNo let filePath: string; if (basename(pathname).includes('.')) { filePath = join(staticFolder, pathname); - } else if (opts.qwikRouterConfig!.trailingSlash) { + } else if (!globalThis.__NO_TRAILING_SLASH__) { filePath = join(staticFolder, pathname + 'index.html'); } else { filePath = join(staticFolder, pathname, 'index.html'); diff --git a/packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md b/packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md index 60f43ae05a9..45c2499bb7d 100644 --- a/packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md +++ b/packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md @@ -204,7 +204,7 @@ export interface ServerRenderOptions extends RenderOptions { checkOrigin?: boolean; // @deprecated (undocumented) qwikCityPlan?: QwikCityPlan; - // (undocumented) + // @deprecated (undocumented) qwikRouterConfig?: QwikRouterConfig; // (undocumented) render: Render; diff --git a/packages/qwik-router/src/middleware/request-handler/request-event.ts b/packages/qwik-router/src/middleware/request-handler/request-event.ts index aea33e469d1..6744b25935c 100644 --- a/packages/qwik-router/src/middleware/request-handler/request-event.ts +++ b/packages/qwik-router/src/middleware/request-handler/request-event.ts @@ -37,7 +37,6 @@ export const RequestEvQwikSerializer = Symbol('RequestEvQwikSerializer'); export const RequestEvLoaderSerializationStrategyMap = Symbol( 'RequestEvLoaderSerializationStrategyMap' ); -export const RequestEvTrailingSlash = Symbol('RequestEvTrailingSlash'); export const RequestRouteName = '@routeName'; export const RequestEvSharedActionId = '@actionId'; export const RequestEvSharedActionFormData = '@actionFormData'; @@ -51,7 +50,6 @@ export function createRequestEvent( serverRequestEv: ServerRequestEvent, loadedRoute: LoadedRoute | null, requestHandlers: RequestHandler[], - trailingSlash: boolean, basePathname: string, qwikSerializer: QwikSerializer, resolved: (response: any) => void @@ -64,7 +62,7 @@ export function createRequestEvent( const url = new URL(request.url); if (url.pathname.endsWith(QDATA_JSON)) { url.pathname = url.pathname.slice(0, -QDATA_JSON_LEN); - if (trailingSlash && !url.pathname.endsWith('/')) { + if (!globalThis.__NO_TRAILING_SLASH__ && !url.pathname.endsWith('/')) { url.pathname += '/'; } sharedMap.set(IsQData, true); @@ -155,7 +153,6 @@ export function createRequestEvent( [RequestEvLoaders]: loaders, [RequestEvLoaderSerializationStrategyMap]: new Map(), [RequestEvMode]: serverRequestEv.mode, - [RequestEvTrailingSlash]: trailingSlash, get [RequestEvRoute]() { return loadedRoute; }, @@ -339,7 +336,6 @@ export interface RequestEventInternal extends RequestEvent, RequestEventLoader { [RequestEvLoaders]: Record | undefined>; [RequestEvLoaderSerializationStrategyMap]: Map; [RequestEvMode]: ServerRequestMode; - [RequestEvTrailingSlash]: boolean; [RequestEvRoute]: LoadedRoute | null; [RequestEvQwikSerializer]: QwikSerializer; @@ -372,10 +368,6 @@ export function getRequestLoaderSerializationStrategyMap(requestEv: RequestEvent return (requestEv as RequestEventInternal)[RequestEvLoaderSerializationStrategyMap]; } -export function getRequestTrailingSlash(requestEv: RequestEventCommon) { - return (requestEv as RequestEventInternal)[RequestEvTrailingSlash]; -} - export function getRequestRoute(requestEv: RequestEventCommon) { return (requestEv as RequestEventInternal)[RequestEvRoute]; } diff --git a/packages/qwik-router/src/middleware/request-handler/request-handler.ts b/packages/qwik-router/src/middleware/request-handler/request-handler.ts index d5d29c51daf..bd64cf6ff09 100644 --- a/packages/qwik-router/src/middleware/request-handler/request-handler.ts +++ b/packages/qwik-router/src/middleware/request-handler/request-handler.ts @@ -5,6 +5,11 @@ import { renderQwikMiddleware, resolveRequestHandlers } from './resolve-request- import type { QwikSerializer, ServerRenderOptions, ServerRequestEvent } from './types'; import { getRouteMatchPathname, runQwikRouter, type QwikRouterRun } from './user-response'; +/** + * We need to delay importing the config until the first request, because vite also imports from + * this file and @qwik-router-config doesn't exist from the vite config before the build. + */ +let qwikRouterConfigActual: QwikRouterConfig; /** * The request handler for QwikRouter. Called by every integration. * @@ -15,13 +20,20 @@ export async function requestHandler( opts: ServerRenderOptions, qwikSerializer: QwikSerializer ): Promise | null> { - const { render, qwikRouterConfig, checkOrigin } = opts; + const { render, checkOrigin } = opts; + let { qwikRouterConfig } = opts; + if (!qwikRouterConfig) { + if (!qwikRouterConfigActual) { + qwikRouterConfigActual = await import('@qwik-router-config'); + } + qwikRouterConfig = qwikRouterConfigActual; + } if (!qwikRouterConfig) { throw new Error('qwikRouterConfig is required.'); } const pathname = serverRequestEv.url.pathname; - const matchPathname = getRouteMatchPathname(pathname, qwikRouterConfig.trailingSlash); + const matchPathname = getRouteMatchPathname(pathname); const routeAndHandlers = await loadRequestHandlers( qwikRouterConfig, matchPathname, @@ -34,7 +46,7 @@ export async function requestHandler( const [route, requestHandlers] = routeAndHandlers; const rebuildRouteInfo: RebuildRouteInfoInternal = async (url: URL) => { - const matchPathname = getRouteMatchPathname(url.pathname, qwikRouterConfig.trailingSlash); + const matchPathname = getRouteMatchPathname(url.pathname); const routeAndHandlers = await loadRequestHandlers( qwikRouterConfig, matchPathname, @@ -56,7 +68,6 @@ export async function requestHandler( route, requestHandlers, rebuildRouteInfo, - qwikRouterConfig.trailingSlash, qwikRouterConfig.basePathname, qwikSerializer ); diff --git a/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts b/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts index f06d4cb0e4b..94396b91938 100644 --- a/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts +++ b/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts @@ -27,7 +27,6 @@ import { getRequestLoaderSerializationStrategyMap, getRequestLoaders, getRequestMode, - getRequestTrailingSlash, type RequestEventInternal, } from './request-event'; import { getQwikRouterServerData } from './response-page'; @@ -397,13 +396,12 @@ async function pureServerFunction(ev: RequestEvent) { } function fixTrailingSlash(ev: RequestEvent) { - const trailingSlash = getRequestTrailingSlash(ev); const { basePathname, originalUrl, sharedMap } = ev; const { pathname, search } = originalUrl; const isQData = sharedMap.has(IsQData); if (!isQData && pathname !== basePathname && !pathname.endsWith('.html')) { // only check for slash redirect on pages - if (trailingSlash) { + if (!globalThis.__NO_TRAILING_SLASH__) { // must have a trailing slash if (!pathname.endsWith('/')) { // add slash to existing pathname @@ -442,12 +440,12 @@ export function isLastModulePageRoute(routeModules: RouteModule[]) { return lastRouteModule && typeof (lastRouteModule as PageModule).default === 'function'; } -export function getPathname(url: URL, trailingSlash: boolean | undefined) { +export function getPathname(url: URL) { url = new URL(url); if (url.pathname.endsWith(QDATA_JSON)) { url.pathname = url.pathname.slice(0, -QDATA_JSON.length); } - if (trailingSlash) { + if (!globalThis.__NO_TRAILING_SLASH__) { if (!url.pathname.endsWith('/')) { url.pathname += '/'; } @@ -500,7 +498,6 @@ export function renderQwikMiddleware(render: Render) { responseHeaders.set('Content-Type', 'text/html; charset=utf-8'); } - const trailingSlash = getRequestTrailingSlash(requestEv); const { readable, writable } = new TextEncoderStream(); const writableStream = requestEv.getWritableStream(); const pipe = readable.pipeTo(writableStream, { preventClose: true }); @@ -522,7 +519,7 @@ export function renderQwikMiddleware(render: Render) { loaders: getRequestLoaders(requestEv), action: requestEv.sharedMap.get(RequestEvSharedActionId), status: status !== 200 ? status : 200, - href: getPathname(requestEv.url, trailingSlash), + href: getPathname(requestEv.url), }; if (typeof (result as any as RenderToStringResult).html === 'string') { // render result used renderToString(), so none of it was streamed @@ -587,7 +584,6 @@ export async function renderQData(requestEv: RequestEvent) { const status = requestEv.status(); const redirectLocation = requestEv.headers.get('Location'); - const trailingSlash = getRequestTrailingSlash(requestEv); const requestHeaders: Record = {}; requestEv.request.headers.forEach((value, key) => (requestHeaders[key] = value)); @@ -612,13 +608,13 @@ export async function renderQData(requestEv: RequestEvent) { // send minimal data to the client loaders, status: status !== 200 ? status : 200, - href: getPathname(requestEv.url, trailingSlash), + href: getPathname(requestEv.url), } : { loaders, action: requestEv.sharedMap.get(RequestEvSharedActionId), status: status !== 200 ? status : 200, - href: getPathname(requestEv.url, trailingSlash), + href: getPathname(requestEv.url), redirect: redirectLocation ?? undefined, isRewrite: requestEv.sharedMap.get(RequestEvIsRewrite), }; diff --git a/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.unit.ts b/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.unit.ts index ef3de3c1e0a..6fe6014964f 100644 --- a/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.unit.ts +++ b/packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.unit.ts @@ -4,31 +4,31 @@ import { getPathname, isContentType } from './resolve-request-handlers'; describe('resolve-request-handler', () => { describe('getPathname', () => { it('should remove q-data.json', () => { - expect(getPathname(new URL('http://server/path/q-data.json?foo=bar#hash'), true)).toBe( + globalThis.__NO_TRAILING_SLASH__ = false; + expect(getPathname(new URL('http://server/path/q-data.json?foo=bar#hash'))).toBe( '/path/?foo=bar#hash' ); - expect(getPathname(new URL('http://server/path/q-data.json?foo=bar#hash'), false)).toBe( + globalThis.__NO_TRAILING_SLASH__ = true; + expect(getPathname(new URL('http://server/path/q-data.json?foo=bar#hash'))).toBe( '/path?foo=bar#hash' ); }); it('should pass non q-data.json through', () => { - expect(getPathname(new URL('http://server/path?foo=bar#hash'), true)).toBe( - '/path/?foo=bar#hash' - ); - expect(getPathname(new URL('http://server/path/?foo=bar#hash'), false)).toBe( - '/path?foo=bar#hash' - ); + globalThis.__NO_TRAILING_SLASH__ = false; + expect(getPathname(new URL('http://server/path?foo=bar#hash'))).toBe('/path/?foo=bar#hash'); + globalThis.__NO_TRAILING_SLASH__ = true; + expect(getPathname(new URL('http://server/path/?foo=bar#hash'))).toBe('/path?foo=bar#hash'); }); it('should remove internal search params', () => { - expect(getPathname(new URL('http://server/path?qaction=123&qdata=data'), true)).toBe( - '/path/' - ); - expect(getPathname(new URL('http://server/path?foo=1&qfunc=f&bar=2'), false)).toBe( + globalThis.__NO_TRAILING_SLASH__ = false; + expect(getPathname(new URL('http://server/path?qaction=123&qdata=data'))).toBe('/path/'); + globalThis.__NO_TRAILING_SLASH__ = true; + expect(getPathname(new URL('http://server/path?foo=1&qfunc=f&bar=2'))).toBe( '/path?foo=1&bar=2' ); - expect(getPathname(new URL('http://server/path?foo=1&qloaders=f&bar=2'), false)).toBe( + expect(getPathname(new URL('http://server/path?foo=1&qloaders=f&bar=2'))).toBe( '/path?foo=1&bar=2' ); }); diff --git a/packages/qwik-router/src/middleware/request-handler/types.ts b/packages/qwik-router/src/middleware/request-handler/types.ts index 640249b16fa..97173a6a66f 100644 --- a/packages/qwik-router/src/middleware/request-handler/types.ts +++ b/packages/qwik-router/src/middleware/request-handler/types.ts @@ -48,9 +48,10 @@ export type ServerResponseHandler = ( export interface ServerRenderOptions extends RenderOptions { render: Render; - /** @deprecated Use `QwikRouterConfig` instead. Will be removed in V3 */ + /** @deprecated Not used */ qwikCityPlan?: QwikCityPlan; + /** @deprecated Not used */ qwikRouterConfig?: QwikRouterConfig; /** * Protection against cross-site request forgery (CSRF) attacks. diff --git a/packages/qwik-router/src/middleware/request-handler/user-response.ts b/packages/qwik-router/src/middleware/request-handler/user-response.ts index e519c34de11..4656d146149 100644 --- a/packages/qwik-router/src/middleware/request-handler/user-response.ts +++ b/packages/qwik-router/src/middleware/request-handler/user-response.ts @@ -41,7 +41,6 @@ export function runQwikRouter( loadedRoute: LoadedRoute | null, requestHandlers: RequestHandler[], rebuildRouteInfo: RebuildRouteInfoInternal, - trailingSlash = true, basePathname = '/', qwikSerializer: QwikSerializer ): QwikRouterRun { @@ -51,7 +50,6 @@ export function runQwikRouter( serverRequestEv, loadedRoute, requestHandlers, - trailingSlash, basePathname, qwikSerializer, resolve! @@ -144,9 +142,9 @@ async function runNext( * The pathname used to match in the route regex array. A pathname ending with /q-data.json should * be treated as a pathname without it. */ -export function getRouteMatchPathname(pathname: string, trailingSlash: boolean | undefined) { +export function getRouteMatchPathname(pathname: string) { if (pathname.endsWith(QDATA_JSON)) { - const trimEnd = pathname.length - QDATA_JSON_LEN + (trailingSlash ? 1 : 0); + const trimEnd = pathname.length - QDATA_JSON_LEN + (globalThis.__NO_TRAILING_SLASH__ ? 0 : 1); pathname = pathname.slice(0, trimEnd); if (pathname === '') { pathname = '/'; diff --git a/packages/qwik-router/src/middleware/vercel-edge/index.ts b/packages/qwik-router/src/middleware/vercel-edge/index.ts index 3e492cbbbaa..186daa0fa9d 100644 --- a/packages/qwik-router/src/middleware/vercel-edge/index.ts +++ b/packages/qwik-router/src/middleware/vercel-edge/index.ts @@ -20,10 +20,8 @@ const BASE_URL = 'BASE_URL'; /** @public */ export function createQwikRouter(opts: QwikRouterVercelEdgeOptions) { if (opts.qwikCityPlan && !opts.qwikRouterConfig) { - console.warn('qwikCityPlan is deprecated. Use qwikRouterConfig instead.'); + console.warn('qwikCityPlan is deprecated. Simply remove it.'); opts.qwikRouterConfig = opts.qwikCityPlan; - } else if (!opts.qwikRouterConfig) { - throw new Error('qwikRouterConfig is required.'); } const qwikSerializer: QwikSerializer = { _deserialize, diff --git a/packages/qwik-router/src/runtime/src/qwik-router-component.tsx b/packages/qwik-router/src/runtime/src/qwik-router-component.tsx index 3ec51f06bc3..198c2a7f9ef 100644 --- a/packages/qwik-router/src/runtime/src/qwik-router-component.tsx +++ b/packages/qwik-router/src/runtime/src/qwik-router-component.tsx @@ -419,10 +419,10 @@ export const QwikRouterProvider = component$((props) => { // ensure correct trailing slash if (trackUrl.pathname.endsWith('/')) { - if (!qwikRouterConfig.trailingSlash) { + if (globalThis.__NO_TRAILING_SLASH__) { trackUrl.pathname = trackUrl.pathname.slice(0, -1); } - } else if (qwikRouterConfig.trailingSlash) { + } else if (!globalThis.__NO_TRAILING_SLASH__) { trackUrl.pathname += '/'; } let loadRoutePromise = loadRoute( diff --git a/packages/qwik-router/src/runtime/src/qwik-router-config.ts b/packages/qwik-router/src/runtime/src/qwik-router-config.ts index be9509c4b75..ca5c0487cea 100644 --- a/packages/qwik-router/src/runtime/src/qwik-router-config.ts +++ b/packages/qwik-router/src/runtime/src/qwik-router-config.ts @@ -4,7 +4,7 @@ import type { MenuData, RouteData, RouteModule } from './types'; export const routes: RouteData[] = []; export const menus: MenuData[] = []; export const serverPlugins: RouteModule[] = []; -export const trailingSlash = false; +export const trailingSlash = !globalThis.__NO_TRAILING_SLASH__; export const basePathname = '/'; export const cacheModules = false; diff --git a/packages/qwik-router/src/static/api-extractor.json b/packages/qwik-router/src/ssg/api-extractor.json similarity index 64% rename from packages/qwik-router/src/static/api-extractor.json rename to packages/qwik-router/src/ssg/api-extractor.json index b33eb2ebb8e..54855628a27 100644 --- a/packages/qwik-router/src/static/api-extractor.json +++ b/packages/qwik-router/src/ssg/api-extractor.json @@ -1,20 +1,20 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", "extends": "../api-extractor.json", - "mainEntryPointFilePath": "/../../dist-dev/dts-out/packages/qwik-router/src/static/index.d.ts", + "mainEntryPointFilePath": "/../../dist-dev/dts-out/packages/qwik-router/src/ssg/index.d.ts", "newlineKind": "lf", "apiReport": { "enabled": true, - "reportFileName": "qwik-router.static", - "reportFolder": "/src/static/", - "reportTempFolder": "/../../dist-dev/api-extractor/qwik-router/static" + "reportFileName": "qwik-router.ssg", + "reportFolder": "/src/ssg/", + "reportTempFolder": "/../../dist-dev/api-extractor/qwik-router/ssg" }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/lib/static/index.d.ts" + "untrimmedFilePath": "/lib/ssg/index.d.ts" }, "docModel": { "enabled": true, - "apiJsonFilePath": "/../../dist-dev/api/qwik-router/static/docs.api.json" + "apiJsonFilePath": "/../../dist-dev/api/qwik-router/ssg/docs.api.json" } } diff --git a/packages/qwik-router/src/ssg/deno/index.ts b/packages/qwik-router/src/ssg/deno/index.ts new file mode 100644 index 00000000000..caf758da9f6 --- /dev/null +++ b/packages/qwik-router/src/ssg/deno/index.ts @@ -0,0 +1,8 @@ +import type { SsgOptions } from '../types'; + +export async function generate(_opts: SsgOptions) { + console.error(`Deno not implemented`); + Deno.exit(1); +} + +declare const Deno: any; diff --git a/packages/qwik-router/src/static/extract-params.ts b/packages/qwik-router/src/ssg/extract-params.ts similarity index 100% rename from packages/qwik-router/src/static/extract-params.ts rename to packages/qwik-router/src/ssg/extract-params.ts diff --git a/packages/qwik-router/src/static/extract-params.unit.ts b/packages/qwik-router/src/ssg/extract-params.unit.ts similarity index 100% rename from packages/qwik-router/src/static/extract-params.unit.ts rename to packages/qwik-router/src/ssg/extract-params.unit.ts diff --git a/packages/qwik-router/src/static/index.ts b/packages/qwik-router/src/ssg/index.ts similarity index 74% rename from packages/qwik-router/src/static/index.ts rename to packages/qwik-router/src/ssg/index.ts index 046d1069e10..4bdcc82a8ea 100644 --- a/packages/qwik-router/src/static/index.ts +++ b/packages/qwik-router/src/ssg/index.ts @@ -1,10 +1,6 @@ -import type { - StaticGenerateOptions, - StaticGenerateRenderOptions, - StaticGenerateResult, -} from './types'; +import type { SsgOptions, SsgRenderOptions, SsgResult } from './types'; -// @qwik.dev/router/static +// @qwik.dev/router/ssg /** * Use this function when SSG should be generated from another module, such as a Vite plugin. This @@ -12,13 +8,17 @@ import type { * * @public */ -export async function generate(opts: StaticGenerateOptions) { +export async function generate(opts: SsgOptions) { const ssgPlatform = await getEntryModule(); - const result: StaticGenerateResult = await ssgPlatform.generate(opts); + const result: SsgResult = await ssgPlatform.generate(opts); return result; } -export type { StaticGenerateOptions, StaticGenerateRenderOptions, StaticGenerateResult }; +export type { + SsgOptions as StaticGenerateOptions, + SsgRenderOptions, + SsgResult as StaticGenerateResult, +}; function getEntryModulePath() { if (isDeno()) { diff --git a/packages/qwik-router/src/static/main-thread.ts b/packages/qwik-router/src/ssg/main-thread.ts similarity index 95% rename from packages/qwik-router/src/static/main-thread.ts rename to packages/qwik-router/src/ssg/main-thread.ts index a6e31c18e5c..0b9f93d50ef 100644 --- a/packages/qwik-router/src/static/main-thread.ts +++ b/packages/qwik-router/src/ssg/main-thread.ts @@ -9,7 +9,7 @@ import { getPathnameForDynamicRoute } from '../utils/pathname'; import { extractParamNames } from './extract-params'; import { generateNotFoundPages } from './not-found'; import { createRouteTester } from './routes'; -import type { StaticGenerateOptions, StaticGenerateResult, StaticRoute, System } from './types'; +import type { SsgOptions, SsgResult, SsgRoute, System } from './types'; export async function mainThread(sys: System) { const opts = sys.getOptions(); @@ -23,16 +23,16 @@ export async function mainThread(sys: System) { await import(pathToFileURL(opts.qwikRouterConfigModulePath).href) ).default; - const queue: StaticRoute[] = []; + const queue: SsgRoute[] = []; const active = new Set(); const routes = qwikRouterConfig.routes || []; const trailingSlash = !!qwikRouterConfig.trailingSlash; const includeRoute = createRouteTester(opts.basePathname || '/', opts.include, opts.exclude); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { const timer = sys.createTimer(); - const generatorResult: StaticGenerateResult = { + const generatorResult: SsgResult = { duration: 0, rendered: 0, errors: 0, @@ -102,7 +102,7 @@ export async function mainThread(sys: System) { } }; - const render = async (staticRoute: StaticRoute) => { + const render = async (staticRoute: SsgRoute) => { try { active.add(staticRoute.pathname); @@ -224,7 +224,7 @@ export async function mainThread(sys: System) { }); } -function validateOptions(opts: StaticGenerateOptions) { +function validateOptions(opts: SsgOptions) { if (!opts.qwikRouterConfigModulePath) { if (!opts.qwikCityPlanModulePath) { throw new Error(`Missing "qwikRouterConfigModulePath" option`); diff --git a/packages/qwik-router/src/static/node/index.ts b/packages/qwik-router/src/ssg/node/index.ts similarity index 83% rename from packages/qwik-router/src/static/node/index.ts rename to packages/qwik-router/src/ssg/node/index.ts index 4f31e3ab603..eab5c8d798f 100644 --- a/packages/qwik-router/src/static/node/index.ts +++ b/packages/qwik-router/src/ssg/node/index.ts @@ -1,10 +1,10 @@ -import type { StaticGenerateOptions } from '../types'; +import type { SsgOptions } from '../types'; import { createSystem } from './node-system'; import { isMainThread, workerData } from 'node:worker_threads'; import { mainThread } from '../main-thread'; import { workerThread } from '../worker-thread'; -export async function generate(opts: StaticGenerateOptions) { +export async function generate(opts: SsgOptions) { if (isMainThread) { const sys = await createSystem(opts); const result = await mainThread(sys); diff --git a/packages/qwik-router/src/static/node/node-main.ts b/packages/qwik-router/src/ssg/node/node-main.ts similarity index 90% rename from packages/qwik-router/src/static/node/node-main.ts rename to packages/qwik-router/src/ssg/node/node-main.ts index 4bf079ba0b6..88e4c89cd63 100644 --- a/packages/qwik-router/src/static/node/node-main.ts +++ b/packages/qwik-router/src/ssg/node/node-main.ts @@ -1,8 +1,8 @@ import type { MainContext, - StaticGenerateOptions, - StaticRoute, - StaticWorkerRenderResult, + SsgOptions, + SsgRoute, + SsgWorkerRenderResult, WorkerOutputMessage, WorkerInputMessage, System, @@ -15,8 +15,8 @@ import { ensureDir } from './node-system'; import { normalizePath } from '../../utils/fs'; import { createSingleThreadWorker } from '../worker-thread'; -export async function createNodeMainProcess(sys: System, opts: StaticGenerateOptions) { - const ssgWorkers: StaticGeneratorWorker[] = []; +export async function createNodeMainProcess(sys: System, opts: SsgOptions) { + const ssgWorkers: SsgWorker[] = []; const sitemapBuffer: string[] = []; let sitemapPromise: Promise | null = null; @@ -56,7 +56,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt const createWorker = (workerIndex: number) => { if (workerIndex === 0) { // same thread worker, don't start a new process - const ssgSameThreadWorker: StaticGeneratorWorker = { + const ssgSameThreadWorker: SsgWorker = { activeTasks: 0, totalTasks: 0, @@ -90,7 +90,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt const nodeWorker = new Worker(workerFilePath, { workerData: opts }); - const ssgWorker: StaticGeneratorWorker = { + const ssgWorker: SsgWorker = { activeTasks: 0, totalTasks: 0, @@ -161,10 +161,10 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt return ssgWorker.activeTasks < maxTasksPerWorker; }; - const render = async (staticRoute: StaticRoute) => { + const render = async (ssgRoute: SsgRoute) => { const ssgWorker = getNextWorker(); - const result = await ssgWorker.render(staticRoute); + const result = await ssgWorker.render(ssgRoute); if (sitemapOutFile && result.ok && result.resourceType === 'page') { sitemapBuffer.push(`${result.url}`); @@ -226,7 +226,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt return mainCtx; } -function ssgWorkerCompare(a: StaticGeneratorWorker, b: StaticGeneratorWorker) { +function ssgWorkerCompare(a: SsgWorker, b: SsgWorker) { if (a.activeTasks < b.activeTasks) { return -1; } @@ -236,11 +236,11 @@ function ssgWorkerCompare(a: StaticGeneratorWorker, b: StaticGeneratorWorker) { return a.totalTasks < b.totalTasks ? -1 : 1; } -type WorkerMainTask = (result: StaticWorkerRenderResult) => void; +type WorkerMainTask = (result: SsgWorkerRenderResult) => void; -interface StaticGeneratorWorker { +interface SsgWorker { activeTasks: number; totalTasks: number; - render: (staticRoute: StaticRoute) => Promise; + render: (staticRoute: SsgRoute) => Promise; terminate: () => Promise; } diff --git a/packages/qwik-router/src/static/node/node-system.ts b/packages/qwik-router/src/ssg/node/node-system.ts similarity index 95% rename from packages/qwik-router/src/static/node/node-system.ts rename to packages/qwik-router/src/ssg/node/node-system.ts index df138924232..79699dae295 100644 --- a/packages/qwik-router/src/static/node/node-system.ts +++ b/packages/qwik-router/src/ssg/node/node-system.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import type { StaticGenerateOptions, System } from '../types'; +import type { SsgOptions, System } from '../types'; import fs from 'node:fs'; import { dirname, join } from 'node:path'; import { createNodeMainProcess } from './node-main'; @@ -7,7 +7,7 @@ import { createNodeWorkerProcess } from './node-worker'; import { normalizePath } from '../../utils/fs'; /** @public */ -export async function createSystem(opts: StaticGenerateOptions) { +export async function createSystem(opts: SsgOptions) { const createWriteStream = (filePath: string) => { return fs.createWriteStream(filePath, { flags: 'w', diff --git a/packages/qwik-router/src/static/node/node-worker.ts b/packages/qwik-router/src/ssg/node/node-worker.ts similarity index 100% rename from packages/qwik-router/src/static/node/node-worker.ts rename to packages/qwik-router/src/ssg/node/node-worker.ts diff --git a/packages/qwik-router/src/static/not-found.ts b/packages/qwik-router/src/ssg/not-found.ts similarity index 82% rename from packages/qwik-router/src/static/not-found.ts rename to packages/qwik-router/src/ssg/not-found.ts index d29a7dc2ee1..7738ecede4c 100644 --- a/packages/qwik-router/src/static/not-found.ts +++ b/packages/qwik-router/src/ssg/not-found.ts @@ -1,13 +1,9 @@ import type { RouteData } from '@qwik.dev/router'; import { getErrorHtml } from '@qwik.dev/router/middleware/request-handler'; -import type { StaticGenerateOptions, System } from './types'; +import type { SsgOptions, System } from './types'; import { RouteDataProp } from '../runtime/src/types'; -export async function generateNotFoundPages( - sys: System, - opts: StaticGenerateOptions, - routes: RouteData[] -) { +export async function generateNotFoundPages(sys: System, opts: SsgOptions, routes: RouteData[]) { if (opts.emit404Pages !== false) { const basePathname = opts.basePathname || '/'; const rootNotFoundPathname = basePathname + '404.html'; diff --git a/packages/qwik-router/src/static/qwik-router.static.api.md b/packages/qwik-router/src/ssg/qwik-router.ssg.api.md similarity index 88% rename from packages/qwik-router/src/static/qwik-router.static.api.md rename to packages/qwik-router/src/ssg/qwik-router.ssg.api.md index d2f73332020..4d021772202 100644 --- a/packages/qwik-router/src/static/qwik-router.static.api.md +++ b/packages/qwik-router/src/ssg/qwik-router.ssg.api.md @@ -10,18 +10,7 @@ import type { RenderOptions } from '@qwik.dev/core/server'; export function generate(opts: StaticGenerateOptions): Promise; // @public (undocumented) -export interface StaticGenerateOptions extends StaticGenerateRenderOptions { - basePathname?: string; - // @deprecated (undocumented) - qwikCityPlanModulePath?: string; - qwikRouterConfigModulePath: string; - renderModulePath: string; - // (undocumented) - rootDir?: string; -} - -// @public (undocumented) -export interface StaticGenerateRenderOptions extends RenderOptions { +export interface SsgRenderOptions extends RenderOptions { emit404Pages?: boolean; emitData?: boolean; emitHtml?: boolean; @@ -35,6 +24,17 @@ export interface StaticGenerateRenderOptions extends RenderOptions { sitemapOutFile?: string | null; } +// @public (undocumented) +export interface StaticGenerateOptions extends SsgRenderOptions { + basePathname?: string; + // @deprecated (undocumented) + qwikCityPlanModulePath?: string; + qwikRouterConfigModulePath: string; + renderModulePath: string; + // (undocumented) + rootDir?: string; +} + // @public (undocumented) export interface StaticGenerateResult { // (undocumented) diff --git a/packages/qwik-router/src/static/routes.ts b/packages/qwik-router/src/ssg/routes.ts similarity index 100% rename from packages/qwik-router/src/static/routes.ts rename to packages/qwik-router/src/ssg/routes.ts diff --git a/packages/qwik-router/src/static/types.ts b/packages/qwik-router/src/ssg/types.ts similarity index 87% rename from packages/qwik-router/src/static/types.ts rename to packages/qwik-router/src/ssg/types.ts index 65f6e21acc5..0b616b68f09 100644 --- a/packages/qwik-router/src/static/types.ts +++ b/packages/qwik-router/src/ssg/types.ts @@ -8,7 +8,7 @@ export interface System { onMessage: (msg: WorkerInputMessage) => Promise ) => void; createLogger: () => Promise; - getOptions: () => StaticGenerateOptions; + getOptions: () => SsgOptions; ensureDir: (filePath: string) => Promise; access: (path: string) => Promise; createWriteStream: (filePath: string) => StaticStreamWriter; @@ -27,7 +27,7 @@ export interface StaticStreamWriter extends StreamWriter { export interface MainContext { hasAvailableWorker: () => boolean; - render: (staticRoute: StaticRenderInput) => Promise; + render: (staticRoute: SsgRenderInput) => Promise; close: () => Promise; } @@ -38,7 +38,7 @@ export interface Logger { } /** @public */ -export interface StaticGenerateRenderOptions extends RenderOptions { +export interface SsgRenderOptions extends RenderOptions { /** File system directory where the static files should be written. */ outDir: string; /** @@ -99,7 +99,7 @@ export interface StaticGenerateRenderOptions extends RenderOptions { } /** @public */ -export interface StaticGenerateOptions extends StaticGenerateRenderOptions { +export interface SsgOptions extends SsgRenderOptions { /** * Path to the SSR module exporting the default render function. In most cases it'll be * `./src/entry.ssr.tsx`. @@ -115,19 +115,17 @@ export interface StaticGenerateOptions extends StaticGenerateRenderOptions { rootDir?: string; } -export interface StaticGenerateHandlerOptions - extends StaticGenerateRenderOptions, - ServerRenderOptions {} +export interface SsgHandlerOptions extends SsgRenderOptions, ServerRenderOptions {} -export type WorkerInputMessage = StaticRenderInput | WorkerCloseMessage; +export type WorkerInputMessage = SsgRenderInput | WorkerCloseMessage; -export type WorkerOutputMessage = StaticWorkerRenderResult | WorkerCloseMessage; +export type WorkerOutputMessage = SsgWorkerRenderResult | WorkerCloseMessage; -export interface StaticRenderInput extends StaticRoute { +export interface SsgRenderInput extends SsgRoute { type: 'render'; } -export interface StaticRoute { +export interface SsgRoute { pathname: string; params: Record | undefined; } @@ -136,7 +134,7 @@ export interface WorkerCloseMessage { type: 'close'; } -export interface StaticWorkerRenderResult { +export interface SsgWorkerRenderResult { type: 'render'; pathname: string; url: string; @@ -148,7 +146,7 @@ export interface StaticWorkerRenderResult { } /** @public */ -export interface StaticGenerateResult { +export interface SsgResult { duration: number; rendered: number; errors: number; diff --git a/packages/qwik-router/src/static/worker-thread.ts b/packages/qwik-router/src/ssg/worker-thread.ts similarity index 94% rename from packages/qwik-router/src/static/worker-thread.ts rename to packages/qwik-router/src/ssg/worker-thread.ts index 497ea2dfb53..884a85b1ae3 100644 --- a/packages/qwik-router/src/static/worker-thread.ts +++ b/packages/qwik-router/src/ssg/worker-thread.ts @@ -6,10 +6,10 @@ import { pathToFileURL } from 'node:url'; import type { QwikSerializer } from '../middleware/request-handler/types'; import type { ClientPageData } from '../runtime/src/types'; import type { - StaticGenerateHandlerOptions, - StaticRoute, + SsgHandlerOptions, + SsgRoute, StaticStreamWriter, - StaticWorkerRenderResult, + SsgWorkerRenderResult, System, } from './types'; @@ -17,7 +17,7 @@ export async function workerThread(sys: System) { const ssgOpts = sys.getOptions(); const pendingPromises = new Set>(); - const opts: StaticGenerateHandlerOptions = { + const opts: SsgHandlerOptions = { ...ssgOpts, render: (await import(pathToFileURL(ssgOpts.renderModulePath).href)).default, qwikRouterConfig: (await import(pathToFileURL(ssgOpts.qwikRouterConfigModulePath).href)) @@ -27,7 +27,7 @@ export async function workerThread(sys: System) { sys.createWorkerProcess(async (msg) => { switch (msg.type) { case 'render': { - return new Promise((resolve) => { + return new Promise((resolve) => { workerRender(sys, opts, msg, pendingPromises, resolve); }); } @@ -45,15 +45,15 @@ export async function createSingleThreadWorker(sys: System) { const ssgOpts = sys.getOptions(); const pendingPromises = new Set>(); - const opts: StaticGenerateHandlerOptions = { + const opts: SsgHandlerOptions = { ...ssgOpts, render: (await import(pathToFileURL(ssgOpts.renderModulePath).href)).default, qwikRouterConfig: (await import(pathToFileURL(ssgOpts.qwikRouterConfigModulePath).href)) .default, }; - return (staticRoute: StaticRoute) => { - return new Promise((resolve) => { + return (staticRoute: SsgRoute) => { + return new Promise((resolve) => { workerRender(sys, opts, staticRoute, pendingPromises, resolve); }); }; @@ -61,10 +61,10 @@ export async function createSingleThreadWorker(sys: System) { async function workerRender( sys: System, - opts: StaticGenerateHandlerOptions, - staticRoute: StaticRoute, + opts: SsgHandlerOptions, + staticRoute: SsgRoute, pendingPromises: Set>, - callback: (result: StaticWorkerRenderResult) => void + callback: (result: SsgWorkerRenderResult) => void ) { const qwikSerializer: QwikSerializer = { _deserialize, @@ -74,7 +74,7 @@ async function workerRender( // pathname and origin already normalized at this point const url = new URL(staticRoute.pathname, opts.origin); - const result: StaticWorkerRenderResult = { + const result: SsgWorkerRenderResult = { type: 'render', pathname: staticRoute.pathname, url: url.href, diff --git a/packages/qwik-router/src/static/deno/index.ts b/packages/qwik-router/src/static/deno/index.ts deleted file mode 100644 index 1c62f3b6972..00000000000 --- a/packages/qwik-router/src/static/deno/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { StaticGenerateOptions } from '../types'; - -export async function generate(_opts: StaticGenerateOptions) { - console.error(`Deno not implemented`); - Deno.exit(1); -} - -declare const Deno: any; diff --git a/packages/qwik-router/src/utils/fs.ts b/packages/qwik-router/src/utils/fs.ts index c4bbb59af46..4ee726f340c 100644 --- a/packages/qwik-router/src/utils/fs.ts +++ b/packages/qwik-router/src/utils/fs.ts @@ -28,7 +28,7 @@ export function getPathnameFromDirPath(opts: NormalizedPluginOptions, dirPath: s // ensure file system path uses / (POSIX) instead of \\ (windows) let pathname = normalizePath(relFilePath); - pathname = normalizePathname(pathname, opts.basePathname, opts.trailingSlash)! + pathname = normalizePathname(pathname, opts.basePathname)! .split('/') // remove grouped layout segments .filter((segment) => !isGroupedLayoutName(segment)) @@ -42,7 +42,11 @@ export function getPathnameFromDirPath(opts: NormalizedPluginOptions, dirPath: s export function getMenuPathname(opts: NormalizedPluginOptions, filePath: string) { let pathname = normalizePath(relative(opts.routesDir, filePath)); pathname = `/` + normalizePath(dirname(pathname)); - return normalizePathname(pathname, opts.basePathname, true)!; + let result = normalizePathname(pathname, opts.basePathname)!; + if (!result.endsWith('/')) { + result += '/'; + } + return result; } export function getExtension(fileName: string) { diff --git a/packages/qwik-router/src/utils/fs.unit.ts b/packages/qwik-router/src/utils/fs.unit.ts index ade081e51c3..f69e52124b2 100644 --- a/packages/qwik-router/src/utils/fs.unit.ts +++ b/packages/qwik-router/src/utils/fs.unit.ts @@ -256,7 +256,6 @@ test('createFileId, Menu', () => { routesDir, serverPluginsDir, basePathname: t.basePathname, - trailingSlash: t.trailingSlash, mdxPlugins: { remarkGfm: true, rehypeSyntaxHighlight: true, @@ -267,6 +266,7 @@ test('createFileId, Menu', () => { rewriteRoutes: [], defaultLoadersSerializationStrategy: 'never', }; + globalThis.__NO_TRAILING_SLASH__ = !t.trailingSlash; const pathname = getPathnameFromDirPath(opts, t.dirPath); assert.equal(pathname, t.expect, t.dirPath); }); @@ -358,7 +358,6 @@ test('parseRouteIndexName', () => { routesDir, serverPluginsDir, basePathname: t.basePathname, - trailingSlash: t.trailingSlash, mdxPlugins: { remarkGfm: true, rehypeSyntaxHighlight: true, @@ -369,6 +368,7 @@ test('parseRouteIndexName', () => { rewriteRoutes: [], defaultLoadersSerializationStrategy: 'never', }; + globalThis.__NO_TRAILING_SLASH__ = !t.trailingSlash; const pathname = getMenuPathname(opts, t.filePath); assert.equal(pathname, t.expect); }); diff --git a/packages/qwik-router/src/utils/pathname.ts b/packages/qwik-router/src/utils/pathname.ts index 5fd688a1db3..42925235baf 100644 --- a/packages/qwik-router/src/utils/pathname.ts +++ b/packages/qwik-router/src/utils/pathname.ts @@ -1,10 +1,6 @@ import type { PathParams } from '../runtime/src'; -export function normalizePathname( - pathname: string | undefined | null, - basePathname: string, - trailingSlash: boolean -) { +export function normalizePathname(pathname: string | undefined | null, basePathname: string) { if (typeof pathname === 'string') { pathname = pathname.trim(); @@ -22,7 +18,7 @@ export function normalizePathname( pathname = new URL(basePathname + pathname, `https://qwik.dev`).pathname; if (pathname !== basePathname) { - if (trailingSlash) { + if (!globalThis.__NO_TRAILING_SLASH__) { if (!pathname.endsWith('/')) { const segments = pathname.split('/'); const lastSegment = segments[segments.length - 1]; diff --git a/packages/qwik-router/src/utils/pathname.unit.ts b/packages/qwik-router/src/utils/pathname.unit.ts index 9d497012070..b31fc6808a0 100644 --- a/packages/qwik-router/src/utils/pathname.unit.ts +++ b/packages/qwik-router/src/utils/pathname.unit.ts @@ -115,7 +115,9 @@ test('normalizePathname', () => { ]; tests.forEach((t) => { - const pathname = normalizePathname(t.pathname, t.basePathname, t.trailingSlash); + globalThis.__NO_TRAILING_SLASH__ = !t.trailingSlash; + const pathname = normalizePathname(t.pathname, t.basePathname); + assert.equal(pathname, t.expect); }); }); @@ -179,5 +181,5 @@ test('dynamic pathname', () => { function getPathname(t: { originalPathname: string; basePathname: string; params?: PathParams }) { const p = parseRoutePathname(t.basePathname, t.originalPathname); const d = getPathnameForDynamicRoute(t.originalPathname, p.paramNames, t.params); - return normalizePathname(d, '/', false); + return normalizePathname(d, '/'); } diff --git a/packages/qwik-router/ssg.d.ts b/packages/qwik-router/ssg.d.ts new file mode 100644 index 00000000000..00861d96fd8 --- /dev/null +++ b/packages/qwik-router/ssg.d.ts @@ -0,0 +1,2 @@ +// re-export for typescript in old resolution mode +export * from './lib/ssg'; diff --git a/packages/qwik-router/static.d.ts b/packages/qwik-router/static.d.ts index b61c4bfedaa..00861d96fd8 100644 --- a/packages/qwik-router/static.d.ts +++ b/packages/qwik-router/static.d.ts @@ -1,2 +1,2 @@ // re-export for typescript in old resolution mode -export * from './lib/static'; +export * from './lib/ssg'; diff --git a/packages/qwik-router/tsconfig.json b/packages/qwik-router/tsconfig.json index a60aee6ff5b..8e931a3c81d 100644 --- a/packages/qwik-router/tsconfig.json +++ b/packages/qwik-router/tsconfig.json @@ -21,9 +21,7 @@ "@qwik.dev/router/adapters/shared/vite": [ "packages/qwik-router/src/adapters/shared/vite/index.ts" ], - "@qwik.dev/router/adapters/static/vite": [ - "packages/qwik-router/src/adapters/static/vite/index.ts" - ], + "@qwik.dev/router/adapters/ssg/vite": ["packages/qwik-router/src/adapters/ssg/vite/index.ts"], "@qwik.dev/router/middleware/azure-swa": ["packages/qwik-router/src/middleware/azure-swa"], "@qwik.dev/router/middleware/aws-lambda": ["packages/qwik-router/src/middleware/aws-lambda"], "@qwik.dev/router/middleware/cloudflare-pages": [ @@ -36,7 +34,7 @@ "@qwik.dev/router/middleware/netlify-edge": [ "packages/qwik-router/src/middleware/netlify-edge" ], - "@qwik.dev/router/static": ["packages/qwik-router/src/static"], + "@qwik.dev/router/ssg": ["packages/qwik-router/src/ssg"], "@qwik.dev/router/vite": ["packages/qwik-router/src/buildtime/vite"], "@qwik-router-config": ["packages/qwik-router/src/runtime/src/qwik-router-config.ts"], diff --git a/packages/qwik/src/optimizer/src/index.ts b/packages/qwik/src/optimizer/src/index.ts index 55bd8c0e25e..88f0af0a6cd 100644 --- a/packages/qwik/src/optimizer/src/index.ts +++ b/packages/qwik/src/optimizer/src/index.ts @@ -52,4 +52,5 @@ export type { BundleGraphAdder } from './plugins/bundle-graph'; export { qwikRollup } from './plugins/rollup'; export { qwikVite } from './plugins/vite'; -export { symbolMapper } from './plugins/vite-dev-server'; +/** @alpha @deprecated No longer needed, it is automatic now */ +export const symbolMapper = undefined; diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index 7bdb3dc7ff1..6f63e811949 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -76,7 +76,7 @@ export enum ExperimentalFeatures { enableRequestRewrite = 'enableRequestRewrite', /** Enable worker$ */ webWorker = 'webWorker', - /** Enable the ability to use the Qwik Insights vite plugin and component */ + /** Enable the ability to use the Qwik Insights vite plugin and `` component */ insights = 'insights', } @@ -107,15 +107,16 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { target: 'client', buildMode: 'development', debug: false, - rootDir: null as any, + rootDir: undefined as any, tsconfigFileNames: ['./tsconfig.json'], - input: null as any, - outDir: '', - assetsDir: null as any, + input: undefined as any, + outDir: undefined as any, + assetsDir: undefined as any, resolveQwikBuild: true, - entryStrategy: null as any, - srcDir: null as any, - srcInputs: null as any, + entryStrategy: undefined as any, + srcDir: undefined as any, + ssrOutDir: undefined as any, + clientOutDir: undefined as any, sourcemap: !!optimizerOptions.sourcemap, manifestInput: null, manifestOutput: null, @@ -131,10 +132,16 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { }; let lazyNormalizePath: (id: string) => string; + let maybeFs: typeof import('fs') | undefined; const init = async () => { if (!internalOptimizer) { internalOptimizer = await createOptimizer(optimizerOptions); lazyNormalizePath = makeNormalizePath(internalOptimizer.sys); + try { + maybeFs = await internalOptimizer.sys.dynamicImport('node:fs'); + } catch { + // ignore + } } }; @@ -161,7 +168,9 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { }; /** Note that as a side-effect this updates the internal plugin `opts` */ - const normalizeOptions = (inputOpts?: QwikPluginOptions) => { + const normalizeOptions = async ( + inputOpts?: QwikPluginOptions + ): Promise => { const updatedOpts: QwikPluginOptions = Object.assign({}, inputOpts); const optimizer = getOptimizer(); @@ -220,81 +229,51 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { if (typeof updatedOpts.srcDir === 'string') { opts.srcDir = normalizePath(path.resolve(opts.rootDir, updatedOpts.srcDir)); srcDir = opts.srcDir; - opts.srcInputs = null; - } else if (Array.isArray(updatedOpts.srcInputs)) { - opts.srcInputs = [...updatedOpts.srcInputs]; - opts.srcDir = null; } else { opts.srcDir ||= srcDir; } + opts.srcDir = normalizePath(path.resolve(opts.rootDir, normalizePath(opts.srcDir))); if (Array.isArray(updatedOpts.tsconfigFileNames) && updatedOpts.tsconfigFileNames.length > 0) { opts.tsconfigFileNames = updatedOpts.tsconfigFileNames; } - if (Array.isArray(opts.srcInputs)) { - opts.srcInputs.forEach((i) => { - i.path = normalizePath(path.resolve(opts.rootDir, i.path)); - }); - } else if (typeof opts.srcDir === 'string') { - opts.srcDir = normalizePath(path.resolve(opts.rootDir, normalizePath(opts.srcDir))); + if (!updatedOpts.input && !opts.input) { + // we only provide inputs if none were provided by the user + if (opts.target === 'ssr') { + // this is for dev mode, prod will have own setting + opts.input = [path.resolve(srcDir, 'entry.ssr')]; + } else if (opts.target === 'client') { + // not really an entry, just a starting point + opts.input = [path.resolve(srcDir, 'root')]; + } else { + // others including lib should be ok already + opts.input = undefined!; + } } if (!updatedOpts.csr) { - if (Array.isArray(updatedOpts.input)) { - opts.input = [...updatedOpts.input]; - } else if (typeof updatedOpts.input === 'string') { - opts.input = [updatedOpts.input]; - } else { - if (opts.target === 'ssr') { - // ssr input default - opts.input ||= [path.resolve(srcDir, 'entry.ssr')]; - } else if (opts.target === 'client') { - // client input default - opts.input ||= [path.resolve(srcDir, 'root')]; - } else if (opts.target === 'lib') { - if (typeof updatedOpts.input === 'object') { - for (const key in updatedOpts.input) { - const resolvedPaths: { [key: string]: string } = {}; - if (Object.hasOwnProperty.call(updatedOpts.input, key)) { - const relativePath = updatedOpts.input[key]; - const absolutePath = path.resolve(opts.rootDir, relativePath); - resolvedPaths[key] = absolutePath; - } - - opts.input = { ...opts.input, ...resolvedPaths }; - } - } else { - // lib input default - opts.input ||= [path.resolve(srcDir, 'index.ts')]; - } - } else { - opts.input ||= []; - } - } - if (Array.isArray(opts.input)) { - opts.input = opts.input.reduce((inputs, i) => { - let input = i; - if (!i.startsWith('@') && !i.startsWith('~') && !i.startsWith('#')) { - input = normalizePath(path.resolve(opts.rootDir, i)); - } - if (!inputs.includes(input)) { - inputs.push(input); - } - return inputs; - }, [] as string[]); + if (updatedOpts.outDir) { + // forced output directory + opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir))); } - if (typeof updatedOpts.outDir === 'string') { - opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir))); - } else if (!opts.outDir) { - if (opts.target === 'ssr') { - opts.outDir = normalizePath(path.resolve(opts.rootDir, SSR_OUT_DIR)); - } else if (opts.target === 'lib') { - opts.outDir = normalizePath(path.resolve(opts.rootDir, LIB_OUT_DIR)); - } else { - opts.outDir = normalizePath(path.resolve(opts.rootDir, CLIENT_OUT_DIR)); - } + // default output directory + opts.clientOutDir = normalizePath( + path.resolve(opts.rootDir, updatedOpts.clientOutDir || CLIENT_OUT_DIR) + ); + opts.ssrOutDir = normalizePath( + path.resolve(opts.rootDir, updatedOpts.ssrOutDir || SSR_OUT_DIR) + ); + if (opts.target === 'ssr') { + // server + opts.outDir ||= opts.ssrOutDir; + } else if (opts.target === 'lib') { + // library + opts.outDir ||= normalizePath(path.resolve(opts.rootDir, LIB_OUT_DIR)); + } else { + // client + opts.outDir ||= opts.clientOutDir; } } @@ -302,9 +281,31 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { opts.manifestOutput = updatedOpts.manifestOutput; } - const clientManifest = getValidManifest(updatedOpts.manifestInput); - if (clientManifest) { - opts.manifestInput = clientManifest; + if (updatedOpts.manifestInput) { + opts.manifestInput = getValidManifest(updatedOpts.manifestInput) || null; + } + if ( + !opts.manifestInput && + opts.target === 'ssr' && + opts.buildMode === 'production' && + maybeFs + ) { + let clientManifestPath = normalizePath(path.resolve(opts.clientOutDir, Q_MANIFEST_FILENAME)); + if (!(await maybeFs.promises.stat(clientManifestPath).catch(() => false))) { + clientManifestPath = normalizePath( + path.resolve(opts.rootDir, CLIENT_OUT_DIR, Q_MANIFEST_FILENAME) + ); + } + try { + const clientManifestStr = await maybeFs.promises.readFile(clientManifestPath, 'utf-8'); + opts.manifestInput = getValidManifest(JSON.parse(clientManifestStr)) || null; + // eslint-disable-next-line no-console + console.info('Read client manifest from', clientManifestPath); + } catch (e) { + console.warn( + `could not read Qwik client manifest ${clientManifestPath}, ignoring. Make sure you provide it to the SSR renderer. (${e})` + ); + } } if (typeof updatedOpts.transformedModuleOutput === 'function') { @@ -313,6 +314,36 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { if (updatedOpts.scope !== undefined) { opts.scope = updatedOpts.scope; + } else if (!opts.scope && maybeFs) { + // Use the package name for the scope + let pkgPath = ''; + try { + let pkgDir = opts.rootDir; + while (true) { + pkgPath = path.resolve(pkgDir, 'package.json'); + if (await maybeFs.promises.stat(pkgPath).catch(() => false)) { + break; + } + const parent = path.resolve(pkgDir, '..'); + if (parent === pkgDir) { + break; + } + pkgDir = parent; + pkgPath = ''; + } + + if (pkgPath) { + const pkgString = await maybeFs.promises.readFile(pkgPath, 'utf-8'); + const pkg = JSON.parse(pkgString); + if (typeof pkg.name === 'string') { + opts.scope = pkg.name; + } + } + } catch (e) { + console.warn( + `could not read ${pkgPath || 'package.json'} to determine package name, ignoring. (${e})` + ); + } } if (typeof updatedOpts.resolveQwikBuild === 'boolean') { @@ -395,20 +426,6 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { } } - const path = getPath(); - - if (Array.isArray(opts.srcInputs)) { - optimizer.sys.getInputFiles = async (rootDir) => - opts.srcInputs!.map((i) => { - const relInput: TransformModuleInput = { - path: normalizePath(path.relative(rootDir, i.path)), - code: i.code, - }; - return relInput; - }); - debug(`buildStart() opts.srcInputs (${opts.srcInputs.length} files)`); - } - debug(`transformedOutputs.clear()`); clientTransformedOutputs.clear(); serverTransformedOutputs.clear(); @@ -918,6 +935,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) { diagnosticsCallback = cb; }; + /** Convert windows backslashes to forward slashes if possible */ const normalizePath = (id: string) => lazyNormalizePath(id); function getQwikBuildModule(isServer: boolean, _target: QwikBuildTarget) { @@ -1056,9 +1074,6 @@ export const manifest = ${JSON.stringify(serverManifest)};\n`; if (typeof opts.transformedModuleOutput === 'function') { await opts.transformedModuleOutput(getTransformedOutputs()); } - - // TODO get rid of this with the vite environment api - return manifestStr; } return { @@ -1174,9 +1189,12 @@ export interface QwikPluginOptions { manifestInput?: QwikManifest | null; input?: string[] | string | { [entry: string]: string }; outDir?: string; + ssrOutDir?: string; + clientOutDir?: string; assetsDir?: string; srcDir?: string | null; scope?: string | null; + /** @deprecated Not used */ srcInputs?: TransformModuleInput[] | null; sourcemap?: boolean; resolveQwikBuild?: boolean; @@ -1204,9 +1222,12 @@ export interface QwikPluginOptions { } export interface NormalizedQwikPluginOptions - extends Omit, 'vendorRoots' | 'experimental'> { - input: string[] | { [entry: string]: string }; - experimental?: Record; + extends Omit< + Required, + 'input' | 'vendorRoots' | 'srcInputs' | 'experimental' + > { + input: string[] | { [entry: string]: string } | undefined; + experimental: Record | undefined; } export type QwikPlugin = ReturnType; diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts b/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts index 1255f59811e..d37a262c1cf 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.unit.ts @@ -15,7 +15,7 @@ test('types', () => () => { test('defaults', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions(); + const opts = await plugin.normalizeOptions(); assert.deepEqual(opts.target, 'client'); assert.deepEqual(opts.buildMode, 'development'); assert.deepEqual(opts.entryStrategy, { type: 'segment' }); @@ -27,12 +27,11 @@ test('defaults', async () => { assert.deepEqual(opts.manifestInput, null); assert.deepEqual(opts.manifestOutput, null); assert.deepEqual(opts.srcDir, normalizePath(resolve(cwd, 'src'))); - assert.deepEqual(opts.srcInputs, null); }); test('defaults (buildMode: production)', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ buildMode: 'production' }); + const opts = await plugin.normalizeOptions({ buildMode: 'production' }); assert.deepEqual(opts.target, 'client'); assert.deepEqual(opts.buildMode, 'production'); assert.deepEqual(opts.entryStrategy, { type: 'smart' }); @@ -45,13 +44,12 @@ test('defaults (buildMode: production)', async () => { assert.deepEqual(opts.manifestInput, null); assert.deepEqual(opts.manifestOutput, null); assert.deepEqual(opts.srcDir, normalizePath(resolve(cwd, 'src'))); - assert.deepEqual(opts.srcInputs, null); assert.deepEqual(opts.entryStrategy, { type: 'smart' }); }); test('defaults (target: ssr)', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ target: 'ssr' }); + const opts = await plugin.normalizeOptions({ target: 'ssr' }); assert.deepEqual(opts.target, 'ssr'); assert.deepEqual(opts.buildMode, 'development'); assert.deepEqual(opts.entryStrategy, { type: 'hoist' }); @@ -64,12 +62,11 @@ test('defaults (target: ssr)', async () => { assert.deepEqual(opts.manifestInput, null); assert.deepEqual(opts.manifestOutput, null); assert.deepEqual(opts.srcDir, normalizePath(resolve(cwd, 'src'))); - assert.deepEqual(opts.srcInputs, null); }); test('defaults (buildMode: production, target: ssr)', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ buildMode: 'production', target: 'ssr' }); + const opts = await plugin.normalizeOptions({ buildMode: 'production', target: 'ssr' }); assert.deepEqual(opts.target, 'ssr'); assert.deepEqual(opts.buildMode, 'production'); assert.deepEqual(opts.entryStrategy, { type: 'hoist' }); @@ -82,24 +79,23 @@ test('defaults (buildMode: production, target: ssr)', async () => { assert.deepEqual(opts.manifestInput, null); assert.deepEqual(opts.manifestOutput, null); assert.deepEqual(opts.srcDir, normalizePath(resolve(cwd, 'src'))); - assert.deepEqual(opts.srcInputs, null); }); test('debug true', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ debug: true }); + const opts = await plugin.normalizeOptions({ debug: true }); assert.deepEqual(opts.debug, true); }); test('csr', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ csr: true }); - assert.deepEqual(opts.outDir, ''); + const opts = await plugin.normalizeOptions({ csr: true }); + assert.deepEqual(opts.outDir, undefined); }); test('override entryStrategy', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ entryStrategy: { type: 'component' }, buildMode: 'production', }); @@ -108,7 +104,7 @@ test('override entryStrategy', async () => { test('entryStrategy, smart', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ entryStrategy: { type: 'smart' }, }); assert.deepEqual(opts.entryStrategy.type, 'smart'); @@ -116,15 +112,14 @@ test('entryStrategy, smart', async () => { test('entryStrategy, segment no forceFullBuild', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ entryStrategy: { type: 'segment' } }); + const opts = await plugin.normalizeOptions({ entryStrategy: { type: 'segment' } }); assert.deepEqual(opts.entryStrategy.type, 'segment'); }); test('entryStrategy, segment and srcInputs', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ entryStrategy: { type: 'segment' }, - srcInputs: [], }); assert.deepEqual(opts.entryStrategy.type, 'segment'); }); @@ -132,20 +127,20 @@ test('entryStrategy, segment and srcInputs', async () => { test('rootDir, abs path', async () => { const plugin = await mockPlugin(); const customRoot = normalizePath(resolve(cwd, 'abs-path')); - const opts = plugin.normalizeOptions({ rootDir: customRoot }); + const opts = await plugin.normalizeOptions({ rootDir: customRoot }); assert.deepEqual(opts.rootDir, customRoot); }); test('rootDir, rel path', async () => { const plugin = await mockPlugin(); const customRoot = 'rel-path'; - const opts = plugin.normalizeOptions({ rootDir: customRoot }); + const opts = await plugin.normalizeOptions({ rootDir: customRoot }); assert.deepEqual(opts.rootDir, normalizePath(resolve(cwd, customRoot))); }); test('tsconfigFileNames', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ tsconfigFileNames: ['./tsconfig.json', './tsconfig.app.json'], }); assert.deepEqual(opts.tsconfigFileNames, ['./tsconfig.json', './tsconfig.app.json']); @@ -153,7 +148,7 @@ test('tsconfigFileNames', async () => { test('tsconfigFileNames, empty array fallback to default', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ tsconfigFileNames: [], }); assert.deepEqual(opts.tsconfigFileNames, ['./tsconfig.json']); @@ -161,31 +156,30 @@ test('tsconfigFileNames, empty array fallback to default', async () => { test('input string', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ input: 'src/cmps/main.tsx' }); - assert.deepEqual(opts.input, [normalizePath(resolve(cwd, 'src', 'cmps', 'main.tsx'))]); + const opts = await plugin.normalizeOptions({ input: 'src/cmps/main.tsx' }); + // we don't provide input so that we don't override the vite input + assert.deepEqual(opts.input, undefined); }); test('input array', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ + const opts = await plugin.normalizeOptions({ input: ['src/cmps/a.tsx', 'src/cmps/b.tsx'], }); - assert.deepEqual(opts.input, [ - normalizePath(resolve(cwd, 'src', 'cmps', 'a.tsx')), - normalizePath(resolve(cwd, 'src', 'cmps', 'b.tsx')), - ]); + // we don't provide input so that we don't override the vite input + assert.deepEqual(opts.input, undefined); }); test('outDir', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ outDir: 'out' }); + const opts = await plugin.normalizeOptions({ outDir: 'out' }); assert.deepEqual(opts.outDir, normalizePath(resolve(cwd, 'out'))); }); test('manifestOutput', async () => { const plugin = await mockPlugin(); const manifestOutput = () => {}; - const opts = plugin.normalizeOptions({ manifestOutput }); + const opts = await plugin.normalizeOptions({ manifestOutput }); assert.deepEqual(opts.manifestOutput, manifestOutput); }); @@ -198,19 +192,19 @@ test('manifestInput', async () => { bundles: {}, version: '1', }; - const opts = plugin.normalizeOptions({ manifestInput }); + const opts = await plugin.normalizeOptions({ manifestInput }); assert.deepEqual(opts.manifestInput, manifestInput); }); test('resolveQwikBuild true', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ resolveQwikBuild: true }); + const opts = await plugin.normalizeOptions({ resolveQwikBuild: true }); assert.deepEqual(opts.resolveQwikBuild, true); }); test('resolveQwikBuild false', async () => { const plugin = await mockPlugin(); - const opts = plugin.normalizeOptions({ resolveQwikBuild: false }); + const opts = await plugin.normalizeOptions({ resolveQwikBuild: false }); assert.deepEqual(opts.resolveQwikBuild, false); }); @@ -221,7 +215,7 @@ test('experimental[]', async () => { // we can't test this without a flag return; } - const opts = plugin.normalizeOptions({ experimental: [flag] }); + const opts = await plugin.normalizeOptions({ experimental: [flag] }); assert.deepEqual(opts.experimental, { [flag]: true } as any); }); diff --git a/packages/qwik/src/optimizer/src/plugins/rollup.ts b/packages/qwik/src/optimizer/src/plugins/rollup.ts index 545956f35ec..5c38b5a1bb6 100644 --- a/packages/qwik/src/optimizer/src/plugins/rollup.ts +++ b/packages/qwik/src/optimizer/src/plugins/rollup.ts @@ -66,9 +66,9 @@ export function qwikRollup(qwikRollupOpts: QwikRollupPluginOptions = {}): any { experimental: qwikRollupOpts.experimental, }; - const opts = qwikPlugin.normalizeOptions(pluginOpts); + const opts = await qwikPlugin.normalizeOptions(pluginOpts); - if (!inputOpts.input) { + if (opts.input) { inputOpts.input = opts.input; } @@ -151,6 +151,35 @@ export function normalizeRollupOutputOptions( }; } +const getChunkFileName = ( + prefix: string, + opts: NormalizedQwikPluginOptions, + optimizer: Optimizer +) => { + if (opts.buildMode === 'production' && !opts.debug) { + return `${prefix}build/q-[hash].js`; + } else { + // Friendlier names in dev or preview with debug mode + return (chunkInfo: Rollup.PreRenderedChunk) => { + if (chunkInfo.moduleIds?.some((id) => id.endsWith('core.prod.mjs'))) { + return `${prefix}build/core.js`; + } + if (chunkInfo.moduleIds?.some((id) => id.endsWith('qwik-router/lib/index.qwik.mjs'))) { + return `${prefix}build/qwik-router.js`; + } + + // The chunk name can often be a path. We sanitize it to use dashes instead of slashes, to keep the same folder structure as without debug:true. + // Besides, Rollup doesn't accept absolute or relative paths as inputs for the [name] placeholder for the same reason. + const relativePath = optimizer.sys.path.relative(optimizer.sys.cwd(), chunkInfo.name); + const sanitized = relativePath + .replace(/^(\.\.\/)+/, '') + .replace(/^\/+/, '') + .replace(/\//g, '-'); + return `${prefix}build/${sanitized}.js`; + }; + } +}; + export function normalizeRollupOutputOptionsObject( qwikPlugin: QwikPlugin, rollupOutputOptsObj: Rollup.OutputOptions | undefined, @@ -160,79 +189,40 @@ export function normalizeRollupOutputOptionsObject( const opts = qwikPlugin.getOptions(); const optimizer = qwikPlugin.getOptimizer(); const manualChunks = qwikPlugin.manualChunks; - if (opts.target === 'client') { - // client output - if (!outputOpts.assetFileNames) { - // SEO likes readable asset names - const assetFileNames = 'assets/[hash]-[name].[ext]'; - outputOpts.assetFileNames = useAssetsDir - ? `${opts.assetsDir}/${assetFileNames}` - : assetFileNames; - } - let fileName: string | ((chunkInfo: Rollup.PreRenderedChunk) => string) | undefined; - if (opts.buildMode === 'production' && !opts.debug) { - fileName = 'build/q-[hash].js'; - } else { - // Friendlier names in dev or preview with debug mode - fileName = (chunkInfo) => { - if (chunkInfo.moduleIds?.some((id) => id.endsWith('core.prod.mjs'))) { - return 'build/core.js'; - } - if (chunkInfo.moduleIds?.some((id) => id.endsWith('qwik-router/lib/index.qwik.mjs'))) { - return 'build/qwik-router.js'; - } - - // The chunk name can often be a path. We sanitize it to use dashes instead of slashes, to keep the same folder structure as without debug:true. - // Besides, Rollup doesn't accept absolute or relative paths as inputs for the [name] placeholder for the same reason. - const path = optimizer.sys.path; - const relativePath = path.relative(optimizer.sys.cwd(), chunkInfo.name); - const sanitized = relativePath - .replace(/^(\.\.\/)+/, '') - .replace(/^\/+/, '') - .replace(/\//g, '-'); - return `build/${sanitized}.js`; - }; - } - // client development/debug output - const getFilePath = (fileNamePattern: string | ((info: Rollup.PreRenderedChunk) => string)) => - typeof fileNamePattern === 'string' - ? useAssetsDir - ? `${opts.assetsDir}/${fileNamePattern}` - : fileNamePattern - : useAssetsDir - ? (chunkInfo: Rollup.PreRenderedChunk) => - `${opts.assetsDir}/${fileNamePattern(chunkInfo)}` - : (chunkInfo: Rollup.PreRenderedChunk) => fileNamePattern(chunkInfo); + if (!outputOpts.assetFileNames) { + // SEO likes readable asset names + // assetsDir allows assets to be in a deeper directory for serving, e.g. Astro + outputOpts.assetFileNames = `${useAssetsDir ? `${opts.assetsDir}/` : ''}assets/[hash]-[name].[ext]`; + } + const chunkFileName = getChunkFileName(useAssetsDir ? `${opts.assetsDir}` : '', opts, optimizer); + if (opts.target === 'client') { + // client output if (!outputOpts.entryFileNames) { - outputOpts.entryFileNames = getFilePath(fileName); + // we don't treat entries specially for the client + outputOpts.entryFileNames = chunkFileName; } if (!outputOpts.chunkFileNames) { - outputOpts.chunkFileNames = getFilePath(fileName); + outputOpts.chunkFileNames = chunkFileName; } - } else if (opts.buildMode === 'production') { - // server production output - // everything in same dir so './@qwik-router...' imports work from entry and chunks - if (!outputOpts.chunkFileNames) { - outputOpts.chunkFileNames = 'q-[hash].js'; - } - } - // all other cases, like lib output - if (!outputOpts.assetFileNames) { - outputOpts.assetFileNames = 'assets/[hash]-[name].[ext]'; - } - if (opts.target === 'client') { // client should always be es outputOpts.format = 'es'; const prevManualChunks = outputOpts.manualChunks; if (prevManualChunks && typeof prevManualChunks !== 'function') { throw new Error('manualChunks must be a function'); } + + // We need custom chunking for the client build outputOpts.manualChunks = prevManualChunks ? (id, meta) => prevManualChunks(id, meta) || manualChunks(id, meta) : manualChunks; + } else { + // server production output, try to be similar to client + if (!outputOpts.chunkFileNames) { + outputOpts.chunkFileNames = chunkFileName; + } } if (!outputOpts.dir) { diff --git a/packages/qwik/src/optimizer/src/plugins/rollup.unit.ts b/packages/qwik/src/optimizer/src/plugins/rollup.unit.ts index d0187bdae81..523ff42106f 100644 --- a/packages/qwik/src/optimizer/src/plugins/rollup.unit.ts +++ b/packages/qwik/src/optimizer/src/plugins/rollup.unit.ts @@ -58,11 +58,10 @@ test('rollup default set input options, ssr', async () => { input: normalizePath(resolve(cwd, 'src', 'my.ssr.tsx')), }); const opts: NormalizedQwikPluginOptions = plugin.api.getOptions(); - assert.deepEqual(typeof rollupInputOpts.onwarn, 'function'); assert.deepEqual(rollupInputOpts.treeshake, undefined); assert.deepEqual(rollupInputOpts.input, normalizePath(resolve(cwd, 'src', 'my.ssr.tsx'))); - assert.deepEqual(opts.input, [normalizePath(resolve(cwd, 'src', 'my.ssr.tsx'))]); + assert.deepEqual(opts.input, undefined); }); test('rollup default output options, client', async () => { 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 bf4cc7e2434..21d4dbefda7 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite-dev-server.ts @@ -4,22 +4,15 @@ import type { IncomingMessage, ServerResponse } from 'http'; import { magenta } from 'kleur/colors'; import type { Connect, ViteDevServer } from 'vite'; -import type { - OptimizerSystem, - Path, - ServerQwikManifest, - SymbolMapper, - SymbolMapperFn, -} from '../types'; +import type { OptimizerSystem, Path, ServerQwikManifest } from '../types'; import clickToComponent from './click-to-component.html?raw'; import errorHost from './error-host.html?raw'; import imageDevTools from './image-size-runtime.html?raw'; import perfWarning from './perf-warning.html?raw'; -import { type NormalizedQwikPluginOptions, QWIK_HANDLERS_ID } from './plugin'; +import { type NormalizedQwikPluginOptions } from './plugin'; import type { QwikViteDevResponse } from './vite'; import { VITE_ERROR_OVERLAY_STYLES } from './vite-error'; import { formatError, parseId } from './vite-utils'; -import { SYNC_QRL } from 'packages/qwik/src/core/shared/qrl/qrl-utils'; function getOrigin(req: IncomingMessage) { const { PROTOCOL_HEADER, HOST_HEADER } = process.env; @@ -33,50 +26,6 @@ function getOrigin(req: IncomingMessage) { return `${protocol}://${host}`; } -function createSymbolMapper(base: string): SymbolMapperFn { - return ( - symbolName: string, - _mapper: SymbolMapper | undefined, - parent: string | undefined - ): [string, string] => { - if (symbolName === SYNC_QRL) { - return [symbolName, '']; - } - if (!parent) { - // Core symbols - if (symbolName.startsWith('_')) { - return [symbolName, `${base}${QWIK_HANDLERS_ID}`]; - } - console.error( - 'qwik vite-dev-server symbolMapper: unknown qrl requested without parent:', - symbolName - ); - return [symbolName, `${base}${symbolName}.js`]; - } - // In dev mode, the `parent` is the Vite URL for the parent, not the real absolute path. - // It is always absolute but when on Windows that's without a / - const qrlFile = `${base}${parent.startsWith('/') ? parent.slice(1) : parent}_${symbolName}.js`; - return [symbolName, qrlFile]; - }; -} - -let lazySymbolMapper: ReturnType | null = null; -/** - * @beta - * For a given symbol (QRL such as `onKeydown$`) the server needs to know which bundle the symbol is in. - * - * Normally this is provided by Qwik's `q-manifest` . But `q-manifest` only exists after a full client build. - * - * This would be a problem in dev mode. So in dev mode the symbol is mapped to the expected URL using the symbolMapper function below. For Vite the given path is fixed for a given symbol. - */ -export let symbolMapper: ReturnType = (symbolName, mapper, parent) => { - // This is a fallback in case the symbolMapper is copied early - if (lazySymbolMapper) { - return lazySymbolMapper(symbolName, mapper, parent); - } - throw new Error('symbolMapper not initialized'); -}; - export async function configureDevServer( base: string, server: ViteDevServer, @@ -84,14 +33,8 @@ export async function configureDevServer( sys: OptimizerSystem, path: Path, isClientDevOnly: boolean, - clientDevInput: string | undefined, - devSsrServer: boolean + clientDevInput: string | undefined ) { - symbolMapper = lazySymbolMapper = createSymbolMapper(base); - if (!devSsrServer) { - // we just needed the symbolMapper - return; - } const hasQwikRouter = server.config.plugins?.some( (plugin) => plugin.name === 'vite-plugin-qwik-router' ); @@ -140,6 +83,14 @@ export async function configureDevServer( } const firstInput = opts.input && Object.values(opts.input)[0]; + if (!firstInput) { + console.error(`no entry found for dev server`); + res.statusCode ||= 404; + res.setHeader('Content-Type', 'text/plain'); + res.writeHead(res.statusCode); + res.end('No entry found for dev server'); + return; + } const ssrModule = await server.ssrLoadModule(firstInput); const render: Render = ssrModule.default ?? ssrModule.render; @@ -214,7 +165,6 @@ export async function configureDevServer( stream: res, snapshot: !isClientDevOnly, manifest: isClientDevOnly ? undefined : manifest, - symbolMapper: isClientDevOnly ? undefined : symbolMapper, serverData, containerAttributes: { ...serverData.containerAttributes }, }; diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts index ceecf43f1b1..97569ba8818 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.ts @@ -12,7 +12,6 @@ import type { import { type BundleGraphAdder } from './bundle-graph'; import { getImageSizeServer } from './image-size-server'; import { - CLIENT_OUT_DIR, QWIK_BUILD_ID, QWIK_CLIENT_MANIFEST_ID, QWIK_CORE_ID, @@ -20,7 +19,6 @@ import { QWIK_CORE_SERVER, QWIK_JSX_DEV_RUNTIME_ID, QWIK_JSX_RUNTIME_ID, - SSR_OUT_DIR, TRANSFORM_REGEX, createQwikPlugin, type ExperimentalFeatures, @@ -56,7 +54,6 @@ type P = VitePlugin & { api: T; config: Extract['config'], F export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { let isClientDevOnly = false; let clientDevInput: undefined | string = undefined; - let tmpClientManifestPath: undefined | string = undefined; let viteCommand: 'build' | 'serve' = 'serve'; let manifestInput: QwikManifest | null = null; let clientOutDir: string | null = null; @@ -140,7 +137,13 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { qwikViteOpts.entryStrategy = { type: 'inline' }; } } - + // Special case: build.ssr can be the input for the ssr build + const ssrInput = + target === 'ssr' + ? typeof viteConfig.build?.ssr === 'string' + ? viteConfig.build.ssr + : qwikViteOpts.ssr?.input + : undefined; const shouldFindVendors = !qwikViteOpts.disableVendorScan && (target !== 'lib' || viteCommand === 'serve'); viteAssetsDir = viteConfig.build?.assetsDir; @@ -157,106 +160,39 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { resolveQwikBuild: true, transformedModuleOutput: qwikViteOpts.transformedModuleOutput, outDir: viteConfig.build?.outDir, + ssrOutDir: qwikViteOpts.ssr?.outDir || viteConfig.build?.outDir, + clientOutDir: + qwikViteOpts.client?.outDir || + // When ssr is true, this is probably an adapter build and not where the client build is + (viteConfig.build?.ssr ? undefined : viteConfig.build?.outDir), assetsDir: useAssetsDir ? viteAssetsDir : undefined, devTools: qwikViteOpts.devTools, sourcemap: !!viteConfig.build?.sourcemap, lint: qwikViteOpts.lint, experimental: qwikViteOpts.experimental, + input: viteConfig.build?.rollupOptions?.input || ssrInput, + manifestInput: qwikViteOpts.ssr?.manifestInput, + manifestOutput: qwikViteOpts.client?.manifestOutput, }; - if (!qwikViteOpts.csr) { - if (target === 'ssr') { - // ssr - if (typeof viteConfig.build?.ssr === 'string') { - // from --ssr flag user config - // entry.server.ts (express/cloudflare/netlify) - pluginOpts.input = viteConfig.build.ssr; - } else if (typeof qwikViteOpts.ssr?.input === 'string') { - // entry.ssr.tsx input (exports render()) - pluginOpts.input = qwikViteOpts.ssr.input; - } - - if (qwikViteOpts.ssr?.outDir) { - pluginOpts.outDir = qwikViteOpts.ssr.outDir; - } - pluginOpts.manifestInput = qwikViteOpts.ssr?.manifestInput; - } else if (target === 'client') { - // client - pluginOpts.input = qwikViteOpts.client?.input; - if (qwikViteOpts.client?.outDir) { - pluginOpts.outDir = qwikViteOpts.client.outDir; - } - pluginOpts.manifestOutput = qwikViteOpts.client?.manifestOutput; - } else { - if (typeof viteConfig.build?.lib === 'object') { - pluginOpts.input = viteConfig.build?.lib.entry; - } - } - if (sys.env === 'node' || sys.env === 'bun') { - const fs: typeof import('fs') = await sys.dynamicImport('node:fs'); - - try { - const rootDir = pluginOpts.rootDir ?? sys.cwd(); - const packageJsonPath = sys.path.join(rootDir, 'package.json'); - const pkgString = await fs.promises.readFile(packageJsonPath, 'utf-8'); - - try { - const data = JSON.parse(pkgString); - - if (typeof data.name === 'string') { - pluginOpts.scope = data.name; - } - } catch (e) { - console.error(e); - } - } catch { - // error reading package.json from Node.js fs, ok to ignore - } - - // In a Node.js environment, create a path to a q-manifest.json file within the - // OS tmp directory. This path should always be the same for both client and ssr. - // Client build will write to this path, and SSR will read from it. For this reason, - // the Client build should always start and finish before the SSR build. - const nodeOs: typeof import('os') = await sys.dynamicImport('node:os'); - - // Additionally, we add a suffix to scope the file to the current application so that - // different applications can be run in parallel without generating conflicts. - const scopeSuffix = pluginOpts.scope ? `-${pluginOpts.scope.replace(/\//g, '--')}` : ''; - - tmpClientManifestPath = path.join( - nodeOs.tmpdir(), - `vite-plugin-qwik-q-manifest${scopeSuffix}.json` - ); - if (target === 'ssr' && !pluginOpts.manifestInput) { - // This is a SSR build so we should load the client build's manifest - // so it can be used as the manifestInput of the SSR build - try { - const clientManifestStr = await fs.promises.readFile(tmpClientManifestPath, 'utf-8'); - pluginOpts.manifestInput = JSON.parse(clientManifestStr); - } catch { - // ignore - } - } - } + const opts = await qwikPlugin.normalizeOptions(pluginOpts); + if (ssrInput) { + // make sure vite uses the ssr input + opts.input ||= [ssrInput]; } - const opts = qwikPlugin.normalizeOptions(pluginOpts); - manifestInput = pluginOpts.manifestInput || null; + manifestInput = opts.manifestInput; srcDir = opts.srcDir; rootDir = opts.rootDir; if (!qwikViteOpts.csr) { - clientOutDir = qwikPlugin.normalizePath( - sys.path.resolve(opts.rootDir, qwikViteOpts.client?.outDir || CLIENT_OUT_DIR) - ); + clientOutDir = opts.clientOutDir; clientPublicOutDir = viteConfig.base ? path.join(clientOutDir, viteConfig.base) : clientOutDir; - ssrOutDir = qwikPlugin.normalizePath( - sys.path.resolve(opts.rootDir, qwikViteOpts.ssr?.outDir || SSR_OUT_DIR) - ); + ssrOutDir = opts.ssrOutDir; if (typeof qwikViteOpts.client?.devInput === 'string') { clientDevInput = path.resolve(opts.rootDir, qwikViteOpts.client.devInput); @@ -345,6 +281,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { * https://github.com/QwikDev/qwik/issues/7226#issuecomment-2647122505 */ maxParallelFileOps: 1, + // This will amend the existing input + input: opts.input, output: { manualChunks: qwikPlugin.manualChunks, }, @@ -368,7 +306,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { updatedViteConfig.build!.outDir = buildOutputDir; const origOnwarn = updatedViteConfig.build!.rollupOptions?.onwarn; updatedViteConfig.build!.rollupOptions = { - input: opts.input, + ...updatedViteConfig.build!.rollupOptions, output: normalizeRollupOutputOptions( qwikPlugin, viteConfig.build?.rollupOptions?.output, @@ -390,7 +328,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { updatedViteConfig.publicDir = false; updatedViteConfig.build!.ssr = true; if (viteConfig.build?.minify == null && buildMode === 'production') { - updatedViteConfig.build!.minify = 'esbuild'; + updatedViteConfig.build!.minify = true; } } } else if (opts.target === 'client') { @@ -447,6 +385,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { }, async buildStart() { + injections.length = 0; + // Using vite.resolveId to check file if exist // for example input might be virtual file const resolver = this.resolve.bind(this); @@ -564,22 +504,10 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { } } - const clientManifestStr = await qwikPlugin.generateManifest( - this, - rollupBundle, - bundleGraphAdders, - { - injections, - platform: { vite: '' }, - } - ); - - const sys = qwikPlugin.getSys(); - if (tmpClientManifestPath && (sys.env === 'node' || sys.env === 'bun')) { - // Client build should write the manifest to a tmp dir - const fs: typeof import('fs') = await sys.dynamicImport('node:fs'); - await fs.promises.writeFile(tmpClientManifestPath, clientManifestStr); - } + await qwikPlugin.generateManifest(this, rollupBundle, bundleGraphAdders, { + injections, + platform: { vite: '' }, + }); } }, }, @@ -639,7 +567,8 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { configureServer(server: ViteDevServer) { qwikPlugin.configureServer(server); - const devSsrServer = 'devSsrServer' in qwikViteOpts ? !!qwikViteOpts.devSsrServer : true; + const devSsrServer = + !qwikViteOpts.csr && ('devSsrServer' in qwikViteOpts ? !!qwikViteOpts.devSsrServer : true); const imageDevTools = qwikViteOpts.devTools && 'imageDevTools' in qwikViteOpts.devTools ? qwikViteOpts.devTools.imageDevTools @@ -649,7 +578,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { server.middlewares.use(getImageSizeServer(qwikPlugin.getSys(), rootDir!, srcDir!)); } - if (!qwikViteOpts.csr) { + if (devSsrServer) { const plugin = async () => { const opts = qwikPlugin.getOptions(); const sys = qwikPlugin.getSys(); @@ -661,11 +590,10 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { sys, path, isClientDevOnly, - clientDevInput, - devSsrServer + clientDevInput ); }; - // TODO: Removed the "__qwikCityNew" condition in V3 + // TODO: Remove the "__qwikCityNew" condition in V3 const isNEW = (globalThis as any).__qwikRouterNew === true || (globalThis as any).__qwikCityNew === true || diff --git a/packages/qwik/src/optimizer/src/plugins/vite.unit.ts b/packages/qwik/src/optimizer/src/plugins/vite.unit.ts index 31731e7995b..6963d75a092 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.unit.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.unit.ts @@ -307,7 +307,8 @@ test('command: build, --ssr entry.server.tsx', async () => { assert.deepEqual(rollupOptions.input, [normalizePath(resolve(cwd, 'src', 'entry.server.tsx'))]); assert.deepEqual(outputOptions.assetFileNames, 'assets/[hash]-[name].[ext]'); - assert.deepEqual(outputOptions.chunkFileNames, undefined); + assert.isFunction(outputOptions.chunkFileNames); + assert.deepEqual((outputOptions.chunkFileNames as any)({ name: 'hello' }), 'build/hello.js'); assert.deepEqual(outputOptions.entryFileNames, undefined); assert.deepEqual(build.outDir, normalizePath(resolve(cwd, 'server'))); @@ -365,7 +366,7 @@ test('command: serve, --mode ssr with build.assetsDir', async () => { { build: { emptyOutDir: true, assetsDir: 'my-assets-dir' } }, { command: 'serve', mode: 'ssr' } ))!; - const opts = await plugin.api?.getOptions(); + const opts = plugin.api?.getOptions(); const build = c.build!; const rollupOptions = build!.rollupOptions!; @@ -451,10 +452,11 @@ test('command: build, --mode lib', async () => { assert.deepEqual(opts.buildMode, 'development'); assert.deepEqual(build.minify, false); assert.deepEqual(build.ssr, undefined); - assert.deepEqual(rollupOptions.input, [normalizePath(resolve(cwd, 'src', 'index.ts'))]); + assert.deepEqual(rollupOptions.input, undefined); assert.deepEqual(outputOptions.assetFileNames, 'assets/[hash]-[name].[ext]'); - assert.deepEqual(outputOptions.chunkFileNames, undefined); + assert.isFunction(outputOptions.chunkFileNames); + assert.deepEqual((outputOptions.chunkFileNames as any)({ name: 'hello' }), 'build/hello.js'); assert.deepEqual(c.build.outDir, normalizePath(resolve(cwd, 'lib'))); assert.deepEqual(build.emptyOutDir, undefined); @@ -506,14 +508,15 @@ test('command: build, --mode lib with multiple outputs', async () => { assert.deepEqual(opts.buildMode, 'development'); assert.deepEqual(build.minify, false); assert.deepEqual(build.ssr, undefined); - assert.deepEqual(rollupOptions.input, [normalizePath(resolve(cwd, 'src', 'index.ts'))]); + assert.deepEqual(rollupOptions.input, undefined); assert.ok(Array.isArray(outputOptions)); assert.lengthOf(outputOptions, 4); outputOptions.forEach((outputOptionsObj) => { assert.deepEqual(outputOptionsObj.assetFileNames, 'assets/[hash]-[name].[ext]'); - assert.deepEqual(outputOptionsObj.chunkFileNames, undefined); + assert.isFunction(outputOptionsObj.chunkFileNames); + assert.deepEqual((outputOptionsObj.chunkFileNames as any)({ name: 'hello' }), 'build/hello.js'); }); assert.deepEqual(c.build.outDir, normalizePath(resolve(cwd, 'lib'))); diff --git a/packages/qwik/src/optimizer/src/qwik.optimizer.api.md b/packages/qwik/src/optimizer/src/qwik.optimizer.api.md index 390e81d8217..80ebfd9b51d 100644 --- a/packages/qwik/src/optimizer/src/qwik.optimizer.api.md +++ b/packages/qwik/src/optimizer/src/qwik.optimizer.api.md @@ -406,10 +406,8 @@ export type SourceMapsOption = 'external' | 'inline' | undefined | null; // @public (undocumented) export type SymbolMapper = Record; -// Warning: (ae-forgotten-export) The symbol "createSymbolMapper" needs to be exported by the entry point index.d.ts -// -// @beta -export let symbolMapper: ReturnType; +// @alpha @deprecated (undocumented) +export const symbolMapper: undefined; // @public (undocumented) export type SymbolMapperFn = (symbolName: string, mapper: SymbolMapper | undefined, parent?: string) => readonly [symbol: string, chunk: string] | undefined; diff --git a/packages/qwik/src/server/platform.ts b/packages/qwik/src/server/platform.ts index fe5e0f7c671..d6a9c59fe0e 100644 --- a/packages/qwik/src/server/platform.ts +++ b/packages/qwik/src/server/platform.ts @@ -1,11 +1,43 @@ -import type { SerializeDocumentOptions } from './types'; import { setPlatform } from '@qwik.dev/core'; -import type { ResolvedManifest } from '@qwik.dev/core/optimizer'; -import type { CorePlatformServer } from './qwik-types'; +import { isDev } from '@qwik.dev/core/build'; +import type { ResolvedManifest, SymbolMapperFn } from '@qwik.dev/core/optimizer'; import { SYNC_QRL } from './qwik-copy'; +import type { CorePlatformServer, SymbolMapper } from './qwik-types'; +import type { SerializeDocumentOptions } from './types'; declare const require: (module: string) => Record; +/** + * In dev mode, we create predicatable QRL segment filenames so we can recover the parent path in + * the vite plugin, because we don't have a manifest + */ +const getDevSegmentPath = ( + mapper: SymbolMapper | undefined, + hash: string, + symbolName: string, + parent?: string +): ReturnType => { + const existing = mapper?.[hash]; + if (existing) { + return existing; + } + if (symbolName === SYNC_QRL) { + return [symbolName, '']; + } + if (!parent) { + // Core symbols + if (symbolName.startsWith('_') && symbolName.length < 6) { + return [symbolName, `${import.meta.env.BASE_URL}@qwik-handlers`]; + } + console.error('qwik symbolMapper: unknown qrl requested without parent:', symbolName); + return [symbolName, `${import.meta.env.BASE_URL}${symbolName}.js`]; + } + // In dev mode, the `parent` is the Vite URL for the parent, not the real absolute path. + // It is always absolute but when on Windows that's without a / + const qrlFile = `${import.meta.env.BASE_URL}${parent.startsWith('/') ? parent.slice(1) : parent}_${symbolName}.js`; + return [symbolName, qrlFile]; +}; + export function createPlatform( opts: SerializeDocumentOptions, resolvedManifest: ResolvedManifest | undefined @@ -14,9 +46,11 @@ export function createPlatform( const mapperFn = opts.symbolMapper ? opts.symbolMapper : (symbolName: string, _chunk: any, parent?: string): readonly [string, string] | undefined => { - if (mapper) { + if (mapper || (isDev && import.meta.env.MODE !== 'test')) { const hash = getSymbolHash(symbolName); - const result = mapper[hash]; + const result = !isDev + ? mapper![hash] + : getDevSegmentPath(mapper, hash, symbolName, parent); if (!result) { if (hash === SYNC_QRL) { return [hash, ''] as const; @@ -25,10 +59,6 @@ export function createPlatform( if (isRegistered) { return [symbolName, '_'] as const; } - 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; diff --git a/scripts/api.ts b/scripts/api.ts index 0803c849c3d..1708b548921 100644 --- a/scripts/api.ts +++ b/scripts/api.ts @@ -141,8 +141,8 @@ export async function apiExtractorQwikRouter(config: BuildConfig) { ); createTypesApi( config, - join(config.packagesDir, 'qwik-router', 'src', 'adapters', 'static', 'vite'), - join(config.packagesDir, 'qwik-router', 'lib', 'adapters', 'static', 'vite', 'index.d.ts') + join(config.packagesDir, 'qwik-router', 'src', 'adapters', 'ssg', 'vite'), + join(config.packagesDir, 'qwik-router', 'lib', 'adapters', 'ssg', 'vite', 'index.d.ts') ); createTypesApi( config, @@ -196,8 +196,8 @@ export async function apiExtractorQwikRouter(config: BuildConfig) { ); createTypesApi( config, - join(config.packagesDir, 'qwik-router', 'src', 'static'), - join(config.packagesDir, 'qwik-router', 'lib', 'static', 'index.d.ts') + join(config.packagesDir, 'qwik-router', 'src', 'ssg'), + join(config.packagesDir, 'qwik-router', 'lib', 'ssg', 'index.d.ts') ); createTypesApi( config, diff --git a/scripts/qwik-router.ts b/scripts/qwik-router.ts index 1b9bc208f90..3b9362a4a47 100644 --- a/scripts/qwik-router.ts +++ b/scripts/qwik-router.ts @@ -21,7 +21,7 @@ export async function buildQwikRouter(config: BuildConfig) { buildAdapterNodeServerVite(config), buildAdapterNetlifyEdgeVite(config), buildAdapterSharedVite(config), - buildAdapterStaticVite(config), + buildAdapterSsgVite(config), buildAdapterVercelEdgeVite(config), buildMiddlewareCloudflarePages(config), buildMiddlewareNetlifyEdge(config), @@ -33,9 +33,9 @@ export async function buildQwikRouter(config: BuildConfig) { buildMiddlewareRequestHandler(config), buildMiddlewareVercelEdge(config), buildMiddlewareFirebase(config), - buildStatic(config), - buildStaticNode(config), - buildStaticDeno(config), + buildSsg(config), + buildSsgNode(config), + buildSsgDeno(config), ]); await buildRuntime(config); @@ -167,7 +167,7 @@ async function buildServiceWorker(config: BuildConfig) { async function buildAdapterAzureSwaVite(config: BuildConfig) { const entryPoints = [join(config.srcQwikRouterDir, 'adapters', 'azure-swa', 'vite', 'index.ts')]; - const external = ['vite', 'fs', 'path', '@qwik.dev/router/static']; + const external = ['vite', 'fs', 'path', '@qwik.dev/router/ssg']; await build({ entryPoints, @@ -377,7 +377,7 @@ async function buildAdapterSharedVite(config: BuildConfig) { format: 'esm', external: ADAPTER_EXTERNALS, plugins: [ - resolveStatic('../../../static/index.mjs'), + resolveSsg('../../../ssg/index.mjs'), resolveRequestHandler('../../../middleware/request-handler/index.mjs'), ], }); @@ -391,35 +391,35 @@ async function buildAdapterSharedVite(config: BuildConfig) { format: 'cjs', external: ADAPTER_EXTERNALS, plugins: [ - resolveStatic('../../../static/index.cjs'), + resolveSsg('../../../ssg/index.cjs'), resolveRequestHandler('../../../middleware/request-handler/index.cjs'), ], }); } -async function buildAdapterStaticVite(config: BuildConfig) { - const entryPoints = [join(config.srcQwikRouterDir, 'adapters', 'static', 'vite', 'index.ts')]; +async function buildAdapterSsgVite(config: BuildConfig) { + const entryPoints = [join(config.srcQwikRouterDir, 'adapters', 'ssg', 'vite', 'index.ts')]; await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'adapters', 'static', 'vite', 'index.mjs'), + outfile: join(config.distQwikRouterPkgDir, 'adapters', 'ssg', 'vite', 'index.mjs'), bundle: true, platform: 'node', target: nodeTarget, format: 'esm', external: ADAPTER_EXTERNALS, - plugins: [resolveStatic('../../../static/index.mjs')], + plugins: [resolveSsg('../../../ssg/index.mjs')], }); await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'adapters', 'static', 'vite', 'index.cjs'), + outfile: join(config.distQwikRouterPkgDir, 'adapters', 'ssg', 'vite', 'index.cjs'), bundle: true, platform: 'node', target: nodeTarget, format: 'cjs', external: ADAPTER_EXTERNALS, - plugins: [resolveStatic('../../../static/index.cjs')], + plugins: [resolveSsg('../../../ssg/index.cjs')], }); } @@ -623,12 +623,12 @@ async function buildMiddlewareFirebase(config: BuildConfig) { }); } -async function buildStatic(config: BuildConfig) { - const entryPoints = [join(config.srcQwikRouterDir, 'static', 'index.ts')]; +async function buildSsg(config: BuildConfig) { + const entryPoints = [join(config.srcQwikRouterDir, 'ssg', 'index.ts')]; await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'static', 'index.mjs'), + outfile: join(config.distQwikRouterPkgDir, 'ssg', 'index.mjs'), bundle: true, platform: 'neutral', format: 'esm', @@ -636,7 +636,7 @@ async function buildStatic(config: BuildConfig) { await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'static', 'index.cjs'), + outfile: join(config.distQwikRouterPkgDir, 'ssg', 'index.cjs'), bundle: true, platform: 'node', target: nodeTarget, @@ -644,12 +644,12 @@ async function buildStatic(config: BuildConfig) { }); } -async function buildStaticDeno(config: BuildConfig) { - const entryPoints = [join(config.srcQwikRouterDir, 'static', 'deno', 'index.ts')]; +async function buildSsgDeno(config: BuildConfig) { + const entryPoints = [join(config.srcQwikRouterDir, 'ssg', 'deno', 'index.ts')]; await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'static', 'deno.mjs'), + outfile: join(config.distQwikRouterPkgDir, 'ssg', 'deno.mjs'), bundle: true, platform: 'neutral', format: 'esm', @@ -657,8 +657,8 @@ async function buildStaticDeno(config: BuildConfig) { }); } -async function buildStaticNode(config: BuildConfig) { - const entryPoints = [join(config.srcQwikRouterDir, 'static', 'node', 'index.ts')]; +async function buildSsgNode(config: BuildConfig) { + const entryPoints = [join(config.srcQwikRouterDir, 'ssg', 'node', 'index.ts')]; const external = [ '@qwik.dev/core', @@ -679,7 +679,7 @@ async function buildStaticNode(config: BuildConfig) { await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'static', 'node.mjs'), + outfile: join(config.distQwikRouterPkgDir, 'ssg', 'node.mjs'), bundle: true, platform: 'node', target: nodeTarget, @@ -690,7 +690,7 @@ async function buildStaticNode(config: BuildConfig) { await build({ entryPoints, - outfile: join(config.distQwikRouterPkgDir, 'static', 'node.cjs'), + outfile: join(config.distQwikRouterPkgDir, 'ssg', 'node.cjs'), bundle: true, platform: 'node', target: nodeTarget, @@ -704,8 +704,8 @@ function resolveRequestHandler(path: string) { return importPath(/middleware\/request-handler/, path); } -function resolveStatic(path: string) { - return importPath(/static$/, path); +function resolveSsg(path: string) { + return importPath(/ssg$/, path); } function resolveAdapterShared(path: string) { @@ -720,7 +720,7 @@ const ADAPTER_EXTERNALS = [ '@qwik.dev/core/server', '@qwik.dev/core/optimizer', '@qwik.dev/router', - '@qwik.dev/router/static', + '@qwik.dev/router/ssg', '@qwik.dev/router/middleware/request-handler', ]; @@ -729,7 +729,7 @@ const MIDDLEWARE_EXTERNALS = [ '@qwik.dev/core/optimizer', '@qwik.dev/core/server', '@qwik.dev/router', - '@qwik.dev/router/static', + '@qwik.dev/router/ssg', '@qwik-router-config', '@qwik-router-not-found-paths', '@qwik-router-static-paths', diff --git a/starters/adapters/aws-lambda/adapters/aws-lambda/vite.config.mts b/starters/adapters/aws-lambda/adapters/aws-lambda/vite.config.mts index c82e07b31a8..87113c81ac6 100755 --- a/starters/adapters/aws-lambda/adapters/aws-lambda/vite.config.mts +++ b/starters/adapters/aws-lambda/adapters/aws-lambda/vite.config.mts @@ -13,7 +13,7 @@ export default extendConfig(baseConfig, () => { minify: false, ssr: true, rollupOptions: { - input: ["./src/entry_aws-lambda.tsx", "@qwik-router-config"], + input: ["./src/entry_aws-lambda.tsx"], }, }, plugins: [nodeServerAdapter({ name: "aws-lambda" })], diff --git a/starters/adapters/aws-lambda/src/entry_aws-lambda.tsx b/starters/adapters/aws-lambda/src/entry_aws-lambda.tsx index 2648f266ade..73ae6ec3d25 100755 --- a/starters/adapters/aws-lambda/src/entry_aws-lambda.tsx +++ b/starters/adapters/aws-lambda/src/entry_aws-lambda.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/aws/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformAwsLambda, @@ -20,10 +19,7 @@ declare global { interface QwikRouterPlatform extends PlatformAwsLambda {} } -export const { handle } = createQwikRouter({ - render, - qwikRouterConfig, -}); +export const { handle } = createQwikRouter({ render }); export const qwikApp = serverless({ handle }, { binary: true }); // handler is the default export for the lambda functions diff --git a/starters/adapters/azure-swa/adapters/azure-swa/vite.config.mts b/starters/adapters/azure-swa/adapters/azure-swa/vite.config.mts index ba1c28f3f1b..7315be0375f 100644 --- a/starters/adapters/azure-swa/adapters/azure-swa/vite.config.mts +++ b/starters/adapters/azure-swa/adapters/azure-swa/vite.config.mts @@ -8,7 +8,7 @@ export default extendConfig(baseConfig, () => { ssr: true, outDir: "azure-functions/render", rollupOptions: { - input: ["src/entry.azure-swa.tsx", "@qwik-router-config"], + input: ["src/entry.azure-swa.tsx"], output: { entryFileNames: `[name].[hash].mjs`, chunkFileNames: `[name].[hash].mjs`, diff --git a/starters/adapters/azure-swa/src/entry.azure-swa.tsx b/starters/adapters/azure-swa/src/entry.azure-swa.tsx index 31c9041a334..ade00a111de 100644 --- a/starters/adapters/azure-swa/src/entry.azure-swa.tsx +++ b/starters/adapters/azure-swa/src/entry.azure-swa.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/azure-swa/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformAzure, @@ -18,4 +17,4 @@ declare global { interface QwikRouterPlatform extends PlatformAzure {} } -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/adapters/bun/adapters/bun/vite.config.mts b/starters/adapters/bun/adapters/bun/vite.config.mts index 0b084c77d0a..7a9e52d6ae5 100644 --- a/starters/adapters/bun/adapters/bun/vite.config.mts +++ b/starters/adapters/bun/adapters/bun/vite.config.mts @@ -11,7 +11,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.bun.ts", "@qwik-router-config"], + input: ["src/entry.bun.ts"], }, minify: false, }, diff --git a/starters/adapters/bun/src/entry.bun.ts b/starters/adapters/bun/src/entry.bun.ts index fec8caea392..57008a56f9e 100644 --- a/starters/adapters/bun/src/entry.bun.ts +++ b/starters/adapters/bun/src/entry.bun.ts @@ -8,14 +8,12 @@ * - https://bun.sh/docs/api/http * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter } from "@qwik.dev/router/middleware/bun"; import render from "./entry.ssr"; // Create the Qwik Router Bun middleware const { router, notFound, staticFile } = createQwikRouter({ render, - qwikRouterConfig, static: { cacheControl: "public, max-age=31536000, immutable", }, diff --git a/starters/adapters/cloud-run/adapters/cloud-run/vite.config.mts b/starters/adapters/cloud-run/adapters/cloud-run/vite.config.mts index b282e4ced32..603f91c4da6 100644 --- a/starters/adapters/cloud-run/adapters/cloud-run/vite.config.mts +++ b/starters/adapters/cloud-run/adapters/cloud-run/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.cloud-run.tsx", "@qwik-router-config"], + input: ["src/entry.cloud-run.tsx"], }, }, plugins: [cloudRunAdapter()], diff --git a/starters/adapters/cloud-run/src/entry.cloud-run.tsx b/starters/adapters/cloud-run/src/entry.cloud-run.tsx index 10d19256300..c1f36a4349c 100644 --- a/starters/adapters/cloud-run/src/entry.cloud-run.tsx +++ b/starters/adapters/cloud-run/src/entry.cloud-run.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/gcp-cloud-run/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformNode, @@ -50,7 +49,6 @@ const DEFAULT_HEADERS = { const { router, notFound, staticFile } = createQwikRouter({ render, - qwikRouterConfig, static: { cacheControl: "public, max-age=31536000, immutable", }, diff --git a/starters/adapters/cloudflare-pages/adapters/cloudflare-pages/vite.config.mts b/starters/adapters/cloudflare-pages/adapters/cloudflare-pages/vite.config.mts index 46de395d49f..bc5079b4f25 100644 --- a/starters/adapters/cloudflare-pages/adapters/cloudflare-pages/vite.config.mts +++ b/starters/adapters/cloudflare-pages/adapters/cloudflare-pages/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.cloudflare-pages.tsx", "@qwik-router-config"], + input: ["src/entry.cloudflare-pages.tsx"], }, }, plugins: [cloudflarePagesAdapter()], diff --git a/starters/adapters/cloudflare-pages/src/entry.cloudflare-pages.tsx b/starters/adapters/cloudflare-pages/src/entry.cloudflare-pages.tsx index 10447b03cd2..5ef2e523fd6 100644 --- a/starters/adapters/cloudflare-pages/src/entry.cloudflare-pages.tsx +++ b/starters/adapters/cloudflare-pages/src/entry.cloudflare-pages.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/cloudflare-pages/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformCloudflarePages, @@ -18,6 +17,6 @@ declare global { interface QwikRouterPlatform extends PlatformCloudflarePages {} } -const fetch = createQwikRouter({ render, qwikRouterConfig }); +const fetch = createQwikRouter({ render }); export { fetch }; diff --git a/starters/adapters/deno/adapters/deno/vite.config.mts b/starters/adapters/deno/adapters/deno/vite.config.mts index aa12b1e1dc4..bb43565fa66 100644 --- a/starters/adapters/deno/adapters/deno/vite.config.mts +++ b/starters/adapters/deno/adapters/deno/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.deno.ts", "@qwik-router-config"], + input: ["src/entry.deno.ts"], }, minify: false, }, diff --git a/starters/adapters/deno/src/entry.deno.ts b/starters/adapters/deno/src/entry.deno.ts index 01e49772a82..cd401b2d90c 100644 --- a/starters/adapters/deno/src/entry.deno.ts +++ b/starters/adapters/deno/src/entry.deno.ts @@ -8,14 +8,12 @@ * - https://docs.deno.com/runtime/tutorials/http_server * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter } from "@qwik.dev/router/middleware/deno"; import render from "./entry.ssr"; // Create the Qwik Router Deno middleware const { router, notFound, staticFile } = createQwikRouter({ render, - qwikRouterConfig, static: { cacheControl: "public, max-age=31536000, immutable", }, diff --git a/starters/adapters/express/adapters/express/vite.config.mts b/starters/adapters/express/adapters/express/vite.config.mts index aced53fd5ce..e55ed3cac65 100644 --- a/starters/adapters/express/adapters/express/vite.config.mts +++ b/starters/adapters/express/adapters/express/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.express.tsx", "@qwik-router-config"], + input: ["src/entry.express.tsx"], }, }, plugins: [nodeServerAdapter({ name: "express" })], diff --git a/starters/adapters/express/src/entry.express.tsx b/starters/adapters/express/src/entry.express.tsx index 12cedfc9510..7da72db8bf2 100644 --- a/starters/adapters/express/src/entry.express.tsx +++ b/starters/adapters/express/src/entry.express.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/node/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformNode, @@ -33,7 +32,6 @@ const PORT = process.env.PORT ?? 3000; // Create the Qwik Router Node middleware const { router, notFound } = createQwikRouter({ render, - qwikRouterConfig, // getOrigin(req) { // // If deploying under a proxy, you may need to build the origin from the request headers // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto diff --git a/starters/adapters/fastify/adapters/fastify/vite.config.mts b/starters/adapters/fastify/adapters/fastify/vite.config.mts index d9e89807ff1..9009ce633b0 100644 --- a/starters/adapters/fastify/adapters/fastify/vite.config.mts +++ b/starters/adapters/fastify/adapters/fastify/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.fastify.tsx", "@qwik-router-config"], + input: ["src/entry.fastify.tsx"], }, }, plugins: [nodeServerAdapter({ name: "fastify" })], diff --git a/starters/adapters/fastify/src/plugins/fastify-qwik.ts b/starters/adapters/fastify/src/plugins/fastify-qwik.ts index 1f8a7f483a8..859f0886d34 100644 --- a/starters/adapters/fastify/src/plugins/fastify-qwik.ts +++ b/starters/adapters/fastify/src/plugins/fastify-qwik.ts @@ -1,5 +1,4 @@ import fastifyStatic from "@fastify/static"; -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter } from "@qwik.dev/router/middleware/node"; import type { FastifyPluginAsync } from "fastify"; import fastifyPlugin from "fastify-plugin"; @@ -12,7 +11,7 @@ export interface FastifyQwikOptions { assetsDir: string; } -const { router, notFound } = createQwikRouter({ render, qwikRouterConfig }); +const { router, notFound } = createQwikRouter({ render }); const qwikPlugin: FastifyPluginAsync = async ( fastify, diff --git a/starters/adapters/firebase/adapters/firebase/vite.config.mts b/starters/adapters/firebase/adapters/firebase/vite.config.mts index 7ca9fb9b626..9043a67bcea 100755 --- a/starters/adapters/firebase/adapters/firebase/vite.config.mts +++ b/starters/adapters/firebase/adapters/firebase/vite.config.mts @@ -12,7 +12,7 @@ export default extendConfig(baseConfig, () => { minify: false, ssr: true, rollupOptions: { - input: ["./src/entry-firebase.tsx", "@qwik-router-config"], + input: ["./src/entry-firebase.tsx"], }, outDir: "./functions/server", }, diff --git a/starters/adapters/firebase/src/entry-firebase.tsx b/starters/adapters/firebase/src/entry-firebase.tsx index 4bbeb8d7ad7..9a1d7511ae4 100755 --- a/starters/adapters/firebase/src/entry-firebase.tsx +++ b/starters/adapters/firebase/src/entry-firebase.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/firebase/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformFirebase, @@ -18,4 +17,4 @@ declare global { interface QwikRouterPlatform extends PlatformFirebase {} } -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/adapters/netlify-edge/adapters/netlify-edge/vite.config.mts b/starters/adapters/netlify-edge/adapters/netlify-edge/vite.config.mts index 41ea67d9da4..5465edf30c7 100644 --- a/starters/adapters/netlify-edge/adapters/netlify-edge/vite.config.mts +++ b/starters/adapters/netlify-edge/adapters/netlify-edge/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.netlify-edge.tsx", "@qwik-router-config"], + input: ["src/entry.netlify-edge.tsx"], }, outDir: ".netlify/edge-functions/entry.netlify-edge", }, diff --git a/starters/adapters/netlify-edge/src/entry.netlify-edge.tsx b/starters/adapters/netlify-edge/src/entry.netlify-edge.tsx index e08f0a3a1b4..c5f58696839 100644 --- a/starters/adapters/netlify-edge/src/entry.netlify-edge.tsx +++ b/starters/adapters/netlify-edge/src/entry.netlify-edge.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/netlify-edge/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformNetlify, @@ -18,4 +17,4 @@ declare global { interface QwikRouterPlatform extends PlatformNetlify {} } -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/adapters/node-server/adapters/node-server/vite.config.mts b/starters/adapters/node-server/adapters/node-server/vite.config.mts index 852ee71aa7f..baf5513ff5b 100644 --- a/starters/adapters/node-server/adapters/node-server/vite.config.mts +++ b/starters/adapters/node-server/adapters/node-server/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.node-server.tsx", "@qwik-router-config"], + input: ["src/entry.node-server.tsx"], }, }, plugins: [nodeServerAdapter({ name: "node-server" })], diff --git a/starters/adapters/node-server/src/entry.node-server.tsx b/starters/adapters/node-server/src/entry.node-server.tsx index 0d2cd8b15e7..0412522e451 100644 --- a/starters/adapters/node-server/src/entry.node-server.tsx +++ b/starters/adapters/node-server/src/entry.node-server.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/node/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter } from "@qwik.dev/router/middleware/node"; import { createServer } from "node:http"; import render from "./entry.ssr"; @@ -18,7 +17,6 @@ const PORT = process.env.PORT ?? 3004; // Create the Qwik Router express middleware const { router, notFound, staticFile } = createQwikRouter({ render, - qwikRouterConfig, static: { cacheControl: "public, max-age=31536000, immutable", }, diff --git a/starters/adapters/static/README.md b/starters/adapters/ssg/README.md similarity index 100% rename from starters/adapters/static/README.md rename to starters/adapters/ssg/README.md diff --git a/starters/adapters/static/adapters/static/vite.config.mts b/starters/adapters/ssg/adapters/ssg/vite.config.mts similarity index 79% rename from starters/adapters/static/adapters/static/vite.config.mts rename to starters/adapters/ssg/adapters/ssg/vite.config.mts index c734c7908d2..d93fbe5614d 100644 --- a/starters/adapters/static/adapters/static/vite.config.mts +++ b/starters/adapters/ssg/adapters/ssg/vite.config.mts @@ -1,4 +1,4 @@ -import { staticAdapter } from "@qwik.dev/router/adapters/static/vite"; +import { ssgAdapter } from "@qwik.dev/router/adapters/ssg/vite"; import { extendConfig } from "@qwik.dev/router/vite"; import baseConfig from "../../vite.config.mts"; @@ -11,7 +11,7 @@ export default extendConfig(baseConfig, () => { }, }, plugins: [ - staticAdapter({ + ssgAdapter({ origin: "https://yoursite.qwik.dev", }), ], diff --git a/starters/adapters/ssg/package.json b/starters/adapters/ssg/package.json new file mode 100644 index 00000000000..faec4a1653e --- /dev/null +++ b/starters/adapters/ssg/package.json @@ -0,0 +1,19 @@ +{ + "description": "Static Site Generator", + "scripts": { + "build.server": "vite build -c adapters/ssg/vite.config.mts" + }, + "__qwik__": { + "priority": 10, + "displayName": "Adapter: Static Site Generation (.html files)", + "docs": [ + "https://qwik.dev/docs/guides/static-site-generation/" + ], + "nextSteps": { + "lines": [ + "You have to change the 'origin' url under ssgAdapter", + "inside './adapters/ssg/vite.config.mts'" + ] + } + } +} diff --git a/starters/adapters/static/package.json b/starters/adapters/static/package.json deleted file mode 100644 index 0d275dc1fa3..00000000000 --- a/starters/adapters/static/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "description": "Static Site Generator", - "scripts": { - "build.server": "vite build -c adapters/static/vite.config.mts" - }, - "__qwik__": { - "priority": 10, - "displayName": "Adapter: Static site (.html files)", - "docs": [ - "https://qwik.dev/docs/guides/static-site-generation/" - ], - "nextSteps": { - "lines": [ - "You have to change the 'origin' url under staticAdapter", - "inside './adapters/static/vite.config.mts'" - ] - } - } -} diff --git a/starters/adapters/vercel-edge/adapters/vercel-edge/vite.config.mts b/starters/adapters/vercel-edge/adapters/vercel-edge/vite.config.mts index c2147113630..c50bcc0a124 100644 --- a/starters/adapters/vercel-edge/adapters/vercel-edge/vite.config.mts +++ b/starters/adapters/vercel-edge/adapters/vercel-edge/vite.config.mts @@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => { build: { ssr: true, rollupOptions: { - input: ["src/entry.vercel-edge.tsx", "@qwik-router-config"], + input: ["src/entry.vercel-edge.tsx"], }, outDir: ".vercel/output/functions/_qwik-router.func", }, diff --git a/starters/adapters/vercel-edge/src/entry.vercel-edge.tsx b/starters/adapters/vercel-edge/src/entry.vercel-edge.tsx index 208a0497ddb..fd31026de50 100644 --- a/starters/adapters/vercel-edge/src/entry.vercel-edge.tsx +++ b/starters/adapters/vercel-edge/src/entry.vercel-edge.tsx @@ -7,7 +7,6 @@ * - https://qwik.dev/docs/deployments/vercel-edge/ * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter, type PlatformVercel, @@ -18,4 +17,4 @@ declare global { interface QwikRouterPlatform extends PlatformVercel {} } -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/apps/base/src/entry.preview.tsx b/starters/apps/base/src/entry.preview.tsx index 7b33ba14fe9..860ecaca285 100644 --- a/starters/apps/base/src/entry.preview.tsx +++ b/starters/apps/base/src/entry.preview.tsx @@ -10,12 +10,10 @@ * - https://vitejs.dev/config/preview-options.html#preview-options * */ -import qwikRouterConfig from "@qwik-router-config"; import { createQwikRouter } from "@qwik.dev/router/middleware/node"; -// make sure qwikRouterConfig is imported before entry import render from "./entry.ssr"; /** * The default export is the QwikRouter adapter used by Vite preview. */ -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/apps/base/src/entry.ssr.tsx b/starters/apps/base/src/entry.ssr.tsx index 875f768696d..62fefc67a42 100644 --- a/starters/apps/base/src/entry.ssr.tsx +++ b/starters/apps/base/src/entry.ssr.tsx @@ -1,14 +1,10 @@ /** * WHAT IS THIS FILE? * - * SSR entry point, in all cases the application is rendered outside the browser, this - * entry point will be the common one. - * - * - Server (express, cloudflare...) - * - npm run start - * - npm run preview - * - npm run build + * SSR renderer function, used for all build/dev targets except client-only. * + * Note that except for client-only, this is the only place the Qwik renderer is called. + * On the client, containers resume and do not call render. */ import { renderToStream, diff --git a/starters/apps/library/src/entry.dev.tsx b/starters/apps/library/src/entry.dev.tsx deleted file mode 100644 index dba2a868c7b..00000000000 --- a/starters/apps/library/src/entry.dev.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* - * WHAT IS THIS FILE? - * - * Development entry point using only client-side modules: - * - Do not use this mode in production! - * - No SSR - * - No portion of the application is pre-rendered on the server. - * - All of the application is running eagerly in the browser. - * - More code is transferred to the browser than in SSR mode. - * - Optimizer/Serialization/Deserialization code is not exercised! - */ -import { render, type RenderOptions } from "@qwik.dev/core"; -import Root from "./root"; - -export default function (opts: RenderOptions) { - return render(document, , opts); -} diff --git a/starters/apps/library/src/entry.ssr.tsx b/starters/apps/library/src/entry.ssr.tsx deleted file mode 100644 index 23f36fab3c4..00000000000 --- a/starters/apps/library/src/entry.ssr.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * WHAT IS THIS FILE? - * - * SSR entry point, in all cases the application is rendered outside the browser, this - * entry point will be the common one. - * - * - Server (express, cloudflare...) - * - npm run start - * - npm run preview - * - npm run build - * - */ -import { - renderToStream, - type RenderToStreamOptions, -} from "@qwik.dev/core/server"; -import Root from "./root"; - -export default function (opts: RenderToStreamOptions) { - return renderToStream(, opts); -} diff --git a/starters/apps/library/src/root.tsx b/starters/apps/library/src/root.tsx index 2850296ea94..eeb5067ae8d 100644 --- a/starters/apps/library/src/root.tsx +++ b/starters/apps/library/src/root.tsx @@ -6,9 +6,15 @@ export default () => { <> - Qwik Blank App + Qwik Library Starter +

Qwik Library Starter

+

+ This is a Qwik library starter. Make your components and export them + from `src/index.ts`. This playground app will not be bundled with your + library. +

diff --git a/starters/apps/library/vite.config.mts b/starters/apps/library/vite.config.mts index d1920e36af6..715e8af28c0 100644 --- a/starters/apps/library/vite.config.mts +++ b/starters/apps/library/vite.config.mts @@ -12,8 +12,9 @@ export default defineConfig(() => { build: { target: "es2020", lib: { - entry: "./src/index.ts", - formats: ["es", "cjs"], + entry: "./src/index", + formats: ["es", "cjs"] as const, + // This adds .qwik so all files are processed by the optimizer fileName: (format, entryName) => `${entryName}.qwik.${format === "es" ? "mjs" : "cjs"}`, }, diff --git a/starters/apps/preloader-test/src/entry.preview.tsx b/starters/apps/preloader-test/src/entry.preview.tsx index c20cc8ab962..53576e5fc34 100644 --- a/starters/apps/preloader-test/src/entry.preview.tsx +++ b/starters/apps/preloader-test/src/entry.preview.tsx @@ -11,11 +11,9 @@ * */ import { createQwikRouter } from "@qwik.dev/router/middleware/node"; -import qwikRouterConfig from "@qwik-router-config"; -// make sure qwikCityPlan is imported before entry import render from "./entry.ssr"; /** * The default export is the QwikCity adapter used by Vite preview. */ -export default createQwikRouter({ render, qwikRouterConfig }); +export default createQwikRouter({ render }); diff --git a/starters/dev-server.ts b/starters/dev-server.ts index 65a97aaccb0..c149b9a1fd9 100644 --- a/starters/dev-server.ts +++ b/starters/dev-server.ts @@ -143,11 +143,9 @@ async function buildApp( load(id) { if (id.endsWith(qwikRouterVirtualEntry)) { return `import { createQwikRouter } from '@qwik.dev/router/middleware/node'; -import qwikRouterConfig from '@qwik-router-config'; import render from '${escapeChars(resolve(appSrcDir, "entry.ssr"))}'; const { router, notFound } = createQwikRouter({ render, - qwikRouterConfig, base: '${basePath}build/', }); export { @@ -241,6 +239,9 @@ export { ...plugins, optimizer.qwikVite({ experimental: ["preventNavigate", "enableRequestRewrite"], + ssr: { + manifestInput: clientManifest, + }, }), ], define: {