From a9db0b1936bbcb38471ad01e9c45bf5630f41975 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 19 Oct 2022 15:28:13 -0500 Subject: [PATCH] refactor: ssr/ssg adaptors, starters (#1805) --- .../adaptors/cloudflare-pages/api.md | 24 + .../cloudflare-pages/vite/api-extractor.json | 15 + .../adaptors/cloudflare-pages/vite/index.ts | 105 ++++ packages/qwik-city/adaptors/express/api.md | 24 + .../adaptors/express/vite/api-extractor.json | 15 + .../qwik-city/adaptors/express/vite/index.ts | 103 ++++ .../qwik-city/adaptors/netlify-edge/api.md | 24 + .../netlify-edge/vite/api-extractor.json | 15 + .../adaptors/netlify-edge/vite/index.ts | 105 ++++ packages/qwik-city/adaptors/static/api.md | 22 + .../adaptors/static/vite/api-extractor.json | 15 + .../qwik-city/adaptors/static/vite/index.ts | 93 ++++ packages/qwik-city/api-extractor.json | 2 +- .../generate-qwik-city-plan.ts | 7 +- .../runtime-generation/generate-routes.ts | 25 +- packages/qwik-city/buildtime/vite/api.md | 15 + packages/qwik-city/buildtime/vite/config.ts | 24 + .../qwik-city/buildtime/vite/dev-server.ts | 1 - packages/qwik-city/buildtime/vite/index.ts | 3 +- packages/qwik-city/buildtime/vite/plugin.ts | 43 +- packages/qwik-city/buildtime/vite/types.ts | 18 +- .../middleware/cloudflare-pages/api.md | 12 +- .../middleware/cloudflare-pages/index.ts | 30 +- .../qwik-city/middleware/netlify-edge/api.md | 12 +- .../middleware/netlify-edge/index.ts | 30 +- packages/qwik-city/middleware/node/api.md | 21 +- packages/qwik-city/middleware/node/http.ts | 4 + packages/qwik-city/middleware/node/index.ts | 32 +- .../request-handler/endpoint-handler.unit.ts | 22 +- .../request-handler/page-handler.ts | 6 +- .../request-handler/request-handler.ts | 11 +- .../middleware/request-handler/test-utils.ts | 24 +- .../middleware/request-handler/types.ts | 15 +- .../request-handler/user-response.ts | 8 +- .../request-handler/user-response.unit.ts | 24 +- packages/qwik-city/package.json | 22 +- packages/qwik-city/runtime/src/api.md | 8 +- .../qwik-city/runtime/src/entry.static.tsx | 11 - .../runtime/src/library/qwik-city-plan.ts | 8 + .../qwik-city/runtime/src/library/types.ts | 4 +- .../static/{node => }/api-extractor.json | 10 +- packages/qwik-city/static/api.md | 46 ++ packages/qwik-city/static/deno/index.ts | 8 + .../qwik-city/static/generator/generate.ts | 18 - packages/qwik-city/static/generator/index.ts | 1 - packages/qwik-city/static/index.ts | 57 ++ .../static/{generator => }/main-thread.ts | 49 +- packages/qwik-city/static/node/api.md | 21 - packages/qwik-city/static/node/index.ts | 24 +- .../qwik-city/static/node/node-generate.ts | 19 - packages/qwik-city/static/node/node-main.ts | 30 +- packages/qwik-city/static/node/node-system.ts | 16 +- packages/qwik-city/static/node/node-worker.ts | 2 +- packages/qwik-city/static/node/types.ts | 6 - .../qwik-city/static/{generator => }/types.ts | 38 +- .../static/{generator => }/worker-thread.ts | 82 +-- packages/qwik-city/utils/fs.ts | 8 +- packages/qwik/package.json | 3 +- packages/qwik/src/cli/add/print-add-help.ts | 13 +- .../qwik/src/cli/add/run-add-interactive.ts | 47 +- packages/qwik/src/cli/add/update-app.ts | 5 - .../qwik/src/cli/add/update-vite-config.ts | 52 -- packages/qwik/src/cli/code-mod/code-mod.ts | 488 ------------------ .../qwik/src/cli/code-mod/code-mod.unit.ts | 185 ------- packages/qwik/src/cli/types.ts | 12 +- packages/qwik/src/cli/utils/integrations.ts | 3 +- packages/qwik/src/optimizer/src/api.md | 24 + packages/qwik/src/optimizer/src/index.ts | 2 +- .../qwik/src/optimizer/src/plugins/plugin.ts | 15 +- .../qwik/src/optimizer/src/plugins/rollup.ts | 1 - .../qwik/src/optimizer/src/plugins/vite.ts | 53 +- scripts/api.ts | 39 +- scripts/create-qwik-cli.ts | 3 +- scripts/qwik-city.ts | 212 +++++++- scripts/submodule-cli.ts | 3 +- .../cloudflare-pages/.node-version | 0 .../cloudflare-pages/README.md | 0 .../adaptors/cloudflare-pages/vite.config.ts | 23 + .../cloudflare-pages/functions/[[path]].ts | 0 .../cloudflare-pages/gitignore | 0 .../adaptors/cloudflare-pages/package.json | 13 + .../cloudflare-pages}/public/_headers | 2 + .../cloudflare-pages/public/_redirects | 1 + .../cloudflare-pages/public/_routes.json | 13 + .../src/entry.cloudflare-pages.tsx | 7 + .../{servers => adaptors}/express/README.md | 0 .../express/adaptors/express/vite.config.ts | 23 + .../express/package.json | 2 +- .../express/src/entry.express.tsx | 7 +- .../netlify-edge/.node-version | 0 .../netlify-edge/README.md | 0 .../adaptors/netlify-edge/vite.config.ts | 26 + .../netlify-edge/netlify.toml | 0 starters/adaptors/netlify-edge/package.json | 13 + .../netlify-edge}/public/_headers | 0 .../netlify-edge/src/entry.netlify-edge.tsx | 5 + .../static-node => adaptors/static}/README.md | 0 .../static/adaptors/static/vite.config.ts | 20 + starters/adaptors/static/package.json | 9 + starters/adaptors/static/src/entry.static.tsx | 5 + .../servers/cloudflare-pages/package.json | 18 - .../src/entry.cloudflare-pages.tsx | 4 - starters/servers/netlify-edge/package.json | 27 - .../netlify-edge/src/entry.netlify-edge.tsx | 6 - .../static-node/package.json | 15 - .../static-node/src/entry.static.tsx | 10 - tsconfig.json | 17 +- 107 files changed, 1684 insertions(+), 1179 deletions(-) create mode 100644 packages/qwik-city/adaptors/cloudflare-pages/api.md create mode 100644 packages/qwik-city/adaptors/cloudflare-pages/vite/api-extractor.json create mode 100644 packages/qwik-city/adaptors/cloudflare-pages/vite/index.ts create mode 100644 packages/qwik-city/adaptors/express/api.md create mode 100644 packages/qwik-city/adaptors/express/vite/api-extractor.json create mode 100644 packages/qwik-city/adaptors/express/vite/index.ts create mode 100644 packages/qwik-city/adaptors/netlify-edge/api.md create mode 100644 packages/qwik-city/adaptors/netlify-edge/vite/api-extractor.json create mode 100644 packages/qwik-city/adaptors/netlify-edge/vite/index.ts create mode 100644 packages/qwik-city/adaptors/static/api.md create mode 100644 packages/qwik-city/adaptors/static/vite/api-extractor.json create mode 100644 packages/qwik-city/adaptors/static/vite/index.ts create mode 100644 packages/qwik-city/buildtime/vite/config.ts delete mode 100644 packages/qwik-city/runtime/src/entry.static.tsx rename packages/qwik-city/static/{node => }/api-extractor.json (67%) create mode 100644 packages/qwik-city/static/api.md create mode 100644 packages/qwik-city/static/deno/index.ts delete mode 100644 packages/qwik-city/static/generator/generate.ts delete mode 100644 packages/qwik-city/static/generator/index.ts create mode 100644 packages/qwik-city/static/index.ts rename packages/qwik-city/static/{generator => }/main-thread.ts (75%) delete mode 100644 packages/qwik-city/static/node/api.md delete mode 100644 packages/qwik-city/static/node/node-generate.ts delete mode 100644 packages/qwik-city/static/node/types.ts rename packages/qwik-city/static/{generator => }/types.ts (78%) rename packages/qwik-city/static/{generator => }/worker-thread.ts (74%) delete mode 100644 packages/qwik/src/cli/add/update-vite-config.ts delete mode 100644 packages/qwik/src/cli/code-mod/code-mod.ts delete mode 100644 packages/qwik/src/cli/code-mod/code-mod.unit.ts rename starters/{servers => adaptors}/cloudflare-pages/.node-version (100%) rename starters/{servers => adaptors}/cloudflare-pages/README.md (100%) create mode 100644 starters/adaptors/cloudflare-pages/adaptors/cloudflare-pages/vite.config.ts rename starters/{servers => adaptors}/cloudflare-pages/functions/[[path]].ts (100%) rename starters/{servers => adaptors}/cloudflare-pages/gitignore (100%) create mode 100644 starters/adaptors/cloudflare-pages/package.json rename starters/{servers/netlify-edge => adaptors/cloudflare-pages}/public/_headers (57%) create mode 100644 starters/adaptors/cloudflare-pages/public/_redirects create mode 100644 starters/adaptors/cloudflare-pages/public/_routes.json create mode 100644 starters/adaptors/cloudflare-pages/src/entry.cloudflare-pages.tsx rename starters/{servers => adaptors}/express/README.md (100%) create mode 100644 starters/adaptors/express/adaptors/express/vite.config.ts rename starters/{servers => adaptors}/express/package.json (73%) rename starters/{servers => adaptors}/express/src/entry.express.tsx (83%) rename starters/{servers => adaptors}/netlify-edge/.node-version (100%) rename starters/{servers => adaptors}/netlify-edge/README.md (100%) create mode 100644 starters/adaptors/netlify-edge/adaptors/netlify-edge/vite.config.ts rename starters/{servers => adaptors}/netlify-edge/netlify.toml (100%) create mode 100644 starters/adaptors/netlify-edge/package.json rename starters/{servers/cloudflare-pages => adaptors/netlify-edge}/public/_headers (100%) create mode 100644 starters/adaptors/netlify-edge/src/entry.netlify-edge.tsx rename starters/{static-generators/static-node => adaptors/static}/README.md (100%) create mode 100644 starters/adaptors/static/adaptors/static/vite.config.ts create mode 100644 starters/adaptors/static/package.json create mode 100644 starters/adaptors/static/src/entry.static.tsx delete mode 100644 starters/servers/cloudflare-pages/package.json delete mode 100644 starters/servers/cloudflare-pages/src/entry.cloudflare-pages.tsx delete mode 100644 starters/servers/netlify-edge/package.json delete mode 100644 starters/servers/netlify-edge/src/entry.netlify-edge.tsx delete mode 100644 starters/static-generators/static-node/package.json delete mode 100644 starters/static-generators/static-node/src/entry.static.tsx diff --git a/packages/qwik-city/adaptors/cloudflare-pages/api.md b/packages/qwik-city/adaptors/cloudflare-pages/api.md new file mode 100644 index 00000000000..9c3afd935a5 --- /dev/null +++ b/packages/qwik-city/adaptors/cloudflare-pages/api.md @@ -0,0 +1,24 @@ +## API Report File for "@builder.io/qwik-city" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { QwikManifest } from '@builder.io/qwik/optimizer'; +import type { SymbolMapper } from '@builder.io/qwik/optimizer'; +import type { SymbolMapperFn } from '@builder.io/qwik/optimizer'; + +// @alpha (undocumented) +export function cloudflarePagesAdaptor(opts?: CloudflarePagesAdaptorOptions): any; + +// @alpha (undocumented) +export interface CloudflarePagesAdaptorOptions { + // Warning: (ae-forgotten-export) The symbol "StaticGenerateRenderOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + staticGenerate?: StaticGenerateRenderOptions | true; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-city/adaptors/cloudflare-pages/vite/api-extractor.json b/packages/qwik-city/adaptors/cloudflare-pages/vite/api-extractor.json new file mode 100644 index 00000000000..439fb5fa116 --- /dev/null +++ b/packages/qwik-city/adaptors/cloudflare-pages/vite/api-extractor.json @@ -0,0 +1,15 @@ +{ + "$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-city/adaptors/cloudflare-pages/vite/index.d.ts", + "apiReport": { + "enabled": true, + "reportFileName": "api.md", + "reportFolder": "/packages/qwik-city/adaptors/cloudflare-pages/", + "reportTempFolder": "/dist-dev/api-extractor/qwik-city/adaptors/cloudflare-pages" + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/packages/qwik-city/lib/adaptors/cloudflare-pages/vite/index.d.ts" + } +} diff --git a/packages/qwik-city/adaptors/cloudflare-pages/vite/index.ts b/packages/qwik-city/adaptors/cloudflare-pages/vite/index.ts new file mode 100644 index 00000000000..b1d0ed386ed --- /dev/null +++ b/packages/qwik-city/adaptors/cloudflare-pages/vite/index.ts @@ -0,0 +1,105 @@ +import type { Plugin } from 'vite'; +import type { QwikVitePlugin } from '@builder.io/qwik/optimizer'; +import type { StaticGenerateOptions, StaticGenerateRenderOptions } from '../../../static'; +import { join } from 'path'; +import fs from 'fs'; + +/** + * @alpha + */ +export function cloudflarePagesAdaptor(opts: CloudflarePagesAdaptorOptions = {}): any { + let qwikVitePlugin: QwikVitePlugin | null = null; + let serverOutDir: string | null = null; + let renderModulePath: string | null = null; + let qwikCityPlanModulePath: string | null = null; + + async function generateBundles() { + const qwikVitePluginApi = qwikVitePlugin!.api; + const clientOutDir = qwikVitePluginApi.getClientOutDir()!; + + const serverPackageJsonPath = join(serverOutDir!, 'package.json'); + const serverPackageJsonCode = `{"type":"module"}`; + await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode); + + if (opts.staticGenerate) { + const staticGenerate = await import('../../../static'); + let generateOpts: StaticGenerateOptions = { + outDir: clientOutDir, + origin: process?.env?.CF_PAGES_URL || 'https://your.cloudflare.pages.dev', + renderModulePath: renderModulePath!, + qwikCityPlanModulePath: qwikCityPlanModulePath!, + }; + + if (typeof opts.staticGenerate === 'object') { + generateOpts = { + ...generateOpts, + ...opts.staticGenerate, + }; + } + + await staticGenerate.generate(generateOpts); + } + } + + const plugin: Plugin = { + name: 'vite-plugin-qwik-city-cloudflare-pages', + enforce: 'post', + apply: 'build', + + configResolved({ build, plugins }) { + qwikVitePlugin = plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; + if (!qwikVitePlugin) { + throw new Error('Missing vite-plugin-qwik'); + } + serverOutDir = build.outDir; + + if (build?.ssr !== true) { + throw new Error( + '"build.ssr" must be set to `true` in order to use the Cloudflare Pages adaptor.' + ); + } + + if (!build?.rollupOptions?.input) { + throw new Error( + '"build.rollupOptions.input" must be set in order to use the Cloudflare Pages adaptor.' + ); + } + }, + + generateBundle(_, bundles) { + for (const fileName in bundles) { + const chunk = bundles[fileName]; + if (chunk.type === 'chunk' && chunk.isEntry) { + if (chunk.name === 'entry.ssr') { + renderModulePath = join(serverOutDir!, fileName); + } else if (chunk.name === '@qwik-city-plan') { + qwikCityPlanModulePath = join(serverOutDir!, fileName); + } + } + } + + if (!renderModulePath) { + throw new Error( + 'Unable to fine "entry.ssr" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + if (!qwikCityPlanModulePath) { + throw new Error( + 'Unable to fine "@qwik-city-plan" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + }, + + async closeBundle() { + await generateBundles(); + }, + }; + return plugin; +} + +/** + * @alpha + */ +export interface CloudflarePagesAdaptorOptions { + staticGenerate?: StaticGenerateRenderOptions | true; +} diff --git a/packages/qwik-city/adaptors/express/api.md b/packages/qwik-city/adaptors/express/api.md new file mode 100644 index 00000000000..52325d33494 --- /dev/null +++ b/packages/qwik-city/adaptors/express/api.md @@ -0,0 +1,24 @@ +## API Report File for "@builder.io/qwik-city" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { QwikManifest } from '@builder.io/qwik/optimizer'; +import type { SymbolMapper } from '@builder.io/qwik/optimizer'; +import type { SymbolMapperFn } from '@builder.io/qwik/optimizer'; + +// @alpha (undocumented) +export function expressAdaptor(opts?: NetlifyEdgeAdaptorOptions): any; + +// @alpha (undocumented) +export interface NetlifyEdgeAdaptorOptions { + // Warning: (ae-forgotten-export) The symbol "StaticGenerateRenderOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + staticGenerate?: StaticGenerateRenderOptions | true; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-city/adaptors/express/vite/api-extractor.json b/packages/qwik-city/adaptors/express/vite/api-extractor.json new file mode 100644 index 00000000000..cea03a73fe1 --- /dev/null +++ b/packages/qwik-city/adaptors/express/vite/api-extractor.json @@ -0,0 +1,15 @@ +{ + "$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-city/adaptors/express/vite/index.d.ts", + "apiReport": { + "enabled": true, + "reportFileName": "api.md", + "reportFolder": "/packages/qwik-city/adaptors/express/", + "reportTempFolder": "/dist-dev/api-extractor/qwik-city/adaptors/express" + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/packages/qwik-city/lib/adaptors/express/vite/index.d.ts" + } +} diff --git a/packages/qwik-city/adaptors/express/vite/index.ts b/packages/qwik-city/adaptors/express/vite/index.ts new file mode 100644 index 00000000000..0008fe3a5c3 --- /dev/null +++ b/packages/qwik-city/adaptors/express/vite/index.ts @@ -0,0 +1,103 @@ +import type { Plugin } from 'vite'; +import type { QwikVitePlugin } from '@builder.io/qwik/optimizer'; +import type { StaticGenerateOptions, StaticGenerateRenderOptions } from '../../../static'; +import { join } from 'path'; +import fs from 'fs'; + +/** + * @alpha + */ +export function expressAdaptor(opts: NetlifyEdgeAdaptorOptions = {}): any { + let qwikVitePlugin: QwikVitePlugin | null = null; + let serverOutDir: string | null = null; + let renderModulePath: string | null = null; + let qwikCityPlanModulePath: string | null = null; + + async function generateBundles() { + const qwikVitePluginApi = qwikVitePlugin!.api; + const clientOutDir = qwikVitePluginApi.getClientOutDir()!; + + const serverPackageJsonPath = join(serverOutDir!, 'package.json'); + const serverPackageJsonCode = `{"type":"module"}`; + await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode); + + if (opts.staticGenerate) { + const staticGenerate = await import('../../../static'); + let generateOpts: StaticGenerateOptions = { + outDir: clientOutDir, + origin: process?.env?.URL || 'https://yoursitename.example.com', + renderModulePath: renderModulePath!, + qwikCityPlanModulePath: qwikCityPlanModulePath!, + }; + + if (typeof opts.staticGenerate === 'object') { + generateOpts = { + ...generateOpts, + ...opts.staticGenerate, + }; + } + + await staticGenerate.generate(generateOpts); + } + } + + const plugin: Plugin = { + name: 'vite-plugin-qwik-city-express', + enforce: 'post', + apply: 'build', + + configResolved({ build, plugins }) { + qwikVitePlugin = plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; + if (!qwikVitePlugin) { + throw new Error('Missing vite-plugin-qwik'); + } + serverOutDir = build.outDir; + + if (build?.ssr !== true) { + throw new Error('"build.ssr" must be set to `true` in order to use the Express adaptor.'); + } + + if (!build?.rollupOptions?.input) { + throw new Error( + '"build.rollupOptions.input" must be set in order to use the Express adaptor.' + ); + } + }, + + generateBundle(_, bundles) { + for (const fileName in bundles) { + const chunk = bundles[fileName]; + if (chunk.type === 'chunk' && chunk.isEntry) { + if (chunk.name === 'entry.ssr') { + renderModulePath = join(serverOutDir!, fileName); + } else if (chunk.name === '@qwik-city-plan') { + qwikCityPlanModulePath = join(serverOutDir!, fileName); + } + } + } + + if (!renderModulePath) { + throw new Error( + 'Unable to fine "entry.ssr" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + if (!qwikCityPlanModulePath) { + throw new Error( + 'Unable to fine "@qwik-city-plan" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + }, + + async closeBundle() { + await generateBundles(); + }, + }; + return plugin; +} + +/** + * @alpha + */ +export interface NetlifyEdgeAdaptorOptions { + staticGenerate?: StaticGenerateRenderOptions | true; +} diff --git a/packages/qwik-city/adaptors/netlify-edge/api.md b/packages/qwik-city/adaptors/netlify-edge/api.md new file mode 100644 index 00000000000..4ea5ef47ba0 --- /dev/null +++ b/packages/qwik-city/adaptors/netlify-edge/api.md @@ -0,0 +1,24 @@ +## API Report File for "@builder.io/qwik-city" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { QwikManifest } from '@builder.io/qwik/optimizer'; +import type { SymbolMapper } from '@builder.io/qwik/optimizer'; +import type { SymbolMapperFn } from '@builder.io/qwik/optimizer'; + +// @alpha (undocumented) +export function netifyEdgeAdaptor(opts?: NetlifyEdgeAdaptorOptions): any; + +// @alpha (undocumented) +export interface NetlifyEdgeAdaptorOptions { + // Warning: (ae-forgotten-export) The symbol "StaticGenerateRenderOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + staticGenerate?: StaticGenerateRenderOptions | true; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-city/adaptors/netlify-edge/vite/api-extractor.json b/packages/qwik-city/adaptors/netlify-edge/vite/api-extractor.json new file mode 100644 index 00000000000..d9403303388 --- /dev/null +++ b/packages/qwik-city/adaptors/netlify-edge/vite/api-extractor.json @@ -0,0 +1,15 @@ +{ + "$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-city/adaptors/netlify-edge/vite/index.d.ts", + "apiReport": { + "enabled": true, + "reportFileName": "api.md", + "reportFolder": "/packages/qwik-city/adaptors/netlify-edge/", + "reportTempFolder": "/dist-dev/api-extractor/qwik-city/adaptors/netlify-edge" + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/packages/qwik-city/lib/adaptors/netlify-edge/vite/index.d.ts" + } +} diff --git a/packages/qwik-city/adaptors/netlify-edge/vite/index.ts b/packages/qwik-city/adaptors/netlify-edge/vite/index.ts new file mode 100644 index 00000000000..e3ed10f68b2 --- /dev/null +++ b/packages/qwik-city/adaptors/netlify-edge/vite/index.ts @@ -0,0 +1,105 @@ +import type { Plugin } from 'vite'; +import type { QwikVitePlugin } from '@builder.io/qwik/optimizer'; +import type { StaticGenerateOptions, StaticGenerateRenderOptions } from '../../../static'; +import { join } from 'path'; +import fs from 'fs'; + +/** + * @alpha + */ +export function netifyEdgeAdaptor(opts: NetlifyEdgeAdaptorOptions = {}): any { + let qwikVitePlugin: QwikVitePlugin | null = null; + let serverOutDir: string | null = null; + let renderModulePath: string | null = null; + let qwikCityPlanModulePath: string | null = null; + + async function generateBundles() { + const qwikVitePluginApi = qwikVitePlugin!.api; + const clientOutDir = qwikVitePluginApi.getClientOutDir()!; + + const serverPackageJsonPath = join(serverOutDir!, 'package.json'); + const serverPackageJsonCode = `{"type":"module"}`; + await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode); + + if (opts.staticGenerate) { + const staticGenerate = await import('../../../static'); + let generateOpts: StaticGenerateOptions = { + outDir: clientOutDir, + origin: process?.env?.URL || 'https://yoursitename.netlify.app', + renderModulePath: renderModulePath!, + qwikCityPlanModulePath: qwikCityPlanModulePath!, + }; + + if (typeof opts.staticGenerate === 'object') { + generateOpts = { + ...generateOpts, + ...opts.staticGenerate, + }; + } + + await staticGenerate.generate(generateOpts); + } + } + + const plugin: Plugin = { + name: 'vite-plugin-qwik-city-netlify-edge', + enforce: 'post', + apply: 'build', + + configResolved({ build, plugins }) { + qwikVitePlugin = plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; + if (!qwikVitePlugin) { + throw new Error('Missing vite-plugin-qwik'); + } + serverOutDir = build.outDir; + + if (build?.ssr !== true) { + throw new Error( + '"build.ssr" must be set to `true` in order to use the Netlify Edge adaptor.' + ); + } + + if (!build?.rollupOptions?.input) { + throw new Error( + '"build.rollupOptions.input" must be set in order to use the Netlify Edge adaptor.' + ); + } + }, + + generateBundle(_, bundles) { + for (const fileName in bundles) { + const chunk = bundles[fileName]; + if (chunk.type === 'chunk' && chunk.isEntry) { + if (chunk.name === 'entry.ssr') { + renderModulePath = join(serverOutDir!, fileName); + } else if (chunk.name === '@qwik-city-plan') { + qwikCityPlanModulePath = join(serverOutDir!, fileName); + } + } + } + + if (!renderModulePath) { + throw new Error( + 'Unable to fine "entry.ssr" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + if (!qwikCityPlanModulePath) { + throw new Error( + 'Unable to fine "@qwik-city-plan" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + }, + + async closeBundle() { + await generateBundles(); + }, + }; + return plugin; +} + +/** + * @alpha + */ +export interface NetlifyEdgeAdaptorOptions { + staticGenerate?: StaticGenerateRenderOptions | true; +} diff --git a/packages/qwik-city/adaptors/static/api.md b/packages/qwik-city/adaptors/static/api.md new file mode 100644 index 00000000000..a84c094fa55 --- /dev/null +++ b/packages/qwik-city/adaptors/static/api.md @@ -0,0 +1,22 @@ +## API Report File for "@builder.io/qwik-city" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { QwikManifest } from '@builder.io/qwik/optimizer'; +import type { SymbolMapper } from '@builder.io/qwik/optimizer'; +import type { SymbolMapperFn } from '@builder.io/qwik/optimizer'; + +// @alpha (undocumented) +export function staticAdaptor(opts: StaticGenerateAdaptorOptions): any; + +// Warning: (ae-forgotten-export) The symbol "StaticGenerateRenderOptions" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export interface StaticGenerateAdaptorOptions extends Omit { +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-city/adaptors/static/vite/api-extractor.json b/packages/qwik-city/adaptors/static/vite/api-extractor.json new file mode 100644 index 00000000000..4a738efd6a7 --- /dev/null +++ b/packages/qwik-city/adaptors/static/vite/api-extractor.json @@ -0,0 +1,15 @@ +{ + "$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-city/adaptors/static/vite/index.d.ts", + "apiReport": { + "enabled": true, + "reportFileName": "api.md", + "reportFolder": "/packages/qwik-city/adaptors/static/", + "reportTempFolder": "/dist-dev/api-extractor/qwik-city/adaptors/static" + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/packages/qwik-city/lib/adaptors/static/vite/index.d.ts" + } +} diff --git a/packages/qwik-city/adaptors/static/vite/index.ts b/packages/qwik-city/adaptors/static/vite/index.ts new file mode 100644 index 00000000000..e385c62fb0a --- /dev/null +++ b/packages/qwik-city/adaptors/static/vite/index.ts @@ -0,0 +1,93 @@ +import type { Plugin } from 'vite'; +import type { QwikVitePlugin } from '@builder.io/qwik/optimizer'; +import type { StaticGenerateRenderOptions } from '../../../static'; +import { join } from 'path'; +import fs from 'fs'; + +/** + * @alpha + */ +export function staticAdaptor(opts: StaticGenerateAdaptorOptions): any { + let qwikVitePlugin: QwikVitePlugin | null = null; + let serverOutDir: string | null = null; + let ssrOutputPath: string | null = null; + let qwikCityPlanOutputPath: string | null = null; + + async function generateBundles() { + const qwikVitePluginApi = qwikVitePlugin!.api; + const clientOutDir = qwikVitePluginApi.getClientOutDir()!; + + const serverPackageJsonPath = join(serverOutDir!, 'package.json'); + const serverPackageJsonCode = `{"type":"module"}`; + await fs.promises.writeFile(serverPackageJsonPath, serverPackageJsonCode); + + const staticGenerate = await import('../../../static'); + + await staticGenerate.generate({ + renderModulePath: ssrOutputPath!, + qwikCityPlanModulePath: qwikCityPlanOutputPath!, + outDir: clientOutDir, + ...opts, + }); + } + + const plugin: Plugin = { + name: 'vite-plugin-qwik-city-static-generate', + enforce: 'post', + apply: 'build', + + configResolved({ build, plugins }) { + qwikVitePlugin = plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; + if (!qwikVitePlugin) { + throw new Error('Missing vite-plugin-qwik'); + } + serverOutDir = build.outDir; + + if (build?.ssr !== true) { + throw new Error( + '"build.ssr" must be set to `true` in order to use the Static Generate adaptor.' + ); + } + + if (!build?.rollupOptions?.input) { + throw new Error( + '"build.rollupOptions.input" must be set in order to use the Static Generate adaptor.' + ); + } + }, + + generateBundle(_, bundles) { + for (const fileName in bundles) { + const chunk = bundles[fileName]; + if (chunk.type === 'chunk' && chunk.isEntry) { + if (chunk.name === 'entry.ssr') { + ssrOutputPath = join(serverOutDir!, fileName); + } else if (chunk.name === '@qwik-city-plan') { + qwikCityPlanOutputPath = join(serverOutDir!, fileName); + } + } + } + + if (!ssrOutputPath) { + throw new Error( + 'Unable to fine "entry.ssr" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + if (!qwikCityPlanOutputPath) { + throw new Error( + 'Unable to fine "@qwik-city-plan" entry point. Did you forget to add it to "build.rollupOptions.input"?' + ); + } + }, + + async closeBundle() { + await generateBundles(); + }, + }; + return plugin; +} + +/** + * @alpha + */ +export interface StaticGenerateAdaptorOptions extends Omit {} diff --git a/packages/qwik-city/api-extractor.json b/packages/qwik-city/api-extractor.json index 924c475908a..e70c2064879 100644 --- a/packages/qwik-city/api-extractor.json +++ b/packages/qwik-city/api-extractor.json @@ -8,7 +8,7 @@ "esModuleInterop": true, "skipLibCheck": true }, - "include": ["dist-dev/dts-out/packages/qwik-city/"] + "include": ["dist-dev/dts-out/packages/qwik-city/**/*.d.ts"] } }, "docModel": { diff --git a/packages/qwik-city/buildtime/runtime-generation/generate-qwik-city-plan.ts b/packages/qwik-city/buildtime/runtime-generation/generate-qwik-city-plan.ts index f074c00be46..d7d8a09b027 100644 --- a/packages/qwik-city/buildtime/runtime-generation/generate-qwik-city-plan.ts +++ b/packages/qwik-city/buildtime/runtime-generation/generate-qwik-city-plan.ts @@ -1,3 +1,4 @@ +import type { QwikVitePlugin } from '../../../qwik/src/optimizer/src'; import type { BuildContext } from '../types'; import { createEntries } from './generate-entries'; import { createMenus } from './generate-menus'; @@ -6,13 +7,13 @@ import { createRoutes } from './generate-routes'; /** * Generates the Qwik City Plan runtime code */ -export function generateQwikCityPlan(ctx: BuildContext) { +export function generateQwikCityPlan(ctx: BuildContext, qwikPlugin: QwikVitePlugin) { const esmImports: string[] = []; const c: string[] = []; c.push(`\n/** Qwik City Plan */`); - createRoutes(ctx, c, esmImports); + createRoutes(ctx, qwikPlugin, c, esmImports); createMenus(ctx, c, esmImports); @@ -24,5 +25,7 @@ export function generateQwikCityPlan(ctx: BuildContext) { c.push(`export const cacheModules = ${JSON.stringify(!ctx.isDevServer)};`); + c.push(`export default { routes, menus, trailingSlash, basePathname, cacheModules };`); + return esmImports.join('\n') + c.join('\n'); } diff --git a/packages/qwik-city/buildtime/runtime-generation/generate-routes.ts b/packages/qwik-city/buildtime/runtime-generation/generate-routes.ts index 016019818b4..f3649448218 100644 --- a/packages/qwik-city/buildtime/runtime-generation/generate-routes.ts +++ b/packages/qwik-city/buildtime/runtime-generation/generate-routes.ts @@ -1,9 +1,15 @@ +import type { QwikVitePlugin } from '../../../qwik/src/optimizer/src'; import type { BuildContext, BuildRoute } from '../types'; import { isModuleExt, isPageExt, removeExtension } from '../../utils/fs'; import { getImportPath } from './utils'; import type { QwikManifest } from '@builder.io/qwik/optimizer'; -export function createRoutes(ctx: BuildContext, c: string[], esmImports: string[]) { +export function createRoutes( + ctx: BuildContext, + qwikPlugin: QwikVitePlugin, + c: string[], + esmImports: string[] +) { const isSsr = ctx.target === 'ssr'; const includeEndpoints = isSsr; const dynamicImports = ctx.target === 'client'; @@ -48,14 +54,19 @@ export function createRoutes(ctx: BuildContext, c: string[], esmImports: string[ } if (loaders.length > 0) { - c.push(` ${createRouteData(route, loaders, isSsr)},`); + c.push(` ${createRouteData(qwikPlugin, route, loaders, isSsr)},`); } } c.push(`];`); } -function createRouteData(r: BuildRoute, loaders: string[], isSsr: boolean) { +function createRouteData( + qwikPlugin: QwikVitePlugin, + r: BuildRoute, + loaders: string[], + isSsr: boolean +) { const pattern = r.pattern.toString(); const moduleLoaders = `[ ${loaders.join(', ')} ]`; @@ -65,7 +76,7 @@ function createRouteData(r: BuildRoute, loaders: string[], isSsr: boolean) { const paramNames = r.paramNames && r.paramNames.length > 0 ? JSON.stringify(r.paramNames) : `undefined`; const originalPathname = JSON.stringify(r.pathname); - const clientBundleNames = JSON.stringify(getClientRouteBundleNames(r)); + const clientBundleNames = JSON.stringify(getClientRouteBundleNames(qwikPlugin, r)); // SSR also adds the originalPathname and clientBundleNames to the RouteData return `[ ${pattern}, ${moduleLoaders}, ${paramNames}, ${originalPathname}, ${clientBundleNames} ]`; @@ -81,11 +92,11 @@ function createRouteData(r: BuildRoute, loaders: string[], isSsr: boolean) { return `[ ${pattern}, ${moduleLoaders} ]`; } -function getClientRouteBundleNames(r: BuildRoute) { +function getClientRouteBundleNames(qwikPlugin: QwikVitePlugin, r: BuildRoute) { const bundlesNames: string[] = []; - // TODO: Better way to get QwikManifest - const manifest: QwikManifest = (globalThis as any).QWIK_MANIFEST; + // TODO: Remove globalThis that was previously used. Left in for backwards compatibility. + const manifest: QwikManifest = (globalThis as any).QWIK_MANIFEST || qwikPlugin.api.getManifest(); if (manifest) { const manifestBundleNames = Object.keys(manifest.bundles); diff --git a/packages/qwik-city/buildtime/vite/api.md b/packages/qwik-city/buildtime/vite/api.md index c1e52a28984..ecdee6434ff 100644 --- a/packages/qwik-city/buildtime/vite/api.md +++ b/packages/qwik-city/buildtime/vite/api.md @@ -5,6 +5,11 @@ ```ts import { CompileOptions } from '@mdx-js/mdx/lib/compile'; +import { ConfigEnv } from 'vite'; +import { UserConfigExport } from 'vite'; + +// @alpha (undocumented) +export function extendConfig(baseConfigExport: UserConfigExport, serverConfigExport: UserConfigExport): (env: ConfigEnv) => Promise>; // @alpha (undocumented) export type MdxOptions = CompileOptions; @@ -12,6 +17,16 @@ export type MdxOptions = CompileOptions; // @alpha (undocumented) export function qwikCity(userOpts?: QwikCityVitePluginOptions): any; +// @alpha (undocumented) +export interface QwikCityPlugin { + // Warning: (ae-forgotten-export) The symbol "QwikCityPluginApi" needs to be exported by the entry point index.d.ts + // + // (undocumented) + api: QwikCityPluginApi; + // (undocumented) + name: 'vite-plugin-qwik-city'; +} + // Warning: (ae-forgotten-export) The symbol "PluginOptions" needs to be exported by the entry point index.d.ts // // @alpha (undocumented) diff --git a/packages/qwik-city/buildtime/vite/config.ts b/packages/qwik-city/buildtime/vite/config.ts new file mode 100644 index 00000000000..f4f6b3111e6 --- /dev/null +++ b/packages/qwik-city/buildtime/vite/config.ts @@ -0,0 +1,24 @@ +// import { basename } from 'path'; +import { ConfigEnv, mergeConfig, UserConfigExport } from 'vite'; + +/** + * @alpha + */ +export function extendConfig( + baseConfigExport: UserConfigExport, + serverConfigExport: UserConfigExport +) { + return async (env: ConfigEnv) => { + let resolvedBase = await baseConfigExport; + if (typeof resolvedBase === 'function') { + resolvedBase = await resolvedBase(env); + } + + let resolvedServer = await serverConfigExport; + if (typeof resolvedServer === 'function') { + resolvedServer = await resolvedServer(env); + } + + return mergeConfig(resolvedBase, resolvedServer); + }; +} diff --git a/packages/qwik-city/buildtime/vite/dev-server.ts b/packages/qwik-city/buildtime/vite/dev-server.ts index d5d7a9f5b8e..b023eb72b27 100644 --- a/packages/qwik-city/buildtime/vite/dev-server.ts +++ b/packages/qwik-city/buildtime/vite/dev-server.ts @@ -96,7 +96,6 @@ export function ssrDevMiddleware(ctx: BuildContext, server: ViteDevServer) { requestCtx, params, routeModules, - {}, ctx.opts.trailingSlash, ctx.opts.basePathname ); diff --git a/packages/qwik-city/buildtime/vite/index.ts b/packages/qwik-city/buildtime/vite/index.ts index f95a9e4ac6a..6f7d0b4a0f0 100644 --- a/packages/qwik-city/buildtime/vite/index.ts +++ b/packages/qwik-city/buildtime/vite/index.ts @@ -1,2 +1,3 @@ +export { extendConfig } from './config'; export { qwikCity } from './plugin'; -export type { MdxOptions, QwikCityVitePluginOptions } from './types'; +export type { MdxOptions, QwikCityPlugin, QwikCityVitePluginOptions } from './types'; diff --git a/packages/qwik-city/buildtime/vite/plugin.ts b/packages/qwik-city/buildtime/vite/plugin.ts index a8a33dcc0fe..25819076584 100644 --- a/packages/qwik-city/buildtime/vite/plugin.ts +++ b/packages/qwik-city/buildtime/vite/plugin.ts @@ -12,7 +12,7 @@ import { removeExtension, } from '../../utils/fs'; import { validatePlugin } from './validate-plugin'; -import type { QwikCityVitePluginOptions } from './types'; +import type { QwikCityPluginApi, QwikCityVitePluginOptions } from './types'; import { build } from '../build'; import { dev404Middleware, ssrDevMiddleware, staticDistMiddleware } from './dev-server'; import { SERVER_ENDPOINT_FNS, stripServerEndpoints } from '../../utils/strip-server-endpoints'; @@ -20,27 +20,38 @@ import { transformMenu } from '../markdown/menu'; import { generateQwikCityEntries } from '../runtime-generation/generate-entries'; import { patchGlobalFetch } from '../../middleware/node/node-fetch'; import type { QwikManifest } from '@builder.io/qwik/optimizer'; -import { readFile, writeFile } from 'fs/promises'; +import fs from 'fs'; import { generateServiceWorkerRegister, prependManifestToServiceWorker, } from '../runtime-generation/generate-service-worker'; import type { RollupError } from 'rollup'; +import type { QwikVitePlugin } from '../../../qwik/src/optimizer/src'; /** * @alpha */ -export function qwikCity(userOpts?: QwikCityVitePluginOptions) { +export function qwikCity(userOpts?: QwikCityVitePluginOptions): any { patchGlobalFetch(); let ctx: BuildContext | null = null; let mdxTransform: MdxTransform | null = null; let rootDir: string | null = null; + let qwikPlugin: QwikVitePlugin | null; + + const api: QwikCityPluginApi = { + getRoutes: () => { + return ctx?.routes.slice() ?? []; + }, + getServiceWorkers: () => { + return ctx?.serviceWorkers.slice() ?? []; + }, + }; const plugin: Plugin = { name: 'vite-plugin-qwik-city', - enforce: 'pre', + api, config() { const updatedViteConfig: UserConfig = { @@ -69,6 +80,11 @@ export function qwikCity(userOpts?: QwikCityVitePluginOptions) { await validatePlugin(ctx.opts); mdxTransform = await createMdxTransformer(ctx); + + qwikPlugin = config.plugins.find((p) => p.name === 'vite-plugin-qwik') as QwikVitePlugin; + if (!qwikPlugin) { + throw new Error('Missing vite-plugin-qwik'); + } }, configureServer(server) { @@ -121,7 +137,7 @@ export function qwikCity(userOpts?: QwikCityVitePluginOptions) { if (isCityPlan) { // @qwik-city-plan - return generateQwikCityPlan(ctx); + return generateQwikCityPlan(ctx, qwikPlugin!); } if (isSwRegister) { @@ -205,21 +221,24 @@ export function qwikCity(userOpts?: QwikCityVitePluginOptions) { } }, - async writeBundle() { + async closeBundle() { if (ctx?.target === 'ssr') { // ssr build - // TODO: Better way to get QwikManifest - const manifest: QwikManifest = (globalThis as any).QWIK_MANIFEST; - const clientOutDir: string = (globalThis as any).QWIK_CLIENT_OUT_DIR; + // TODO: Remove globalThis that was previously used. Left in for backwards compatibility. + const manifest: QwikManifest = + (globalThis as any).QWIK_MANIFEST || qwikPlugin!.api.getManifest(); + const clientOutDir: string = + (globalThis as any).QWIK_CLIENT_OUT_DIR || qwikPlugin!.api.getClientOutDir(); + if (manifest && clientOutDir) { for (const swEntry of ctx.serviceWorkers) { try { const swClientDistPath = join(clientOutDir, swEntry.chunkFileName); - const swCode = await readFile(swClientDistPath, 'utf-8'); + const swCode = await fs.promises.readFile(swClientDistPath, 'utf-8'); const swCodeUpdate = prependManifestToServiceWorker(ctx, manifest, swCode); if (swCodeUpdate) { - await writeFile(swClientDistPath, swCodeUpdate); + await fs.promises.writeFile(swClientDistPath, swCodeUpdate); } } catch (e) { console.error(e); @@ -230,7 +249,7 @@ export function qwikCity(userOpts?: QwikCityVitePluginOptions) { }, }; - return plugin as any; + return plugin; } const QWIK_CITY_PLAN_ID = '@qwik-city-plan'; diff --git a/packages/qwik-city/buildtime/vite/types.ts b/packages/qwik-city/buildtime/vite/types.ts index a1c8c785b88..69a887e6ce4 100644 --- a/packages/qwik-city/buildtime/vite/types.ts +++ b/packages/qwik-city/buildtime/vite/types.ts @@ -1,5 +1,5 @@ import type { MdxTransform } from '../markdown/mdx'; -import type { BuildContext, PluginOptions, MdxPlugins } from '../types'; +import type { BuildContext, BuildEntry, BuildRoute, PluginOptions, MdxPlugins } from '../types'; /** * @alpha @@ -20,3 +20,19 @@ export interface PluginContext { cityPlanCode: string | null; mdxTransform: MdxTransform | null; } + +/** + * @alpha + */ +export interface QwikCityPlugin { + name: 'vite-plugin-qwik-city'; + api: QwikCityPluginApi; +} + +/** + * @alpha + */ +export interface QwikCityPluginApi { + getRoutes: () => BuildRoute[]; + getServiceWorkers: () => BuildEntry[]; +} diff --git a/packages/qwik-city/middleware/cloudflare-pages/api.md b/packages/qwik-city/middleware/cloudflare-pages/api.md index ef674be4db4..b3085c775d7 100644 --- a/packages/qwik-city/middleware/cloudflare-pages/api.md +++ b/packages/qwik-city/middleware/cloudflare-pages/api.md @@ -6,6 +6,10 @@ import type { Render } from '@builder.io/qwik/server'; import type { RenderOptions } from '@builder.io/qwik/server'; +import type { RenderOptions as RenderOptions_2 } from '@builder.io/qwik'; + +// @alpha (undocumented) +export function createQwikCity(opts: QwikCityCloudflarePagesOptions): ({ request, next, env, waitUntil }: EventPluginContext) => Promise; // @alpha (undocumented) export interface EventPluginContext { @@ -19,13 +23,13 @@ export interface EventPluginContext { waitUntil: (promise: Promise) => void; } -// @alpha (undocumented) -export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions): ({ request, next, env, waitUntil }: EventPluginContext) => Promise; +// @alpha @deprecated (undocumented) +export function qwikCity(render: Render, opts?: RenderOptions_2): ({ request, next, env, waitUntil }: EventPluginContext) => Promise; -// Warning: (ae-forgotten-export) The symbol "QwikCityRequestOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "QwikCityHandlerOptions" needs to be exported by the entry point index.d.ts // // @alpha (undocumented) -export interface QwikCityCloudflarePagesOptions extends QwikCityRequestOptions { +export interface QwikCityCloudflarePagesOptions extends QwikCityHandlerOptions { } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-city/middleware/cloudflare-pages/index.ts b/packages/qwik-city/middleware/cloudflare-pages/index.ts index 25bf0fb98b5..4ae9ab1db8e 100644 --- a/packages/qwik-city/middleware/cloudflare-pages/index.ts +++ b/packages/qwik-city/middleware/cloudflare-pages/index.ts @@ -1,13 +1,15 @@ -import type { QwikCityRequestOptions, QwikCityRequestContext } from '../request-handler/types'; +import type { QwikCityHandlerOptions, QwikCityRequestContext } from '../request-handler/types'; import { notFoundHandler, requestHandler } from '../request-handler'; +import type { RenderOptions } from '@builder.io/qwik'; import type { Render } from '@builder.io/qwik/server'; +import qwikCityPlan from '@qwik-city-plan'; // @builder.io/qwik-city/middleware/cloudflare-pages /** * @alpha */ -export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions) { +export function createQwikCity(opts: QwikCityCloudflarePagesOptions) { async function onRequest({ request, next, env, waitUntil }: EventPluginContext) { try { const url = new URL(request.url); @@ -66,6 +68,7 @@ export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions) } }); }, + platform: env, }; // check if the next middleware is able to handle this request @@ -78,7 +81,7 @@ export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions) // next middleware unable to handle request // send request to qwik city request handler - const handledResponse = await requestHandler(requestCtx, render, env, opts); + const handledResponse = await requestHandler(requestCtx, opts); if (handledResponse) { return handledResponse; } @@ -88,6 +91,7 @@ export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions) const notFoundResponse = await notFoundHandler(requestCtx); return notFoundResponse; } catch (e: any) { + console.error(e); return new Response(String(e || 'Error'), { status: 500, headers: { 'Content-Type': 'text/plain; charset=utf-8' }, @@ -101,7 +105,7 @@ export function qwikCity(render: Render, opts?: QwikCityCloudflarePagesOptions) /** * @alpha */ -export interface QwikCityCloudflarePagesOptions extends QwikCityRequestOptions {} +export interface QwikCityCloudflarePagesOptions extends QwikCityHandlerOptions {} /** * @alpha @@ -112,3 +116,21 @@ export interface EventPluginContext { next: (input?: Request | string, init?: RequestInit) => Promise; env: Record; } + +/** + * @alpha + * @deprecated Please use `createQwikCity()` instead. + * + * Example: + * + * ```ts + * import { createQwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'; + * import qwikCityPlan from '@qwik-city-plan'; + * import render from './entry.ssr'; + * + * export const onRequest = createQwikCity({ render, qwikCityPlan }); + * ``` + */ +export function qwikCity(render: Render, opts?: RenderOptions) { + return createQwikCity({ render, qwikCityPlan, ...opts }); +} diff --git a/packages/qwik-city/middleware/netlify-edge/api.md b/packages/qwik-city/middleware/netlify-edge/api.md index 87b9bb146cd..6d9ce27aa27 100644 --- a/packages/qwik-city/middleware/netlify-edge/api.md +++ b/packages/qwik-city/middleware/netlify-edge/api.md @@ -7,18 +7,22 @@ import type { Context } from '@netlify/edge-functions'; import type { Render } from '@builder.io/qwik/server'; import type { RenderOptions } from '@builder.io/qwik/server'; +import type { RenderOptions as RenderOptions_2 } from '@builder.io/qwik'; + +// @alpha (undocumented) +export function createQwikCity(opts: QwikCityNetlifyOptions): (request: Request, context: Context) => Promise; // @alpha (undocumented) export interface EventPluginContext extends Context { } -// @alpha (undocumented) -export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions): (request: Request, context: Context) => Promise; +// @alpha @deprecated (undocumented) +export function qwikCity(render: Render, opts?: RenderOptions_2): (request: Request, context: Context) => Promise; -// Warning: (ae-forgotten-export) The symbol "QwikCityRequestOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "QwikCityHandlerOptions" needs to be exported by the entry point index.d.ts // // @alpha (undocumented) -export interface QwikCityNetlifyOptions extends QwikCityRequestOptions { +export interface QwikCityNetlifyOptions extends QwikCityHandlerOptions { } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-city/middleware/netlify-edge/index.ts b/packages/qwik-city/middleware/netlify-edge/index.ts index 3f28be279c7..c7c1a6f5c30 100644 --- a/packages/qwik-city/middleware/netlify-edge/index.ts +++ b/packages/qwik-city/middleware/netlify-edge/index.ts @@ -1,14 +1,16 @@ import type { Context } from '@netlify/edge-functions'; -import type { QwikCityRequestOptions, QwikCityRequestContext } from '../request-handler/types'; +import type { QwikCityHandlerOptions, QwikCityRequestContext } from '../request-handler/types'; import { notFoundHandler, requestHandler } from '../request-handler'; import type { Render } from '@builder.io/qwik/server'; +import type { RenderOptions } from '@builder.io/qwik'; +import qwikCityPlan from '@qwik-city-plan'; // @builder.io/qwik-city/middleware/netlify-edge /** * @alpha */ -export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions) { +export function createQwikCity(opts: QwikCityNetlifyOptions) { async function onRequest(request: Request, context: Context) { try { const requestCtx: QwikCityRequestContext = { @@ -43,6 +45,7 @@ export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions) { }); }); }, + platform: context, }; // check if the next middleware is able to handle this request @@ -55,7 +58,7 @@ export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions) { // next middleware unable to handle request // send request to qwik city request handler - const handledResponse = await requestHandler(requestCtx, render, context, opts); + const handledResponse = await requestHandler(requestCtx, opts); if (handledResponse) { return handledResponse; } @@ -65,6 +68,7 @@ export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions) { const notFoundResponse = await notFoundHandler(requestCtx); return notFoundResponse; } catch (e: any) { + console.error(e); return new Response(String(e || 'Error'), { status: 500, headers: { 'Content-Type': 'text/plain; charset=utf-8' }, @@ -78,9 +82,27 @@ export function qwikCity(render: Render, opts?: QwikCityNetlifyOptions) { /** * @alpha */ -export interface QwikCityNetlifyOptions extends QwikCityRequestOptions {} +export interface QwikCityNetlifyOptions extends QwikCityHandlerOptions {} /** * @alpha */ export interface EventPluginContext extends Context {} + +/** + * @alpha + * @deprecated Please use `createQwikCity()` instead. + * + * Example: + * + * ```ts + * import { createQwikCity } from '@builder.io/qwik-city/middleware/netlify-edge'; + * import qwikCityPlan from '@qwik-city-plan'; + * import render from './entry.ssr'; + * + * export default createQwikCity({ render, qwikCityPlan }); + * ``` + */ +export function qwikCity(render: Render, opts?: RenderOptions) { + return createQwikCity({ render, qwikCityPlan, ...opts }); +} diff --git a/packages/qwik-city/middleware/node/api.md b/packages/qwik-city/middleware/node/api.md index 8efe42aac26..7649be92dd7 100644 --- a/packages/qwik-city/middleware/node/api.md +++ b/packages/qwik-city/middleware/node/api.md @@ -4,27 +4,36 @@ ```ts +/// + import type { IncomingMessage } from 'http'; import type { Render } from '@builder.io/qwik/server'; import type { RenderOptions } from '@builder.io/qwik/server'; +import type { RenderOptions as RenderOptions_2 } from '@builder.io/qwik'; import type { ServerResponse } from 'http'; +// @alpha (undocumented) +export function createQwikCity(opts: QwikCityNodeRequestOptions): { + router: (req: IncomingMessage, res: ServerResponse, next: NodeRequestNextFunction) => Promise; + notFound: (req: IncomingMessage, res: ServerResponse, next: (e: any) => void) => Promise; +}; + // @alpha (undocumented) export interface NodeRequestNextFunction { // (undocumented) (err?: any): void; } -// @alpha (undocumented) -export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions): { - router: (req: IncomingMessage, res: ServerResponse, next: NodeRequestNextFunction) => Promise; - notFound: (req: IncomingMessage, res: ServerResponse, next: (e: any) => void) => Promise; +// @alpha @deprecated (undocumented) +export function qwikCity(render: Render, opts?: RenderOptions_2): { + router: (req: IncomingMessage, res: ServerResponse, next: NodeRequestNextFunction) => Promise; + notFound: (req: IncomingMessage, res: ServerResponse, next: (e: any) => void) => Promise; }; -// Warning: (ae-forgotten-export) The symbol "QwikCityRequestOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "QwikCityHandlerOptions" needs to be exported by the entry point index.d.ts // // @alpha (undocumented) -export interface QwikCityNodeRequestOptions extends QwikCityRequestOptions { +export interface QwikCityNodeRequestOptions extends QwikCityHandlerOptions { } // (No @packageDocumentation comment for this package) diff --git a/packages/qwik-city/middleware/node/http.ts b/packages/qwik-city/middleware/node/http.ts index fe8c8d80aee..97ff84857ec 100644 --- a/packages/qwik-city/middleware/node/http.ts +++ b/packages/qwik-city/middleware/node/http.ts @@ -57,6 +57,10 @@ export function fromNodeHttp(url: URL, req: IncomingMessage, res: ServerResponse return res; }, url, + platform: { + ssr: true, + node: process.versions.node, + }, }; return requestCtx; diff --git a/packages/qwik-city/middleware/node/index.ts b/packages/qwik-city/middleware/node/index.ts index 8354f55e5b1..eab1beb1892 100644 --- a/packages/qwik-city/middleware/node/index.ts +++ b/packages/qwik-city/middleware/node/index.ts @@ -1,16 +1,18 @@ -import type { Render } from '@builder.io/qwik/server'; import type { IncomingMessage, ServerResponse } from 'http'; -import type { QwikCityRequestOptions } from '../request-handler/types'; +import type { QwikCityHandlerOptions } from '../request-handler/types'; import { errorHandler, notFoundHandler, requestHandler } from '../request-handler'; import { fromNodeHttp, getUrl } from './http'; import { patchGlobalFetch } from './node-fetch'; +import type { Render } from '@builder.io/qwik/server'; +import type { RenderOptions } from '@builder.io/qwik'; +import qwikCityPlan from '@qwik-city-plan'; // @builder.io/qwik-city/middleware/node /** * @alpha */ -export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions) { +export function createQwikCity(opts: QwikCityNodeRequestOptions) { patchGlobalFetch(); const router = async ( @@ -21,7 +23,7 @@ export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions) { try { const requestCtx = fromNodeHttp(getUrl(req), req, res); try { - const rsp = await requestHandler(requestCtx, render, {}, opts); + const rsp = await requestHandler(requestCtx, opts); if (!rsp) { next(); } @@ -29,6 +31,7 @@ export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions) { await errorHandler(requestCtx, e); } } catch (e) { + console.error(e); next(e); } }; @@ -38,6 +41,7 @@ export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions) { const requestCtx = fromNodeHttp(getUrl(req), req, res); await notFoundHandler(requestCtx); } catch (e) { + console.error(e); next(e); } }; @@ -51,10 +55,28 @@ export function qwikCity(render: Render, opts?: QwikCityNodeRequestOptions) { /** * @alpha */ -export interface QwikCityNodeRequestOptions extends QwikCityRequestOptions {} +export interface QwikCityNodeRequestOptions extends QwikCityHandlerOptions {} /** * @alpha */ export interface NodeRequestNextFunction { (err?: any): void; } + +/** + * @alpha + * @deprecated Please use `createQwikCity()` instead. + * + * Example: + * + * ```ts + * import { createQwikCity } from '@builder.io/qwik-city/middleware/node'; + * import qwikCityPlan from '@qwik-city-plan'; + * import render from './entry.ssr'; + * + * const { router, notFound } = createQwikCity({ render, qwikCityPlan }); + * ``` + */ +export function qwikCity(render: Render, opts?: RenderOptions) { + return createQwikCity({ render, qwikCityPlan, ...opts }); +} diff --git a/packages/qwik-city/middleware/request-handler/endpoint-handler.unit.ts b/packages/qwik-city/middleware/request-handler/endpoint-handler.unit.ts index d997adc9f3e..dc7451b07c6 100644 --- a/packages/qwik-city/middleware/request-handler/endpoint-handler.unit.ts +++ b/packages/qwik-city/middleware/request-handler/endpoint-handler.unit.ts @@ -20,7 +20,7 @@ test('onRequest, async return callback, async callback data', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); instance(userResponse.pendingBody, Promise); equal(userResponse.resolvedBody, undefined); @@ -45,7 +45,7 @@ test('onRequest, async return callback, sync callback data', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); instance(userResponse.pendingBody, Promise); equal(userResponse.resolvedBody, undefined); @@ -70,7 +70,7 @@ test('onRequest, sync return callback, async callback data', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); instance(userResponse.pendingBody, Promise); equal(userResponse.resolvedBody, undefined); @@ -94,7 +94,7 @@ test('onRequest, sync return callback, sync callback data', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); instance(userResponse.pendingBody, Promise); equal(userResponse.resolvedBody, undefined); @@ -115,7 +115,7 @@ test('onRequest, sync return number', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, 88); @@ -138,7 +138,7 @@ test('onRequest, async return string', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, `mph`); @@ -159,7 +159,7 @@ test('onRequest, sync return string', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, `mph`); @@ -181,7 +181,7 @@ test('onRequest, async return object', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, { mph: 88 }); @@ -202,7 +202,7 @@ test('onRequest, sync return object', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, { mph: 88 }); @@ -224,7 +224,7 @@ test('onRequest, user manually set content-type', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); equal(userResponse.pendingBody, undefined); equal(userResponse.resolvedBody, 88); @@ -250,7 +250,7 @@ test('onGet preference over onRequest', async () => { }, }, ]; - const userResponse = await loadUserResponse(requestCtx, {}, routeModules, {}); + const userResponse = await loadUserResponse(requestCtx, {}, routeModules); await endpointHandler(requestCtx, userResponse); equal(calledOnGet, true); diff --git a/packages/qwik-city/middleware/request-handler/page-handler.ts b/packages/qwik-city/middleware/request-handler/page-handler.ts index cd881f1c143..be8a76b51ae 100644 --- a/packages/qwik-city/middleware/request-handler/page-handler.ts +++ b/packages/qwik-city/middleware/request-handler/page-handler.ts @@ -1,4 +1,4 @@ -import type { StreamWriter } from '@builder.io/qwik'; +import type { RenderOptions, StreamWriter } from '@builder.io/qwik'; import type { QwikManifest } from '@builder.io/qwik/optimizer'; import type { PrefetchResource, @@ -9,13 +9,13 @@ import type { import type { ClientPageData, QwikCityEnvData } from '../../runtime/src/library/types'; import { getErrorHtml } from './error-handler'; import { HttpStatus } from './http-status-codes'; -import type { QwikCityRequestContext, QwikCityRequestOptions, UserResponseContext } from './types'; +import type { QwikCityRequestContext, UserResponseContext } from './types'; export function pageHandler( requestCtx: QwikCityRequestContext, userResponse: UserResponseContext, render: Render, - opts?: QwikCityRequestOptions, + opts?: RenderOptions, routeBundleNames?: string[] ): Promise { const { status, headers } = userResponse; diff --git a/packages/qwik-city/middleware/request-handler/request-handler.ts b/packages/qwik-city/middleware/request-handler/request-handler.ts index 54480944532..4d2ca1258c8 100644 --- a/packages/qwik-city/middleware/request-handler/request-handler.ts +++ b/packages/qwik-city/middleware/request-handler/request-handler.ts @@ -1,9 +1,7 @@ import { loadRoute } from '../../runtime/src/library/routing'; import { loadUserResponse, updateRequestCtx } from './user-response'; -import type { QwikCityRequestContext, QwikCityRequestOptions } from './types'; -import type { Render } from '@builder.io/qwik/server'; +import type { QwikCityRequestContext, QwikCityHandlerOptions } from './types'; import { errorHandler, ErrorResponse, errorResponse } from './error-handler'; -import { routes, menus, cacheModules, trailingSlash, basePathname } from '@qwik-city-plan'; import { endpointHandler } from './endpoint-handler'; import { pageHandler } from './page-handler'; import { RedirectResponse, redirectResponse } from './redirect-handler'; @@ -13,11 +11,11 @@ import { RedirectResponse, redirectResponse } from './redirect-handler'; */ export async function requestHandler( requestCtx: QwikCityRequestContext, - render: Render, - platform: Record, - opts?: QwikCityRequestOptions + opts: QwikCityHandlerOptions ): Promise { try { + const { render, qwikCityPlan } = opts; + const { routes, menus, cacheModules, trailingSlash, basePathname } = qwikCityPlan; updateRequestCtx(requestCtx, trailingSlash); const loadedRoute = await loadRoute(routes, menus, cacheModules, requestCtx.url.pathname); @@ -30,7 +28,6 @@ export async function requestHandler( requestCtx, params, mods, - platform, trailingSlash, basePathname ); diff --git a/packages/qwik-city/middleware/request-handler/test-utils.ts b/packages/qwik-city/middleware/request-handler/test-utils.ts index 85eea822691..7d881889cec 100644 --- a/packages/qwik-city/middleware/request-handler/test-utils.ts +++ b/packages/qwik-city/middleware/request-handler/test-utils.ts @@ -1,15 +1,21 @@ import type { RequestContext } from '../../runtime/src/library/types'; import { createHeaders } from './headers'; -import type { ResponseHandler } from './types'; +import type { QwikCityRequestContext, ResponseHandler } from './types'; -export function mockRequestContext(opts?: { method?: string; url?: string | URL }) { +export function mockRequestContext(opts?: { + method?: string; + url?: string | URL; +}): TestQwikCityRequestContext { const url = new URL(opts?.url || '/', 'https://qwik.builder.io'); const request: RequestContext = { method: opts?.method || 'GET', - url, + url: url.href, headers: createHeaders(), - } as any; + formData: () => Promise.resolve(new URLSearchParams()), + json: () => Promise.resolve({}), + text: () => Promise.resolve(''), + }; const responseData: { status: number; headers: Headers; body: Promise } = { status: 200, @@ -32,7 +38,15 @@ export function mockRequestContext(opts?: { method?: string; url?: string | URL }); }; - return { url, request, response, responseData }; + return { url, request, response, responseData, platform: { testing: true } }; +} + +export interface TestQwikCityRequestContext extends QwikCityRequestContext { + responseData: { + status: number; + headers: Headers; + body: any; + }; } export async function wait() { diff --git a/packages/qwik-city/middleware/request-handler/types.ts b/packages/qwik-city/middleware/request-handler/types.ts index 75f0eebdf1b..a735a657c2e 100644 --- a/packages/qwik-city/middleware/request-handler/types.ts +++ b/packages/qwik-city/middleware/request-handler/types.ts @@ -1,11 +1,17 @@ import type { StreamWriter } from '@builder.io/qwik'; -import type { RenderOptions } from '@builder.io/qwik/server'; -import type { ClientPageData, RequestContext, RouteParams } from '../../runtime/src/library/types'; +import type { Render, RenderOptions } from '@builder.io/qwik/server'; +import type { + ClientPageData, + QwikCityPlan, + RequestContext, + RouteParams, +} from '../../runtime/src/library/types'; export interface QwikCityRequestContext { request: RequestContext; response: ResponseHandler; url: URL; + platform: Record; } export interface QwikCityDevRequestContext extends QwikCityRequestContext { @@ -34,4 +40,7 @@ export interface UserResponseContext { aborted: boolean; } -export interface QwikCityRequestOptions extends RenderOptions {} +export interface QwikCityHandlerOptions extends RenderOptions { + render: Render; + qwikCityPlan: QwikCityPlan; +} diff --git a/packages/qwik-city/middleware/request-handler/user-response.ts b/packages/qwik-city/middleware/request-handler/user-response.ts index 7c1a9f3db19..e064da3b88f 100644 --- a/packages/qwik-city/middleware/request-handler/user-response.ts +++ b/packages/qwik-city/middleware/request-handler/user-response.ts @@ -16,7 +16,6 @@ export async function loadUserResponse( requestCtx: QwikCityRequestContext, params: RouteParams, routeModules: RouteModule[], - platform: Record, trailingSlash?: boolean, basePathname: string = '/' ) { @@ -24,7 +23,7 @@ export async function loadUserResponse( throw new ErrorResponse(HttpStatus.NotFound, `Not Found`); } - const { request, url } = requestCtx; + const { request, url, platform } = requestCtx; const { pathname } = url; const isPageModule = isLastModulePageRoute(routeModules); const isPageDataRequest = isPageModule && request.headers.get('Accept') === 'application/json'; @@ -224,7 +223,10 @@ function isLastModulePageRoute(routeModules: RouteModule[]) { return lastRouteModule && typeof (lastRouteModule as PageModule).default === 'function'; } -export function updateRequestCtx(requestCtx: QwikCityRequestContext, trailingSlash: boolean) { +export function updateRequestCtx( + requestCtx: QwikCityRequestContext, + trailingSlash: boolean | undefined +) { let pathname = requestCtx.url.pathname; if (pathname.endsWith(QDATA_JSON)) { diff --git a/packages/qwik-city/middleware/request-handler/user-response.unit.ts b/packages/qwik-city/middleware/request-handler/user-response.unit.ts index 8e36130f83e..08de65a86f4 100644 --- a/packages/qwik-city/middleware/request-handler/user-response.unit.ts +++ b/packages/qwik-city/middleware/request-handler/user-response.unit.ts @@ -16,7 +16,7 @@ test('endpoint type cuz no default module export', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.type, 'endpoint'); }); @@ -31,7 +31,7 @@ test('pagedata type cuz default module export and application/jon accept header' }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.type, 'pagedata'); }); @@ -46,7 +46,7 @@ test('pagehtml type cuz default module export', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.type, 'pagehtml'); }); @@ -63,7 +63,7 @@ test('sync endpoint, undefined body', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.status, 204); equal(u.headers.get('name'), 'value'); equal(u.pendingBody, undefined); @@ -85,7 +85,7 @@ test('async endpoint, resolved data, render blocking', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.status, 204); equal(u.headers.get('name'), 'value'); equal(u.pendingBody, undefined); @@ -115,7 +115,7 @@ test('onPost priority over onRequest, dont call onGet', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.status, 200); equal(calledOnGet, false); equal(calledOnPost, true); @@ -136,7 +136,7 @@ test('catchall onRequest', async () => { }, ]; - const u = await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + const u = await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(u.status, 200); equal(calledOnRequest, true); }); @@ -157,7 +157,7 @@ test('user manual redirect, PageModule', async () => { }, ]; - await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(true, false, 'Should have thrown'); } catch (e: any) { instance(e, RedirectResponse); @@ -181,7 +181,7 @@ test('throw redirect', async () => { }, ]; - await loadUserResponse(requestCtx, {}, endpoints, {}, trailingSlash); + await loadUserResponse(requestCtx, {}, endpoints, trailingSlash); equal(true, false, 'Should have thrown'); } catch (e: any) { instance(e, RedirectResponse); @@ -198,7 +198,7 @@ test('no handler for endpoint', async () => { { onDelete: () => {} }, { onPost: () => {} }, ]; - await loadUserResponse(requestCtx, {}, routeModules, {}, trailingSlash); + await loadUserResponse(requestCtx, {}, routeModules, trailingSlash); equal(true, false, 'Should have thrown'); } catch (e: any) { instance(e, ErrorResponse); @@ -215,7 +215,7 @@ test('remove trailing slash, PageModule', async () => { default: () => {}, }, ]; - await loadUserResponse(requestCtx, {}, routeModules, {}, trailingSlash); + await loadUserResponse(requestCtx, {}, routeModules, trailingSlash); equal(true, false, 'Should have thrown'); } catch (e: any) { instance(e, RedirectResponse); @@ -233,7 +233,7 @@ test('add trailing slash, PageModule', async () => { default: () => {}, }, ]; - await loadUserResponse(requestCtx, {}, routeModules, {}, trailingSlash); + await loadUserResponse(requestCtx, {}, routeModules, trailingSlash); equal(true, false, 'Should have thrown'); } catch (e: any) { instance(e, RedirectResponse); diff --git a/packages/qwik-city/package.json b/packages/qwik-city/package.json index 52487092bf4..187536488c3 100644 --- a/packages/qwik-city/package.json +++ b/packages/qwik-city/package.json @@ -12,6 +12,22 @@ "import": "./lib/index.qwik.mjs", "require": "./lib/index.qwik.cjs" }, + "./adaptors/cloudflare-pages/vite": { + "import": "./lib/adaptors/cloudflare-pages/vite/index.mjs", + "require": "./lib/adaptors/cloudflare-pages/vite/index.cjs" + }, + "./adaptors/express/vite": { + "import": "./lib/adaptors/express/vite/index.mjs", + "require": "./lib/adaptors/express/vite/index.cjs" + }, + "./adaptors/netlify-edge/vite": { + "import": "./lib/adaptors/netlify-edge/vite/index.mjs", + "require": "./lib/adaptors/netlify-edge/vite/index.cjs" + }, + "./adaptors/static/vite": { + "import": "./lib/adaptors/static/vite/index.mjs", + "require": "./lib/adaptors/static/vite/index.cjs" + }, "./middleware/cloudflare-pages": { "import": "./lib/middleware/cloudflare-pages/index.mjs" }, @@ -22,9 +38,9 @@ "import": "./lib/middleware/node/index.mjs", "require": "./lib/middleware/node/index.cjs" }, - "./static/node": { - "import": "./lib/static/node/index.mjs", - "require": "./lib/static/node/index.cjs" + "./static": { + "import": "./lib/static/index.mjs", + "require": "./lib/static/index.cjs" }, "./vite": { "import": "./lib/vite/index.mjs", diff --git a/packages/qwik-city/runtime/src/api.md b/packages/qwik-city/runtime/src/api.md index b789693aa4c..392c1479eea 100644 --- a/packages/qwik-city/runtime/src/api.md +++ b/packages/qwik-city/runtime/src/api.md @@ -143,17 +143,15 @@ export const QwikCity: Component; // @alpha (undocumented) export interface QwikCityPlan { // (undocumented) - cacheModules?: boolean; - // Warning: (ae-forgotten-export) The symbol "FallbackRouteData" needs to be exported by the entry point index.d.ts - // + basePathname?: string; // (undocumented) - fallbackRoutes?: FallbackRouteData[]; + cacheModules?: boolean; // Warning: (ae-forgotten-export) The symbol "MenuData" needs to be exported by the entry point index.d.ts // // (undocumented) menus?: MenuData[]; // (undocumented) - routes?: RouteData[]; + routes: RouteData[]; // (undocumented) trailingSlash?: boolean; } diff --git a/packages/qwik-city/runtime/src/entry.static.tsx b/packages/qwik-city/runtime/src/entry.static.tsx deleted file mode 100644 index 5c911adc07c..00000000000 --- a/packages/qwik-city/runtime/src/entry.static.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { qwikCityGenerate } from '../../static/node'; -import render from './entry.ssr'; -import { fileURLToPath } from 'url'; -import { join } from 'path'; - -// Execute Qwik City Static Generator -qwikCityGenerate(render, { - origin: 'https://qwik.builder.io', - outDir: join(fileURLToPath(import.meta.url), '..', '..', 'dist'), - log: 'debug', -}); diff --git a/packages/qwik-city/runtime/src/library/qwik-city-plan.ts b/packages/qwik-city/runtime/src/library/qwik-city-plan.ts index 6486c185c7b..b03f239ae74 100644 --- a/packages/qwik-city/runtime/src/library/qwik-city-plan.ts +++ b/packages/qwik-city/runtime/src/library/qwik-city-plan.ts @@ -6,3 +6,11 @@ export const menus: MenuData[] = []; export const trailingSlash = false; export const basePathname = '/'; export const cacheModules = false; + +export default { + routes, + menus, + trailingSlash, + basePathname, + cacheModules, +}; diff --git a/packages/qwik-city/runtime/src/library/types.ts b/packages/qwik-city/runtime/src/library/types.ts index fff8173b825..b0f358ee05e 100644 --- a/packages/qwik-city/runtime/src/library/types.ts +++ b/packages/qwik-city/runtime/src/library/types.ts @@ -196,8 +196,8 @@ export type MenuData = [pathname: string, menuLoader: MenuModuleLoader]; * @alpha */ export interface QwikCityPlan { - routes?: RouteData[]; - fallbackRoutes?: FallbackRouteData[]; + routes: RouteData[]; + basePathname?: string; menus?: MenuData[]; trailingSlash?: boolean; cacheModules?: boolean; diff --git a/packages/qwik-city/static/node/api-extractor.json b/packages/qwik-city/static/api-extractor.json similarity index 67% rename from packages/qwik-city/static/node/api-extractor.json rename to packages/qwik-city/static/api-extractor.json index 6b64593bcdf..e4fdf88be42 100644 --- a/packages/qwik-city/static/node/api-extractor.json +++ b/packages/qwik-city/static/api-extractor.json @@ -1,15 +1,15 @@ { "$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-city/static/node/index.d.ts", + "extends": "../api-extractor.json", + "mainEntryPointFilePath": "/dist-dev/dts-out/packages/qwik-city/static/index.d.ts", "apiReport": { "enabled": true, "reportFileName": "api.md", - "reportFolder": "/packages/qwik-city/static/node/", - "reportTempFolder": "/dist-dev/api-extractor/qwik-city/static/node" + "reportFolder": "/packages/qwik-city/static/", + "reportTempFolder": "/dist-dev/api-extractor/qwik-city/static" }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/packages/qwik-city/lib/static/node/index.d.ts" + "untrimmedFilePath": "/packages/qwik-city/lib/static/index.d.ts" } } diff --git a/packages/qwik-city/static/api.md b/packages/qwik-city/static/api.md new file mode 100644 index 00000000000..feb3e21cfcd --- /dev/null +++ b/packages/qwik-city/static/api.md @@ -0,0 +1,46 @@ +## API Report File for "@builder.io/qwik-city" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { QwikManifest } from '@builder.io/qwik/optimizer'; +import type { SymbolMapper } from '@builder.io/qwik/optimizer'; +import type { SymbolMapperFn } from '@builder.io/qwik/optimizer'; + +// @alpha +export function generate(opts: StaticGenerateOptions): Promise; + +// @alpha (undocumented) +export interface StaticGenerateOptions extends StaticGenerateRenderOptions { + qwikCityPlanModulePath: string; + renderModulePath: string; +} + +// Warning: (ae-forgotten-export) The symbol "RenderOptions" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export interface StaticGenerateRenderOptions extends RenderOptions { + emitData?: boolean; + emitHtml?: boolean; + log?: 'debug'; + maxTasksPerWorker?: number; + maxWorkers?: number; + origin: string; + outDir: string; + sitemapOutFile?: string; +} + +// @alpha (undocumented) +export interface StaticGenerateResult { + // (undocumented) + duration: number; + // (undocumented) + errors: number; + // (undocumented) + rendered: number; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/qwik-city/static/deno/index.ts b/packages/qwik-city/static/deno/index.ts new file mode 100644 index 00000000000..1c62f3b6972 --- /dev/null +++ b/packages/qwik-city/static/deno/index.ts @@ -0,0 +1,8 @@ +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-city/static/generator/generate.ts b/packages/qwik-city/static/generator/generate.ts deleted file mode 100644 index 935f30eb423..00000000000 --- a/packages/qwik-city/static/generator/generate.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable no-console */ -import type { Render } from '@builder.io/qwik/server'; -import type { System } from './types'; -import { mainThread } from './main-thread'; -import { workerThread } from './worker-thread'; - -// @builder.io/qwik-city/static/node - -/** - * @alpha - */ -export async function staticGenerate(sys: System, render: Render) { - if (sys.isMainThread()) { - await mainThread(sys); - } else { - await workerThread(sys, render); - } -} diff --git a/packages/qwik-city/static/generator/index.ts b/packages/qwik-city/static/generator/index.ts deleted file mode 100644 index 9943bb612b7..00000000000 --- a/packages/qwik-city/static/generator/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { staticGenerate } from './generate'; diff --git a/packages/qwik-city/static/index.ts b/packages/qwik-city/static/index.ts new file mode 100644 index 00000000000..56fd3a53e26 --- /dev/null +++ b/packages/qwik-city/static/index.ts @@ -0,0 +1,57 @@ +/* eslint-disable no-console */ +import type { + StaticGenerateRenderOptions, + StaticGenerateOptions, + StaticGenerateResult, +} from './types'; + +// @builder.io/qwik-city/static + +/** + * 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 City Plan. + * @alpha + */ +export async function generate(opts: StaticGenerateOptions) { + const ssgPlatform = await getEntryModule(); + const result: StaticGenerateResult = await ssgPlatform.generate(opts); + return result; +} + +export { StaticGenerateOptions, StaticGenerateRenderOptions, StaticGenerateResult }; + +function getEntryModulePath() { + if (isDeno()) { + return './deno.mjs'; + } + if (isNode()) { + if (isCjs()) { + return './node.cjs'; + } + return './node.mjs'; + } + throw new Error(`Unsupported platform`); +} + +function getEntryModule() { + const entryModule = getEntryModulePath(); + if (isCjs()) { + return require(entryModule); + } + return import(entryModule); +} + +function isDeno() { + return typeof Deno !== 'undefined'; +} + +function isNode() { + return !isDeno() && typeof process !== 'undefined' && !!process.versions?.node; +} + +function isCjs() { + const req = 'require'; + return isNode() && typeof globalThis[req] === 'function'; +} + +declare const Deno: any; diff --git a/packages/qwik-city/static/generator/main-thread.ts b/packages/qwik-city/static/main-thread.ts similarity index 75% rename from packages/qwik-city/static/generator/main-thread.ts rename to packages/qwik-city/static/main-thread.ts index a6208618d44..ea89eb4ec53 100644 --- a/packages/qwik-city/static/generator/main-thread.ts +++ b/packages/qwik-city/static/main-thread.ts @@ -1,8 +1,12 @@ -import type { StaticGeneratorOptions, StaticGeneratorResults, StaticRoute, System } from './types'; -import { msToString } from '../../utils/format'; -import { getPathnameForDynamicRoute, normalizePathname } from '../../utils/pathname'; -import type { PageModule, RouteParams } from '../../runtime/src/library/types'; -import { routes, trailingSlash, basePathname } from '@qwik-city-plan'; +import type { PageModule, QwikCityPlan, RouteParams } from '../runtime/src/library/types'; +import type { + StaticGenerateRenderOptions, + StaticGenerateResult, + StaticRoute, + System, +} from './types'; +import { msToString } from '../utils/format'; +import { getPathnameForDynamicRoute, normalizePathname } from '../utils/pathname'; export async function mainThread(sys: System) { const opts = sys.getOptions(); @@ -10,13 +14,18 @@ export async function mainThread(sys: System) { const main = await sys.createMainProcess(); const log = await sys.createLogger(); + const qwikCityPlan: QwikCityPlan = (await import(opts.qwikCityPlanModulePath)).default; + const queue: StaticRoute[] = []; const active = new Set(); + const routes = qwikCityPlan.routes || []; + const basePathname = qwikCityPlan.basePathname || '/'; + const trailingSlash = !!qwikCityPlan.trailingSlash; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { const timer = sys.createTimer(); - const generatorResults: StaticGeneratorResults = { + const generatorResult: StaticGenerateResult = { duration: 0, rendered: 0, errors: 0, @@ -36,25 +45,25 @@ export async function mainThread(sys: System) { if (!isCompleted && isRoutesLoaded && queue.length === 0 && active.size === 0) { isCompleted = true; - generatorResults.duration = timer(); + generatorResult.duration = timer(); - if (generatorResults.rendered > 0) { + if (generatorResult.rendered > 0) { log.info( - `Generated: ${generatorResults.rendered} page${ - generatorResults.rendered === 1 ? '' : 's' + `Generated: ${generatorResult.rendered} page${ + generatorResult.rendered === 1 ? '' : 's' }` ); } - if (generatorResults.errors > 0) { - log.info(`Errors: ${generatorResults.errors}`); + if (generatorResult.errors > 0) { + log.info(`Errors: ${generatorResult.errors}`); } - log.info(`Duration: ${msToString(generatorResults.duration)}`); + log.info(`Duration: ${msToString(generatorResult.duration)}`); - const total = generatorResults.rendered + generatorResults.errors; + const total = generatorResult.rendered + generatorResult.errors; if (total > 0) { - log.info(`Average: ${msToString(generatorResults.duration / total)} per page`); + log.info(`Average: ${msToString(generatorResult.duration / total)} per page`); } log.info(``); @@ -62,7 +71,7 @@ export async function mainThread(sys: System) { main .close() .then(() => { - setTimeout(() => resolve(generatorResults)); + setTimeout(() => resolve(generatorResult)); }) .catch(reject); } @@ -89,9 +98,9 @@ export async function mainThread(sys: System) { if (result.error) { log.error(staticRoute.pathname, result.error); - generatorResults.errors++; + generatorResult.errors++; } else if (result.ok) { - generatorResults.rendered++; + generatorResult.rendered++; } flushQueue(); @@ -157,7 +166,7 @@ export async function mainThread(sys: System) { }); } -function validateOptions(opts: StaticGeneratorOptions) { +function validateOptions(opts: StaticGenerateRenderOptions) { let siteOrigin = opts.origin; if (typeof siteOrigin !== 'string' || siteOrigin.trim().length === 0) { throw new Error(`Missing "origin" option`); diff --git a/packages/qwik-city/static/node/api.md b/packages/qwik-city/static/node/api.md deleted file mode 100644 index dcdcdaa40fc..00000000000 --- a/packages/qwik-city/static/node/api.md +++ /dev/null @@ -1,21 +0,0 @@ -## API Report File for "@builder.io/qwik-city" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import type { Render } from '@builder.io/qwik/server'; -import type { RenderOptions } from '@builder.io/qwik/server'; - -// Warning: (ae-forgotten-export) The symbol "StaticGeneratorOptions" needs to be exported by the entry point index.d.ts -// -// @alpha (undocumented) -export interface NodeStaticGeneratorOptions extends StaticGeneratorOptions { -} - -// @alpha (undocumented) -export function qwikCityGenerate(render: Render, opts: NodeStaticGeneratorOptions): Promise; - -// (No @packageDocumentation comment for this package) - -``` diff --git a/packages/qwik-city/static/node/index.ts b/packages/qwik-city/static/node/index.ts index ac76f46df43..8bb79767980 100644 --- a/packages/qwik-city/static/node/index.ts +++ b/packages/qwik-city/static/node/index.ts @@ -1,3 +1,23 @@ -export { qwikCityGenerate } from './node-generate'; +import type { StaticGenerateOptions } from '../types'; +import { createSystem } from './node-system'; +import { isMainThread, workerData } from 'worker_threads'; +import { mainThread } from '../main-thread'; +import { workerThread } from '../worker-thread'; -export type { NodeStaticGeneratorOptions } from './types'; +export async function generate(opts: StaticGenerateOptions) { + if (isMainThread) { + const sys = await createSystem(opts); + const result = await mainThread(sys); + return result; + } + + throw new Error(`generate() cannot be called from a worker thread`); +} + +if (!isMainThread && workerData) { + (async () => { + // self initializing worker thread with workerData + const sys = await createSystem(workerData); + await workerThread(sys); + })(); +} diff --git a/packages/qwik-city/static/node/node-generate.ts b/packages/qwik-city/static/node/node-generate.ts deleted file mode 100644 index 0991f1fdbb5..00000000000 --- a/packages/qwik-city/static/node/node-generate.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable no-console */ -import type { Render } from '@builder.io/qwik/server'; -import type { NodeStaticGeneratorOptions } from './types'; -import { createNodeSystem } from './node-system'; -import { staticGenerate } from '../generator/generate'; - -// @builder.io/qwik-city/static/node - -/** - * @alpha - */ -export async function qwikCityGenerate(render: Render, opts: NodeStaticGeneratorOptions) { - try { - const nodeSys = await createNodeSystem(opts); - await staticGenerate(nodeSys, render); - } catch (e) { - console.error(e); - } -} diff --git a/packages/qwik-city/static/node/node-main.ts b/packages/qwik-city/static/node/node-main.ts index 634b36751c5..31bf03ecc12 100644 --- a/packages/qwik-city/static/node/node-main.ts +++ b/packages/qwik-city/static/node/node-main.ts @@ -1,26 +1,27 @@ import type { MainContext, + StaticGenerateOptions, StaticRoute, - StaticGeneratorOptions, StaticWorkerRenderResult, WorkerOutputMessage, WorkerInputMessage, -} from '../generator/types'; +} from '../types'; import fs from 'fs'; import { cpus as nodeCpus } from 'os'; import { Worker } from 'worker_threads'; -import { fileURLToPath } from 'url'; -import { isAbsolute, join } from 'path'; +import { isAbsolute, resolve } from 'path'; import { ensureDir } from './node-system'; import { normalizePath } from '../../utils/fs'; -export async function createNodeMainProcess(opts: StaticGeneratorOptions) { - const currentFile = fileURLToPath(import.meta.url); +export async function createNodeMainProcess(opts: StaticGenerateOptions) { const ssgWorkers: StaticGeneratorWorker[] = []; const sitemapBuffer: string[] = []; let sitemapPromise: Promise | null = null; let outDir = opts.outDir; + if (typeof outDir !== 'string') { + throw new Error(`Missing "outDir" option`); + } if (!isAbsolute(outDir)) { throw new Error(`"outDir" must be an absolute file path, received: ${outDir}`); } @@ -42,14 +43,27 @@ export async function createNodeMainProcess(opts: StaticGeneratorOptions) { sitemapOutFile = 'sitemap.xml'; } if (!isAbsolute(sitemapOutFile)) { - sitemapOutFile = join(outDir, sitemapOutFile); + sitemapOutFile = resolve(outDir, sitemapOutFile); } } const createWorker = () => { let terminateResolve: (() => void) | null = null; const mainTasks = new Map(); - const nodeWorker = new Worker(currentFile); + + let workerFilePath: string | URL; + + if (typeof __filename === 'string') { + workerFilePath = __filename; + } else { + workerFilePath = import.meta.url; + } + + if (typeof workerFilePath === 'string' && workerFilePath.startsWith('file://')) { + workerFilePath = new URL(workerFilePath); + } + + const nodeWorker = new Worker(workerFilePath, { workerData: opts }); const ssgWorker: StaticGeneratorWorker = { activeTasks: 0, diff --git a/packages/qwik-city/static/node/node-system.ts b/packages/qwik-city/static/node/node-system.ts index 54118edcc9a..d0ecf895543 100644 --- a/packages/qwik-city/static/node/node-system.ts +++ b/packages/qwik-city/static/node/node-system.ts @@ -1,17 +1,16 @@ /* eslint-disable no-console */ -import type { System } from '../generator/types'; +import type { StaticGenerateOptions, System } from '../types'; import fs from 'fs'; import { dirname, join } from 'path'; import { patchGlobalFetch } from '../../middleware/node/node-fetch'; -import type { NodeStaticGeneratorOptions } from './types'; import { createNodeMainProcess } from './node-main'; import { createNodeWorkerProcess } from './node-worker'; -import { isMainThread } from 'worker_threads'; import { normalizePath } from '../../utils/fs'; -export async function createNodeSystem(opts: NodeStaticGeneratorOptions) { - opts = { ...opts }; - +/** + * @alpha + */ +export async function createSystem(opts: StaticGenerateOptions) { patchGlobalFetch(); const createWriteStream = (filePath: string) => { @@ -63,13 +62,16 @@ export async function createNodeSystem(opts: NodeStaticGeneratorOptions) { createMainProcess: () => createNodeMainProcess(opts), createWorkerProcess: createNodeWorkerProcess, createLogger, - isMainThread: () => isMainThread, getOptions: () => opts, ensureDir, createWriteStream, createTimer, getPageFilePath, getDataFilePath, + platform: { + static: true, + node: process.versions.node, + }, }; return sys; diff --git a/packages/qwik-city/static/node/node-worker.ts b/packages/qwik-city/static/node/node-worker.ts index a0b50dab313..58b68137d01 100644 --- a/packages/qwik-city/static/node/node-worker.ts +++ b/packages/qwik-city/static/node/node-worker.ts @@ -1,5 +1,5 @@ import { parentPort } from 'worker_threads'; -import type { WorkerInputMessage, WorkerOutputMessage } from '../generator/types'; +import type { WorkerInputMessage, WorkerOutputMessage } from '../types'; export async function createNodeWorkerProcess( onMessage: (msg: WorkerInputMessage) => Promise diff --git a/packages/qwik-city/static/node/types.ts b/packages/qwik-city/static/node/types.ts deleted file mode 100644 index 7a80a0ba8e1..00000000000 --- a/packages/qwik-city/static/node/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { StaticGeneratorOptions } from '../generator/types'; - -/** - * @alpha - */ -export interface NodeStaticGeneratorOptions extends StaticGeneratorOptions {} diff --git a/packages/qwik-city/static/generator/types.ts b/packages/qwik-city/static/types.ts similarity index 78% rename from packages/qwik-city/static/generator/types.ts rename to packages/qwik-city/static/types.ts index c982e7ad35c..f74a42d004f 100644 --- a/packages/qwik-city/static/generator/types.ts +++ b/packages/qwik-city/static/types.ts @@ -1,6 +1,7 @@ import type { StreamWriter } from '@builder.io/qwik'; -import type { RouteParams } from '../../runtime/src'; -import type { QwikCityRequestOptions } from '../../middleware/request-handler/types'; +import type { RouteParams } from '../runtime/src'; +import type { RenderOptions } from '../../qwik/src/server'; +import type { QwikCityHandlerOptions } from '../middleware/request-handler/types'; export interface System { createMainProcess: () => Promise; @@ -8,13 +9,13 @@ export interface System { onMessage: (msg: WorkerInputMessage) => Promise ) => void; createLogger: () => Promise; - getOptions: () => StaticGeneratorOptions; - isMainThread: () => boolean; + getOptions: () => StaticGenerateOptions; ensureDir: (filePath: string) => Promise; createWriteStream: (filePath: string) => StaticStreamWriter; createTimer: () => () => number; getPageFilePath: (pathname: string) => string; getDataFilePath: (pathname: string) => string; + platform: { [key: string]: any }; } export interface StaticStreamWriter extends StreamWriter { @@ -33,7 +34,10 @@ export interface Logger { debug: (...msgs: any[]) => void; } -export interface StaticGeneratorOptions extends QwikCityRequestOptions { +/** + * @alpha + */ +export interface StaticGenerateRenderOptions extends RenderOptions { /** * File system directory where the static files should be written. */ @@ -85,6 +89,25 @@ export interface StaticGeneratorOptions extends QwikCityRequestOptions { emitData?: boolean; } +/** + * @alpha + */ +export interface StaticGenerateOptions extends StaticGenerateRenderOptions { + /** + * Path to the SSR module exporting the default render function. + * In most cases it'll be `./src/entry.ssr.tsx`. + */ + renderModulePath: string; + /** + * Path to the Qwik City Plan module exporting the default `@qwik-city-plan`. + */ + qwikCityPlanModulePath: string; +} + +export interface StaticGenerateHandlerOptions + extends StaticGenerateRenderOptions, + QwikCityHandlerOptions {} + export type WorkerInputMessage = StaticRenderInput | WorkerCloseMessage; export type WorkerOutputMessage = StaticWorkerRenderResult | WorkerCloseMessage; @@ -110,7 +133,10 @@ export interface StaticWorkerRenderResult { error: string | null; } -export interface StaticGeneratorResults { +/** + * @alpha + */ +export interface StaticGenerateResult { duration: number; rendered: number; errors: number; diff --git a/packages/qwik-city/static/generator/worker-thread.ts b/packages/qwik-city/static/worker-thread.ts similarity index 74% rename from packages/qwik-city/static/generator/worker-thread.ts rename to packages/qwik-city/static/worker-thread.ts index bc65514a478..93f116056a3 100644 --- a/packages/qwik-city/static/generator/worker-thread.ts +++ b/packages/qwik-city/static/worker-thread.ts @@ -1,24 +1,29 @@ -import type { Render } from '@builder.io/qwik/server'; import type { - StaticGeneratorOptions, + StaticGenerateHandlerOptions, StaticRoute, StaticWorkerRenderResult, System, } from './types'; -import { requestHandler } from '../../middleware/request-handler'; -import { createHeaders } from '../../middleware/request-handler/headers'; -import type { QwikCityRequestContext } from '../../middleware/request-handler/types'; -import type { RequestContext } from '../../runtime/src/library/types'; +import type { QwikCityRequestContext } from '../middleware/request-handler/types'; +import type { RequestContext } from '../runtime/src/library/types'; +import { createHeaders } from '../middleware/request-handler/headers'; +import { requestHandler } from '../middleware/request-handler'; -export async function workerThread(sys: System, render: Render) { - const opts = sys.getOptions(); +export async function workerThread(sys: System) { + const ssgOpts = sys.getOptions(); const pendingPromises = new Set>(); + const opts: StaticGenerateHandlerOptions = { + ...ssgOpts, + render: await import(ssgOpts.renderModulePath), + qwikCityPlan: await import(ssgOpts.qwikCityPlanModulePath), + }; + sys.createWorkerProcess(async (msg) => { switch (msg.type) { case 'render': { return new Promise((resolve) => { - workerRender(sys, render, opts, msg, pendingPromises, resolve); + workerRender(sys, opts, msg, pendingPromises, resolve); }); } case 'close': { @@ -33,8 +38,7 @@ export async function workerThread(sys: System, render: Render) { async function workerRender( sys: System, - render: Render, - opts: StaticGeneratorOptions, + opts: StaticGenerateHandlerOptions, staticRoute: StaticRoute, pendingPromises: Set>, callback: (result: StaticWorkerRenderResult) => void @@ -51,19 +55,7 @@ async function workerRender( }; try { - const requestHeaders = createHeaders(); - requestHeaders.set('Accept', 'text/html,application/json'); - requestHeaders.set('Host', url.host); - requestHeaders.set('User-Agent', 'Qwik City SSG'); - - const request: RequestContext = { - formData: async () => new URLSearchParams(), - headers: requestHeaders, - json: async () => {}, - method: 'GET', - text: async () => '', - url: url.href, - }; + const request = new SsgRequestContext(url); const requestCtx: QwikCityRequestContext = { url, @@ -130,17 +122,10 @@ async function workerRender( }); } }, + platform: sys.platform, }; - const promise = requestHandler( - requestCtx, - render, - {}, - { - ...opts, - ...staticRoute, - } - ) + const promise = requestHandler(requestCtx, opts) .then((rsp) => { if (rsp == null) { callback(result); @@ -180,3 +165,34 @@ async function workerRender( callback(result); } } + +class SsgRequestContext implements RequestContext { + url: string; + headers: Headers; + + constructor(url: URL) { + this.url = url.href; + + const headers = createHeaders(); + headers.set('Host', url.host); + headers.set('Accept', 'text/html,application/json'); + headers.set('User-Agent', 'Qwik City SSG'); + this.headers = headers; + } + + get method() { + return 'GET'; + } + + async json() { + return {}; + } + + async text() { + return ''; + } + + async formData() { + return new URLSearchParams(); + } +} diff --git a/packages/qwik-city/utils/fs.ts b/packages/qwik-city/utils/fs.ts index 0c587ed5f97..a8090881dce 100644 --- a/packages/qwik-city/utils/fs.ts +++ b/packages/qwik-city/utils/fs.ts @@ -85,11 +85,11 @@ export function normalizePath(path: string) { return path; } -export function createFileId(routesDir: string, path: string) { +export function createFileId(routesDir: string, fsPath: string) { const ids: string[] = []; for (let i = 0; i < 25; i++) { - let baseName = removeExtension(basename(path)); + let baseName = removeExtension(basename(fsPath)); baseName = baseName.replace(/[\W_]+/g, ''); if (baseName === '') { @@ -99,9 +99,9 @@ export function createFileId(routesDir: string, path: string) { } ids.push(toTitleCase(baseName)); - path = normalizePath(dirname(path)); + fsPath = normalizePath(dirname(fsPath)); - if (path === routesDir) { + if (fsPath === routesDir) { break; } } diff --git a/packages/qwik/package.json b/packages/qwik/package.json index bd2dcb2bd9c..cce7e4b1fc2 100644 --- a/packages/qwik/package.json +++ b/packages/qwik/package.json @@ -106,9 +106,8 @@ "server.d.ts", "server-modules.d.ts", "server/package.json", + "starters/adaptors", "starters/features", - "starters/servers", - "starters/static-generators", "testing/index.cjs", "testing/index.mjs", "testing/index.d.ts", diff --git a/packages/qwik/src/cli/add/print-add-help.ts b/packages/qwik/src/cli/add/print-add-help.ts index c8a8031b65b..8f942acf810 100644 --- a/packages/qwik/src/cli/add/print-add-help.ts +++ b/packages/qwik/src/cli/add/print-add-help.ts @@ -5,8 +5,7 @@ import { pmRunCmd } from '../utils/utils'; export async function printAddHelp() { const integrations = await loadIntegrations(); - const servers = integrations.filter((i) => i.type === 'server'); - const staticGenerators = integrations.filter((i) => i.type === 'static-generator'); + const adaptors = integrations.filter((i) => i.type === 'adaptor'); const features = integrations.filter((i) => i.type === 'feature'); const pmRun = pmRunCmd(); @@ -14,14 +13,8 @@ export async function printAddHelp() { console.log(`${pmRun} qwik ${color.magenta(`add`)} [integration]`); console.log(``); - console.log(` ${color.cyan('Servers')}`); - for (const s of servers) { - console.log(` ${s.id} ${color.dim(s.pkgJson.description)}`); - } - console.log(``); - - console.log(` ${color.cyan('Static Generator')}`); - for (const s of staticGenerators) { + console.log(` ${color.cyan('Adaptors')}`); + for (const s of adaptors) { console.log(` ${s.id} ${color.dim(s.pkgJson.description)}`); } console.log(``); diff --git a/packages/qwik/src/cli/add/run-add-interactive.ts b/packages/qwik/src/cli/add/run-add-interactive.ts index 8f2c450b595..fe9330c3757 100644 --- a/packages/qwik/src/cli/add/run-add-interactive.ts +++ b/packages/qwik/src/cli/add/run-add-interactive.ts @@ -33,21 +33,19 @@ export async function runAddInteractive(app: AppCommand, id: string | undefined) console.log(`🦋 ${color.bgCyan(` Add Integration `)}`); console.log(``); - const staticGenerator = integrations.find((i) => i.type === 'static-generator')!; - const features = integrations.filter((i) => i.type === 'feature'); + const integrationChoices = [ + ...integrations.filter((i) => i.type === 'adaptor'), + ...integrations.filter((i) => i.type === 'feature'), + ].map((f) => { + return { title: f.name, value: f.id }; + }); - const featureAnswer = await prompts( + const integrationAnswer = await prompts( { type: 'select', name: 'featureType', - message: `What feature would you like to add?`, - choices: [ - { title: 'Server Adaptors (SSR)', value: '__server' }, - { title: 'Static Generator (SSG)', value: staticGenerator.id }, - ...features.map((f) => { - return { title: f.name, value: f.id }; - }), - ], + message: `What integration would you like to add?`, + choices: integrationChoices, hint: '(use ↓↑ arrows, hit enter)', }, { @@ -59,31 +57,8 @@ export async function runAddInteractive(app: AppCommand, id: string | undefined) ); console.log(``); - if (featureAnswer.featureType === '__server') { - // narrow list to just server integrations - const servers = integrations.filter((i) => i.type === 'server'); - const serverAnswer = await prompts( - { - type: 'select', - name: 'id', - message: `Which server adaptor would you like to add?`, - choices: servers.map((f) => { - return { title: f.name, value: f.id, description: f.pkgJson.description }; - }), - hint: ' ', - }, - { - onCancel: () => { - console.log(``); - process.exit(0); - }, - } - ); - integration = integrations.find((i) => i.id === serverAnswer.id); - console.log(``); - } else { - integration = integrations.find((i) => i.id === featureAnswer.featureType); - } + integration = integrations.find((i) => i.id === integrationAnswer.featureType); + if (!integration) { throw new Error(`Invalid integration: ${id}`); } diff --git a/packages/qwik/src/cli/add/update-app.ts b/packages/qwik/src/cli/add/update-app.ts index 7d3759f12a7..b90ab1d5902 100644 --- a/packages/qwik/src/cli/add/update-app.ts +++ b/packages/qwik/src/cli/add/update-app.ts @@ -5,7 +5,6 @@ import { getPackageManager, panic } from '../utils/utils'; import { loadIntegrations } from '../utils/integrations'; import { installDeps, startSpinner } from '../utils/install-deps'; import { mergeIntegrationDir } from './update-files'; -import { updateViteConfigs } from './update-vite-config'; export async function updateApp(opts: UpdateAppOptions) { const integrations = await loadIntegrations(); @@ -28,10 +27,6 @@ export async function updateApp(opts: UpdateAppOptions) { await mergeIntegrationDir(fileUpdates, opts, integration.dir, opts.rootDir); - if ((globalThis as any).CODE_MOD) { - await updateViteConfigs(fileUpdates, integration, opts.rootDir); - } - const commit = async (showSpinner?: boolean) => { const isInstallingDeps = Object.keys(fileUpdates.installedDeps).length > 0; const spinner = showSpinner diff --git a/packages/qwik/src/cli/add/update-vite-config.ts b/packages/qwik/src/cli/add/update-vite-config.ts deleted file mode 100644 index b66ecb78629..00000000000 --- a/packages/qwik/src/cli/add/update-vite-config.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { FsUpdates, IntegrationData } from '../types'; -import fs from 'fs'; -import { join } from 'path'; -import { updateViteConfig } from '../code-mod/code-mod'; -import type { Options } from 'prettier'; -import { panic } from '../utils/utils'; - -export async function updateViteConfigs( - fileUpdates: FsUpdates, - integration: IntegrationData, - rootDir: string -) { - try { - const viteConfig = integration.pkgJson.__qwik__?.viteConfig; - if (viteConfig) { - const viteConfigPath = join(rootDir, 'vite.config.ts'); - const destContent = await fs.promises.readFile(viteConfigPath, 'utf-8'); - - const ts = (await import('typescript')).default; - let updatedContent = updateViteConfig(ts, destContent, viteConfig); - - if (updatedContent) { - try { - const prettier = (await import('prettier')).default; - - let prettierOpts: Options = { - filepath: viteConfigPath, - }; - - const opts = await prettier.resolveConfig(viteConfigPath); - if (opts) { - prettierOpts = { ...opts, ...prettierOpts }; - } - - updatedContent = prettier.format(updatedContent, prettierOpts); - - updatedContent = updatedContent.replace(`export default`, `\nexport default`); - } catch (e) { - console.error(e); - } - - fileUpdates.files.push({ - path: viteConfigPath, - content: updatedContent, - type: 'modify', - }); - } - } - } catch (e) { - panic(String(e)); - } -} diff --git a/packages/qwik/src/cli/code-mod/code-mod.ts b/packages/qwik/src/cli/code-mod/code-mod.ts deleted file mode 100644 index c50e02ac8c5..00000000000 --- a/packages/qwik/src/cli/code-mod/code-mod.ts +++ /dev/null @@ -1,488 +0,0 @@ -import type { - ArrayLiteralExpression, - Block, - CallExpression, - Expression, - Identifier, - ImportSpecifier, - NamedImports, - ObjectLiteralExpression, - SourceFile, - Statement, - TransformerFactory, -} from 'typescript'; -import type { EnsureImport, ViteConfigUpdates } from '../types'; - -export function updateViteConfig(ts: TypeScript, sourceText: string, updates?: ViteConfigUpdates) { - if ( - !updates?.imports && - !updates?.qwikViteConfig && - !updates?.viteConfig && - !updates?.vitePlugins - ) { - return null; - } - - sourceText = transformSource(ts, sourceText, () => (tsSourceFile) => { - if (updates.imports) { - for (const importData of updates.imports) { - tsSourceFile = ensureImport(ts, tsSourceFile, importData); - } - } - - const statements: Statement[] = []; - - for (const s of tsSourceFile.statements) { - if (ts.isExportAssignment(s) && s.expression && ts.isCallExpression(s.expression)) { - if ( - ts.isIdentifier(s.expression.expression) && - s.expression.expression.text === 'defineConfig' && - (updates.viteConfig || updates.qwikViteConfig || updates.vitePlugins) - ) { - statements.push( - ts.factory.updateExportAssignment( - s, - s.decorators, - s.modifiers, - updateDefineConfig(ts, s.expression, updates) - ) - ); - continue; - } - } - statements.push(s); - } - - return ts.factory.updateSourceFile(tsSourceFile, statements); - }); - - return sourceText; -} - -function ensureImport(ts: TypeScript, tsSourceFile: SourceFile, importData: EnsureImport) { - if (importData && importData.importPath) { - if (Array.isArray(importData.namedImports)) { - importData.namedImports.forEach((namedImport) => { - tsSourceFile = ensureNamedImport(ts, tsSourceFile, namedImport, importData.importPath); - }); - } - if (typeof importData.defaultImport === 'string') { - tsSourceFile = ensureDefaultImport( - ts, - tsSourceFile, - importData.defaultImport, - importData.importPath - ); - } - } - return tsSourceFile; -} - -function ensureNamedImport( - ts: TypeScript, - tsSourceFile: SourceFile, - namedImport: string, - importPath: string -) { - if (!hasNamedImport(ts, tsSourceFile, namedImport, importPath)) { - tsSourceFile = appendImports(ts, tsSourceFile, null, namedImport, importPath); - } - return tsSourceFile; -} - -function ensureDefaultImport( - ts: TypeScript, - tsSourceFile: SourceFile, - defaultImport: string, - importPath: string -) { - if (!hasDefaultImport(ts, tsSourceFile, importPath)) { - tsSourceFile = appendImports(ts, tsSourceFile, defaultImport, null, importPath); - } - return tsSourceFile; -} - -function hasNamedImport( - ts: TypeScript, - tsSourceFile: SourceFile, - namedImport: string, - importPath: string -) { - return !!findNamedImport(ts, tsSourceFile, namedImport, importPath); -} - -function hasDefaultImport(ts: TypeScript, tsSourceFile: SourceFile, importPath: string) { - return !!findDefaultImport(ts, tsSourceFile, importPath); -} - -function findNamedImport( - ts: TypeScript, - tsSourceFile: SourceFile, - namedImport: string, - importPath: string -) { - return findImportDeclarations(ts, tsSourceFile).find((n) => { - if (n.importClause && n.moduleSpecifier && ts.isStringLiteral(n.moduleSpecifier)) { - if (n.moduleSpecifier.text !== importPath) { - return false; - } - const namedImports = n.importClause.namedBindings; - if (namedImports && ts.isNamedImports(namedImports) && namedImports.elements) { - return namedImports.elements.some((namedImportElement) => { - if (ts.isImportSpecifier(namedImportElement)) { - const importName = namedImportElement.name; - if (importName && ts.isIdentifier(importName)) { - return importName.text === namedImport; - } - } - return false; - }); - } - } - return false; - }); -} - -function findDefaultImport( - ts: TypeScript, - tsSourceFile: SourceFile, - - importPath: string -) { - return findImportDeclarations(ts, tsSourceFile).find((n) => { - if (n.importClause && n.moduleSpecifier) { - const modulePath = n.moduleSpecifier; - if (ts.isStringLiteral(modulePath) && modulePath.text === importPath) { - const moduleDefault = n.importClause.name; - if (moduleDefault && moduleDefault.text === importPath) { - return true; - } - } - } - return false; - }); -} - -function findImportDeclarations(ts: TypeScript, tsSourceFile: SourceFile) { - return tsSourceFile.statements.filter(ts.isImportDeclaration); -} - -function appendImports( - ts: TypeScript, - tsSourceFile: SourceFile, - defaultImport: string | null, - namedImport: string | null, - importPath: string -) { - const statements = tsSourceFile.statements.slice(); - let foundExistingImport = false; - - for (let i = statements.length - 1; i >= 0; i--) { - const n = statements[i]; - if (!ts.isImportDeclaration(n)) { - continue; - } - - if (!n.moduleSpecifier || !ts.isStringLiteral(n.moduleSpecifier)) { - continue; - } - - if (n.moduleSpecifier.text !== importPath) { - continue; - } - - foundExistingImport = true; - - const existingNamedImports: ImportSpecifier[] = []; - if (n.importClause) { - const namedImports = n.importClause.namedBindings; - if (namedImports && ts.isNamedImports(namedImports) && namedImports.elements) { - existingNamedImports.push(...namedImports.elements); - } - } - - if (typeof namedImport === 'string') { - const identifier = ts.factory.createIdentifier(namedImport); - const importSpecifier = ts.factory.createImportSpecifier(false, undefined, identifier); - existingNamedImports.push(importSpecifier); - } - - existingNamedImports.sort((a, b) => { - const aName = a.name.escapedText.toString(); - const bName = b.name.escapedText.toString(); - return aName < bName ? -1 : 1; - }); - - let defaultIdentifier = n.importClause ? n.importClause.name : undefined; - if (typeof defaultImport === 'string') { - defaultIdentifier = ts.factory.createIdentifier(defaultImport); - } - - let namedBindings: NamedImports = undefined as any; - if (existingNamedImports.length > 0) { - namedBindings = ts.factory.createNamedImports(existingNamedImports); - } - - statements[i] = ts.factory.updateImportDeclaration( - n, - undefined, - undefined, - ts.factory.createImportClause(false, defaultIdentifier, namedBindings), - n.moduleSpecifier, - undefined - ); - } - - if (!foundExistingImport) { - let defaultIdentifier: Identifier = undefined as any; - let namedBindings: NamedImports = undefined as any; - - if (typeof defaultImport === 'string') { - defaultIdentifier = ts.factory.createIdentifier(defaultImport); - } - - if (typeof namedImport === 'string') { - namedBindings = ts.factory.createNamedImports([ - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier(namedImport) - ), - ]); - } - - const newNamedImport = ts.factory.createImportDeclaration( - undefined, - undefined, - ts.factory.createImportClause(false, defaultIdentifier, namedBindings), - ts.factory.createStringLiteral(importPath) - ); - const lastImportIndex = findLastImportIndex(ts, tsSourceFile); - statements.splice(lastImportIndex + 1, 0, newNamedImport); - } - - return ts.factory.updateSourceFile(tsSourceFile, statements); -} - -function findLastImportIndex(ts: TypeScript, tsSourceFile: SourceFile) { - for (let i = tsSourceFile.statements.length - 1; i >= 0; i--) { - const s = tsSourceFile.statements[i]; - if (ts.isImportDeclaration(s)) { - return i; - } - if (ts.isStringLiteral(s) && s.text === 'use strict') { - return i; - } - } - return 0; -} - -function updateDefineConfig(ts: TypeScript, callExp: CallExpression, updates: ViteConfigUpdates) { - const args: Expression[] = []; - - for (let i = 0; i < callExp.arguments.length; i++) { - const exp = callExp.arguments[i]; - - if (i === 0) { - if (ts.isArrowFunction(exp) && ts.isBlock(exp.body)) { - args.push( - ts.factory.updateArrowFunction( - exp, - exp.modifiers, - exp.typeParameters, - exp.parameters, - exp.type, - exp.equalsGreaterThanToken, - updateDefineConfigFnReturn(ts, exp.body, updates) - ) - ); - continue; - } - - if (ts.isFunctionExpression(exp) && ts.isBlock(exp.body)) { - args.push( - ts.factory.updateFunctionExpression( - exp, - exp.modifiers, - exp.asteriskToken, - exp.name, - exp.typeParameters, - exp.parameters, - exp.type, - updateDefineConfigFnReturn(ts, exp.body, updates) - ) - ); - continue; - } - } - - args.push(exp); - } - - return ts.factory.updateCallExpression(callExp, callExp.expression, callExp.typeArguments, args); -} - -function updateDefineConfigFnReturn(ts: TypeScript, fnBody: Block, updates: ViteConfigUpdates) { - const statements: Statement[] = []; - for (const s of fnBody.statements) { - if (ts.isReturnStatement(s) && s.expression && ts.isObjectLiteralExpression(s.expression)) { - statements.push( - ts.factory.updateReturnStatement(s, updateVitConfigObj(ts, s.expression, updates)) - ); - } else { - statements.push(s); - } - } - return ts.factory.updateBlock(fnBody, statements); -} - -function updateVitConfigObj( - ts: TypeScript, - obj: ObjectLiteralExpression, - updates: ViteConfigUpdates -) { - if (updates.viteConfig) { - obj = updateObjectLiteralExpression(ts, obj, updates.viteConfig); - } - if (updates.vitePlugins || updates.qwikViteConfig) { - obj = updatePlugins(ts, obj, updates); - } - return obj; -} - -function updatePlugins(ts: TypeScript, obj: ObjectLiteralExpression, updates: ViteConfigUpdates) { - const properties: any[] = []; - - for (const p of obj.properties) { - if (ts.isPropertyAssignment(p)) { - if (p.name && ts.isIdentifier(p.name) && p.name.text === 'plugins') { - if (ts.isArrayLiteralExpression(p.initializer)) { - properties.push( - ts.factory.updatePropertyAssignment( - p, - p.name, - updatePluginsArray(ts, p.initializer, updates) - ) - ); - continue; - } - } - } - properties.push(p); - } - - return ts.factory.updateObjectLiteralExpression(obj, properties); -} - -function updatePluginsArray( - ts: TypeScript, - arr: ArrayLiteralExpression, - updates: ViteConfigUpdates -) { - const elms: Expression[] = [...arr.elements]; - - if (updates.vitePlugins) { - for (const vitePlugin of updates.vitePlugins) { - const pluginExp = createPluginCall(ts, vitePlugin); - if (pluginExp) { - elms.push(pluginExp); - } - } - } - - if (updates.qwikViteConfig) { - for (let i = 0; i < elms.length; i++) { - const elm = elms[i]; - if (ts.isCallExpression(elm) && ts.isIdentifier(elm.expression)) { - if (elm.expression.escapedText === 'qwikVite') { - elms[i] = updateQwikCityPlugin(ts, elm, updates.qwikViteConfig); - } - } - } - } - - return ts.factory.updateArrayLiteralExpression(arr, elms); -} - -function createPluginCall(ts: TypeScript, vitePlugin: string) { - if (typeof vitePlugin === 'string') { - const tmp = ts.createSourceFile( - 'tmp.ts', - 'export default ' + vitePlugin, - ts.ScriptTarget.Latest - ); - for (const s of tmp.statements) { - if (ts.isExportAssignment(s)) { - return s.expression; - } - } - } - return null; -} - -function updateQwikCityPlugin( - ts: TypeScript, - callExp: CallExpression, - qwikViteConfig: { [key: string]: string } -) { - const args = callExp.arguments.slice(); - - const config = - args[0] && ts.isObjectLiteralExpression(args[0]) - ? args[0] - : ts.factory.createObjectLiteralExpression(); - - args[0] = updateObjectLiteralExpression(ts, config, qwikViteConfig); - - return ts.factory.updateCallExpression(callExp, callExp.expression, callExp.typeArguments, args); -} - -function updateObjectLiteralExpression( - ts: TypeScript, - obj: ObjectLiteralExpression, - updateObj: { [propName: string]: string } -) { - for (const [propName, value] of Object.entries(updateObj)) { - if (typeof value === 'string') { - const tmp = ts.createSourceFile('tmp.ts', 'export default ' + value, ts.ScriptTarget.Latest); - - for (const s of tmp.statements) { - if (ts.isExportAssignment(s)) { - const exp = s.expression; - let added = false; - const properties: any[] = []; - for (const p of obj.properties) { - if (p.name && ts.isIdentifier(p.name) && p.name.text === propName) { - properties.push(ts.factory.createPropertyAssignment(propName, exp)); - added = true; - } else { - properties.push(p); - } - } - if (!added) { - properties.unshift(ts.factory.createPropertyAssignment(propName, exp)); - } - - obj = ts.factory.updateObjectLiteralExpression(obj, properties); - } - } - } - } - return obj; -} - -function transformSource(ts: TypeScript, sourceText: string, transformer: TransformerFactory) { - const t = ts.transform(ts.createSourceFile('/tmp.ts', sourceText, ts.ScriptTarget.Latest), [ - transformer, - ]); - - const p = ts.createPrinter({ - removeComments: false, - omitTrailingSemicolon: false, - noEmitHelpers: true, - }); - - return p.printFile(t.transformed[0]); -} - -type TypeScript = typeof import('typescript'); diff --git a/packages/qwik/src/cli/code-mod/code-mod.unit.ts b/packages/qwik/src/cli/code-mod/code-mod.unit.ts deleted file mode 100644 index c45acbc66e3..00000000000 --- a/packages/qwik/src/cli/code-mod/code-mod.unit.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { updateViteConfig } from './code-mod'; -import { test } from 'uvu'; -import { match } from 'uvu/assert'; -import ts from 'typescript'; - -test('update existing qwik vite plugin config prop', () => { - const sourceText = ` - export default defineConfig(() => { - return { - plugins: [ - qwikVite({ssr:false}), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - qwikViteConfig: { ssr: `{ outDir: 'netlify/edge-functions/entry.netlify' }` }, - })!; - match(outputText, 'qwikVite({ ssr: { outDir: "netlify/edge-functions/entry.netlify" } })'); -}); - -test('update qwik vite plugin config', () => { - const sourceText = ` - export default defineConfig(() => { - return { - plugins: [ - qwikVite({abc:88}), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - qwikViteConfig: { ssr: `{ outDir: 'netlify/edge-functions/entry.netlify' }` }, - })!; - match( - outputText, - 'qwikVite({ ssr: { outDir: "netlify/edge-functions/entry.netlify" }, abc: 88 })' - ); -}); - -test('add qwik vite plugin config', () => { - const sourceText = ` - export default defineConfig(() => { - return { - plugins: [ - qwikVite(), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - qwikViteConfig: { ssr: `{ outDir: 'netlify/edge-functions/entry.netlify' }` }, - })!; - match(outputText, 'qwikVite({ ssr: { outDir: "netlify/edge-functions/entry.netlify" } })'); -}); - -test('add vite plugin', () => { - const sourceText = ` - export default defineConfig(() => { - return { - plugins: [ - qwikVite(), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - vitePlugins: [`netlifyEdge({ functionName: 'entry.netlify' })`], - })!; - match(outputText, 'netlifyEdge({ functionName: "entry.netlify" })'); -}); - -test('update vite config', () => { - const sourceText = ` - export default defineConfig(() => { - return { - ssr: {}, - plugins: [ - qwikVite(), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - viteConfig: { ssr: `{ target: 'webworker', noExternal: true }` }, - })!; - match(outputText, 'ssr: { target: "webworker", noExternal: true'); -}); - -test('add vite config', () => { - const sourceText = ` - export default defineConfig(() => { - return { - plugins: [ - qwikVite(), - ], - }; - }); - `; - const outputText = updateViteConfig(ts, sourceText, { - viteConfig: { ssr: `{ target: 'webworker', noExternal: true }` }, - })!; - match(outputText, 'ssr: { target: "webworker", noExternal: true'); -}); - -test('add imports to side effect default import', () => { - const sourceText = `import a from "@builder.io/qwik";`; - const outputText = updateViteConfig(ts, sourceText, { - imports: [ - { namedImports: ['b'], importPath: '@builder.io/qwik' }, - { namedImports: ['c', 'd'], importPath: '@builder.io/sdk-react' }, - ], - })!; - match(outputText, 'import a, { b } from "@builder.io/qwik";'); - match(outputText, 'import { c, d } from "@builder.io/sdk-react";'); -}); - -test('do not re-add named imports', () => { - const sourceText = ` - import { a } from "@builder.io/qwik"; - import { b, c } from "@builder.io/sdk-react"; - `; - const outputText = updateViteConfig(ts, sourceText, { - imports: [ - { namedImports: ['a'], importPath: '@builder.io/qwik' }, - { namedImports: ['b', 'c'], importPath: '@builder.io/sdk-react' }, - ], - })!; - match(outputText, 'import { a } from "@builder.io/qwik";'); - match(outputText, 'import { b, c } from "@builder.io/sdk-react";'); -}); - -test('add imports to side effect import', () => { - const sourceText = `import "@builder.io/qwik";\nconsole.log(88);`; - const outputText = updateViteConfig(ts, sourceText, { - imports: [{ namedImports: ['a'], importPath: '@builder.io/qwik' }], - })!; - match(outputText, 'import { a } from "@builder.io/qwik"'); -}); - -test('leave existing imports', () => { - const sourceText = `import { a } from "@builder.io/qwik";`; - const outputText = updateViteConfig(ts, sourceText, { - imports: [{ namedImports: ['b'], importPath: '@builder.io/qwik' }], - })!; - match(outputText, 'import { a, b } from "@builder.io/qwik";'); -}); - -test('renamed default import with existing named import', () => { - const sourceText = `import a, { b } from '@builder.io/sdk-react'`; - const outputText = updateViteConfig(ts, sourceText, { - imports: [ - { defaultImport: 'c', importPath: '@builder.io/sdk-react' }, - { namedImports: ['d'], importPath: '@builder.io/qwik' }, - ], - })!; - match(outputText, 'import c, { b } from "@builder.io/sdk-react";'); - match(outputText, 'import { d } from "@builder.io/qwik";'); -}); - -test('renamed default import', () => { - const sourceText = `import a from '@builder.io/sdk-react'`; - const outputText = updateViteConfig(ts, sourceText, { - imports: [{ defaultImport: 'b', importPath: '@builder.io/sdk-react' }], - })!; - match(outputText, 'import b from "@builder.io/sdk-react";'); -}); - -test('add default import to empty file', () => { - const sourceText = ``; - const outputText = updateViteConfig(ts, sourceText, { - imports: [{ defaultImport: 'a', importPath: '@builder.io/sdk-react' }], - })!; - match(outputText, 'import a from "@builder.io/sdk-react";'); -}); - -test('add named imports to empty file', () => { - const sourceText = ``; - const outputText = updateViteConfig(ts, sourceText, { - imports: [{ namedImports: ['a'], importPath: '@builder.io/sdk-react' }], - })!; - match(outputText, 'import { a } from "@builder.io/sdk-react";'); -}); - -test.run(); diff --git a/packages/qwik/src/cli/types.ts b/packages/qwik/src/cli/types.ts index 1a5ac684cb1..18e30c41e5b 100644 --- a/packages/qwik/src/cli/types.ts +++ b/packages/qwik/src/cli/types.ts @@ -36,15 +36,13 @@ export interface IntegrationData { pkgJson: IntegrationPackageJson; dir: string; priority: number; - viteConfig?: ViteConfigUpdates; } -export type IntegrationType = 'app' | 'feature' | 'server' | 'static-generator'; +export type IntegrationType = 'app' | 'feature' | 'adaptor'; export interface Feature { id: string; description: string; - type: 'server' | 'static'; add: FeatureCmd; } @@ -69,7 +67,6 @@ export interface IntegrationPackageJson { __qwik__?: { nextSteps?: string[]; priority: number; - viteConfig?: ViteConfigUpdates; }; } @@ -78,10 +75,3 @@ export interface EnsureImport { namedImports?: string[]; importPath: string; } - -export interface ViteConfigUpdates { - imports?: EnsureImport[]; - viteConfig?: { [key: string]: string }; - vitePlugins?: string[]; - qwikViteConfig?: { [key: string]: string }; -} diff --git a/packages/qwik/src/cli/utils/integrations.ts b/packages/qwik/src/cli/utils/integrations.ts index 4f762cb95ce..c568863484e 100644 --- a/packages/qwik/src/cli/utils/integrations.ts +++ b/packages/qwik/src/cli/utils/integrations.ts @@ -8,7 +8,7 @@ let integrations: IntegrationData[] | null = null; export async function loadIntegrations() { if (!integrations) { const loadingIntegrations: IntegrationData[] = []; - const integrationTypes: IntegrationType[] = ['app', 'feature', 'server', 'static-generator']; + const integrationTypes: IntegrationType[] = ['app', 'feature', 'adaptor']; const integrationsDir = join(__dirname, 'starters'); const integrationsDirNames = await fs.promises.readdir(integrationsDir); @@ -33,7 +33,6 @@ export async function loadIntegrations() { dir: dirPath, pkgJson, priority: pkgJson?.__qwik__?.priority ?? 0, - viteConfig: pkgJson?.__qwik__?.viteConfig, }; loadingIntegrations.push(integration); } diff --git a/packages/qwik/src/optimizer/src/api.md b/packages/qwik/src/optimizer/src/api.md index 2aade84ee46..e69a6c47ff5 100644 --- a/packages/qwik/src/optimizer/src/api.md +++ b/packages/qwik/src/optimizer/src/api.md @@ -270,6 +270,30 @@ export interface QwikSymbol { // @alpha (undocumented) export function qwikVite(qwikViteOpts?: QwikVitePluginOptions): any; +// @alpha (undocumented) +export interface QwikVitePlugin { + // (undocumented) + api: QwikVitePluginApi; + // (undocumented) + name: 'vite-plugin-qwik'; +} + +// @alpha (undocumented) +export interface QwikVitePluginApi { + // (undocumented) + getClientOutDir: () => string | null; + // (undocumented) + getManifest: () => QwikManifest | null; + // (undocumented) + getOptimizer: () => Optimizer | null; + // Warning: (ae-forgotten-export) The symbol "NormalizedQwikPluginOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getOptions: () => NormalizedQwikPluginOptions; + // (undocumented) + getRootDir: () => string | null; +} + // @alpha (undocumented) export interface QwikVitePluginOptions { // (undocumented) diff --git a/packages/qwik/src/optimizer/src/index.ts b/packages/qwik/src/optimizer/src/index.ts index 2ea3a2a6622..05596cac4d7 100644 --- a/packages/qwik/src/optimizer/src/index.ts +++ b/packages/qwik/src/optimizer/src/index.ts @@ -35,7 +35,7 @@ export type { } from './types'; export type { QwikRollupPluginOptions } from './plugins/rollup'; -export type { QwikVitePluginOptions } from './plugins/vite'; +export type { QwikVitePluginOptions, QwikVitePluginApi, QwikVitePlugin } from './plugins/vite'; export type { QwikBuildMode, QwikBuildTarget } from './plugins/plugin'; export { qwikRollup } from './plugins/rollup'; diff --git a/packages/qwik/src/optimizer/src/plugins/plugin.ts b/packages/qwik/src/optimizer/src/plugins/plugin.ts index 45cc8ec2318..78b25ecdd95 100644 --- a/packages/qwik/src/optimizer/src/plugins/plugin.ts +++ b/packages/qwik/src/optimizer/src/plugins/plugin.ts @@ -169,7 +169,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { } if (Array.isArray(updatedOpts.input)) { - opts.input = updatedOpts.input; + opts.input = [...updatedOpts.input]; } else if (typeof updatedOpts.input === 'string') { opts.input = [updatedOpts.input]; } else { @@ -184,9 +184,16 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) { opts.input = [path.resolve(srcDir, 'index.ts')]; } } - opts.input = opts.input.map((input) => { - return normalizePath(path.resolve(opts.rootDir, input)); - }); + opts.input = opts.input.reduce((inputs, i) => { + let input = i; + if (!i.startsWith('@') && !i.startsWith('~')) { + input = normalizePath(path.resolve(opts.rootDir, i)); + } + if (!inputs.includes(input)) { + inputs.push(input); + } + return inputs; + }, [] as string[]); if (typeof updatedOpts.outDir === 'string') { opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir))); diff --git a/packages/qwik/src/optimizer/src/plugins/rollup.ts b/packages/qwik/src/optimizer/src/plugins/rollup.ts index 45503f55cae..8cc15543263 100644 --- a/packages/qwik/src/optimizer/src/plugins/rollup.ts +++ b/packages/qwik/src/optimizer/src/plugins/rollup.ts @@ -184,7 +184,6 @@ export function normalizeRollupOutputOptions( if (opts.target === 'ssr') { // ssr output - outputOpts.inlineDynamicImports = true; if (opts.buildMode === 'production') { if (!outputOpts.assetFileNames) { outputOpts.assetFileNames = 'build/q-[hash].[ext]'; diff --git a/packages/qwik/src/optimizer/src/plugins/vite.ts b/packages/qwik/src/optimizer/src/plugins/vite.ts index faebb69b18a..8c19d10778d 100644 --- a/packages/qwik/src/optimizer/src/plugins/vite.ts +++ b/packages/qwik/src/optimizer/src/plugins/vite.ts @@ -2,6 +2,7 @@ import type { Plugin as VitePlugin, UserConfig, ViteDevServer } from 'vite'; import type { EntryStrategy, GlobalInjections, + Optimizer, OptimizerOptions, OptimizerSystem, QwikManifest, @@ -38,18 +39,23 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { 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; const injections: GlobalInjections[] = []; const qwikPlugin = createPlugin(qwikViteOpts.optimizerOptions); + const api: QwikVitePluginApi = { + getOptimizer: () => qwikPlugin.getOptimizer(), + getOptions: () => qwikPlugin.getOptions(), + getManifest: () => manifestInput, + getRootDir: () => qwikPlugin.getOptions().rootDir, + getClientOutDir: () => clientOutDir, + }; + const vitePlugin: VitePlugin = { name: 'vite-plugin-qwik', - enforce: 'pre', - - api: { - getOptimizer: () => qwikPlugin.getOptimizer(), - getOptions: () => qwikPlugin.getOptions(), - }, + api, async config(viteConfig, viteEnv) { await qwikPlugin.init(); @@ -117,9 +123,11 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { // from --ssr flag user config // entry.server.ts (express/cloudflare/netlify) pluginOpts.input = viteConfig.build.ssr; - } else { + } else if (typeof qwikViteOpts.ssr?.input === 'string') { // entry.ssr.tsx input (exports render()) - pluginOpts.input = qwikViteOpts.ssr?.input; + pluginOpts.input = qwikViteOpts.ssr.input; + } else if (viteConfig.build?.ssr && Array.isArray(viteConfig.build?.rollupOptions?.input)) { + pluginOpts.input = viteConfig.build!.rollupOptions!.input; } pluginOpts.outDir = qwikViteOpts.ssr?.outDir; @@ -178,14 +186,16 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any { const opts = qwikPlugin.normalizeOptions(pluginOpts); - // TODO: better way for other plugins to get ahold of the manifest info - (globalThis as any).QWIK_MANIFEST = pluginOpts.manifestInput; + manifestInput = pluginOpts.manifestInput || null; - // TODO: better way for other plugins to get ahold of client output directory path - (globalThis as any).QWIK_CLIENT_OUT_DIR = qwikPlugin.normalizePath( + clientOutDir = qwikPlugin.normalizePath( sys.path.resolve(opts.rootDir, qwikViteOpts.client?.outDir || CLIENT_OUT_DIR) ); + // TODO: Remove globalThis that was previously used. Left in for backwards compatibility. + (globalThis as any).QWIK_MANIFEST = manifestInput; + (globalThis as any).QWIK_CLIENT_OUT_DIR = clientOutDir; + if (typeof qwikViteOpts.client?.devInput === 'string') { clientDevInput = path.resolve(opts.rootDir, qwikViteOpts.client.devInput); } else { @@ -680,6 +690,25 @@ export interface QwikVitePluginOptions { | null; } +/** + * @alpha + */ +export interface QwikVitePluginApi { + getOptimizer: () => Optimizer | null; + getOptions: () => NormalizedQwikPluginOptions; + getManifest: () => QwikManifest | null; + getRootDir: () => string | null; + getClientOutDir: () => string | null; +} + +/** + * @alpha + */ +export interface QwikVitePlugin { + name: 'vite-plugin-qwik'; + api: QwikVitePluginApi; +} + export interface QwikViteDevResponse { _qwikEnvData?: Record; } diff --git a/scripts/api.ts b/scripts/api.ts index 1659f9840ad..487a7e3ce9d 100644 --- a/scripts/api.ts +++ b/scripts/api.ts @@ -64,6 +64,34 @@ export function apiExtractor(config: BuildConfig) { join(config.packagesDir, 'qwik-city', 'buildtime', 'vite'), join(config.packagesDir, 'qwik-city', 'lib', 'vite', 'index.d.ts') ); + createTypesApi( + config, + join(config.packagesDir, 'qwik-city', 'adaptors', 'cloudflare-pages', 'vite'), + join( + config.packagesDir, + 'qwik-city', + 'lib', + 'adaptors', + 'cloudflare-pages', + 'vite', + 'index.d.ts' + ) + ); + createTypesApi( + config, + join(config.packagesDir, 'qwik-city', 'adaptors', 'express', 'vite'), + join(config.packagesDir, 'qwik-city', 'lib', 'adaptors', 'express', 'vite', 'index.d.ts') + ); + createTypesApi( + config, + join(config.packagesDir, 'qwik-city', 'adaptors', 'netlify-edge', 'vite'), + join(config.packagesDir, 'qwik-city', 'lib', 'adaptors', 'netlify-edge', 'vite', 'index.d.ts') + ); + createTypesApi( + config, + join(config.packagesDir, 'qwik-city', 'adaptors', 'static', 'vite'), + join(config.packagesDir, 'qwik-city', 'lib', 'adaptors', 'static', 'vite', 'index.d.ts') + ); createTypesApi( config, join(config.packagesDir, 'qwik-city', 'middleware', 'cloudflare-pages'), @@ -81,8 +109,8 @@ export function apiExtractor(config: BuildConfig) { ); createTypesApi( config, - join(config.packagesDir, 'qwik-city', 'static', 'node'), - join(config.packagesDir, 'qwik-city', 'lib', 'static', 'node', 'index.d.ts') + join(config.packagesDir, 'qwik-city', 'static'), + join(config.packagesDir, 'qwik-city', 'lib', 'static', 'index.d.ts') ); generateQwikCityReferenceModules(config); @@ -129,6 +157,13 @@ declare module '@qwik-city-plan' { export const trailingSlash: boolean; export const basePathname: string; export const cacheModules: boolean; + export default { + routes: any[]; + menus: any[]; + trailingSlash: boolean; + basePathname: string; + cacheModules: boolean; + }; } `; const srcModulesPath = join(config.packagesDir, 'qwik-city', 'lib'); diff --git a/scripts/create-qwik-cli.ts b/scripts/create-qwik-cli.ts index 1b4c98e5012..f7cf27ab8d1 100644 --- a/scripts/create-qwik-cli.ts +++ b/scripts/create-qwik-cli.ts @@ -52,7 +52,6 @@ async function bundleCreateQwikCli(config: BuildConfig, srcCliDir: string, distC ], external: ['prettier', 'typescript'], define: { - 'globalThis.CODE_MOD': 'false', 'globalThis.QWIK_VERSION': JSON.stringify(config.distVersion), }, banner: { @@ -113,7 +112,7 @@ export async function publishCreateQwikCli( export async function copyStartersDir( config: BuildConfig, distCliDir: string, - typeDirs: ('apps' | 'features' | 'servers' | 'static-generators')[] + typeDirs: ('apps' | 'features' | 'adaptors')[] ) { const distStartersDir = join(distCliDir, 'starters'); try { diff --git a/scripts/qwik-city.ts b/scripts/qwik-city.ts index 34009cf69a5..6c9875ace7a 100644 --- a/scripts/qwik-city.ts +++ b/scripts/qwik-city.ts @@ -1,4 +1,4 @@ -import { BuildConfig, nodeTarget, panic, run, watcher } from './util'; +import { BuildConfig, nodeTarget, panic, run, watcher, importPath } from './util'; import { build, Plugin, transform } from 'esbuild'; import { join } from 'path'; import { readPackageJson, writePackageJson } from './package-json'; @@ -20,10 +20,16 @@ export async function buildQwikCity(config: BuildConfig) { await Promise.all([ buildServiceWorker(config, inputDir, outputDir), buildVite(config, inputDir, outputDir), + buildAdaptorCloudflarePagesVite(config, inputDir, outputDir), + buildAdaptorExpressVite(config, inputDir, outputDir), + buildAdaptorNetlifyEdgeVite(config, inputDir, outputDir), + buildAdaptorStaticVite(config, inputDir, outputDir), buildMiddlewareCloudflarePages(config, inputDir, outputDir), buildMiddlewareNetlifyEdge(config, inputDir, outputDir), buildMiddlewareNode(config, inputDir, outputDir), + buildStatic(config, inputDir, outputDir), buildStaticNode(config, inputDir, outputDir), + buildStaticDeno(config, inputDir, outputDir), ]); await buildRuntime(inputDir); @@ -39,6 +45,22 @@ export async function buildQwikCity(config: BuildConfig) { import: './index.qwik.mjs', require: './index.qwik.cjs', }, + './adaptors/cloudflare-pages/vite': { + import: './adaptors/cloudflare-pages/vite/index.mjs', + require: './adaptors/cloudflare-pages/vite/index.cjs', + }, + './adaptors/express/vite': { + import: './adaptors/express/vite/index.mjs', + require: './adaptors/express/vite/index.cjs', + }, + './adaptors/netlify-edge/vite': { + import: './adaptors/netlify-edge/vite/index.mjs', + require: './adaptors/netlify-edge/vite/index.cjs', + }, + './adaptors/static/vite': { + import: './adaptors/static/vite/index.mjs', + require: './adaptors/static/vite/index.cjs', + }, './middleware/cloudflare-pages': { import: './middleware/cloudflare-pages/index.mjs', }, @@ -49,9 +71,9 @@ export async function buildQwikCity(config: BuildConfig) { './middleware/netlify-edge': { import: './middleware/netlify-edge/index.mjs', }, - './static/node': { - import: './static/node/index.mjs', - require: './static/node/index.cjs', + './static': { + import: './static/index.mjs', + require: './static/index.cjs', }, './vite': { import: './vite/index.mjs', @@ -63,6 +85,7 @@ export async function buildQwikCity(config: BuildConfig) { }, }, files: [ + 'adaptors', 'index.d.ts', 'index.qwik.mjs', 'index.qwik.cjs', @@ -103,7 +126,16 @@ async function buildRuntime(input: string) { async function buildVite(config: BuildConfig, inputDir: string, outputDir: string) { const entryPoints = [join(inputDir, 'buildtime', 'vite', 'index.ts')]; - const external = ['source-map', 'vfile', '@mdx-js/mdx', 'typescript']; + const external = [ + 'fs', + 'path', + 'url', + 'vite', + 'source-map', + 'vfile', + '@mdx-js/mdx', + 'typescript', + ]; const swRegisterPath = join(inputDir, 'runtime', 'src', 'library', 'sw-register.ts'); let swRegisterCode = await readFile(swRegisterPath, 'utf-8'); @@ -183,6 +215,134 @@ async function buildServiceWorker(config: BuildConfig, inputDir: string, outputD }); } +async function buildAdaptorCloudflarePagesVite( + config: BuildConfig, + inputDir: string, + outputDir: string +) { + const entryPoints = [join(inputDir, 'adaptors', 'cloudflare-pages', 'vite', 'index.ts')]; + + const external = ['vite', 'fs', 'path', '@builder.io/qwik-city/static']; + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'cloudflare-pages', 'vite', 'index.mjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'esm', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.mjs')], + }); + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'cloudflare-pages', 'vite', 'index.cjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'cjs', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.cjs')], + }); +} + +async function buildAdaptorExpressVite(config: BuildConfig, inputDir: string, outputDir: string) { + const entryPoints = [join(inputDir, 'adaptors', 'express', 'vite', 'index.ts')]; + + const external = ['vite', 'fs', 'path', '@builder.io/qwik-city/static']; + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'express', 'vite', 'index.mjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'esm', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.mjs')], + }); + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'express', 'vite', 'index.cjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'cjs', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.cjs')], + }); +} + +async function buildAdaptorNetlifyEdgeVite( + config: BuildConfig, + inputDir: string, + outputDir: string +) { + const entryPoints = [join(inputDir, 'adaptors', 'netlify-edge', 'vite', 'index.ts')]; + + const external = ['vite', 'fs', 'path', '@builder.io/qwik-city/static']; + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'netlify-edge', 'vite', 'index.mjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'esm', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.mjs')], + }); + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'netlify-edge', 'vite', 'index.cjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'cjs', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.cjs')], + }); +} + +async function buildAdaptorStaticVite(config: BuildConfig, inputDir: string, outputDir: string) { + const entryPoints = [join(inputDir, 'adaptors', 'static', 'vite', 'index.ts')]; + + const external = ['vite', 'fs', 'path', '@builder.io/qwik-city/static']; + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'static', 'vite', 'index.mjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'esm', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.mjs')], + }); + + await build({ + entryPoints, + outfile: join(outputDir, 'adaptors', 'netlify-edge', 'vite', 'index.cjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'cjs', + watch: watcher(config), + external, + plugins: [importPath(/static$/, '../../../static/index.cjs')], + }); +} + async function buildMiddlewareCloudflarePages( config: BuildConfig, inputDir: string, @@ -253,14 +413,50 @@ async function buildMiddlewareNetlifyEdge( }); } +async function buildStatic(config: BuildConfig, inputDir: string, outputDir: string) { + const entryPoints = [join(inputDir, 'static', 'index.ts')]; + + await build({ + entryPoints, + outfile: join(outputDir, 'static', 'index.mjs'), + bundle: true, + platform: 'neutral', + format: 'esm', + watch: watcher(config), + }); + + await build({ + entryPoints, + outfile: join(outputDir, 'static', 'index.cjs'), + bundle: true, + platform: 'node', + target: nodeTarget, + format: 'cjs', + watch: watcher(config), + }); +} + +async function buildStaticDeno(config: BuildConfig, inputDir: string, outputDir: string) { + const entryPoints = [join(inputDir, 'static', 'deno', 'index.ts')]; + + await build({ + entryPoints, + outfile: join(outputDir, 'static', 'deno.mjs'), + bundle: true, + platform: 'neutral', + format: 'esm', + watch: watcher(config), + }); +} + async function buildStaticNode(config: BuildConfig, inputDir: string, outputDir: string) { const entryPoints = [join(inputDir, 'static', 'node', 'index.ts')]; - const external = ['fs', 'os', 'path', 'url', 'node-fetch', '@qwik-city-plan']; + const external = ['fs', 'node-fetch', 'os', 'path', 'url', 'worker_threads']; await build({ entryPoints, - outfile: join(outputDir, 'static', 'node', 'index.mjs'), + outfile: join(outputDir, 'static', 'node.mjs'), bundle: true, platform: 'node', target: nodeTarget, @@ -271,7 +467,7 @@ async function buildStaticNode(config: BuildConfig, inputDir: string, outputDir: await build({ entryPoints, - outfile: join(outputDir, 'static', 'node', 'index.cjs'), + outfile: join(outputDir, 'static', 'node.cjs'), bundle: true, platform: 'node', target: nodeTarget, diff --git a/scripts/submodule-cli.ts b/scripts/submodule-cli.ts index 1e82da6b596..c67e3bddb9c 100644 --- a/scripts/submodule-cli.ts +++ b/scripts/submodule-cli.ts @@ -38,14 +38,13 @@ export async function submoduleCli(config: BuildConfig) { ], external: ['prettier', 'typescript'], define: { - 'globalThis.CODE_MOD': 'true', 'globalThis.QWIK_VERSION': JSON.stringify(config.distVersion), }, }); await copyFile(join(config.srcDir, submodule, 'qwik.cjs'), join(config.distPkgDir, 'qwik.cjs')); - await copyStartersDir(config, config.distPkgDir, ['features', 'servers', 'static-generators']); + await copyStartersDir(config, config.distPkgDir, ['features', 'adaptors']); console.log('📠', submodule); } diff --git a/starters/servers/cloudflare-pages/.node-version b/starters/adaptors/cloudflare-pages/.node-version similarity index 100% rename from starters/servers/cloudflare-pages/.node-version rename to starters/adaptors/cloudflare-pages/.node-version diff --git a/starters/servers/cloudflare-pages/README.md b/starters/adaptors/cloudflare-pages/README.md similarity index 100% rename from starters/servers/cloudflare-pages/README.md rename to starters/adaptors/cloudflare-pages/README.md diff --git a/starters/adaptors/cloudflare-pages/adaptors/cloudflare-pages/vite.config.ts b/starters/adaptors/cloudflare-pages/adaptors/cloudflare-pages/vite.config.ts new file mode 100644 index 00000000000..65cd35af183 --- /dev/null +++ b/starters/adaptors/cloudflare-pages/adaptors/cloudflare-pages/vite.config.ts @@ -0,0 +1,23 @@ +import { cloudflarePagesAdaptor } from '@builder.io/qwik-city/adaptors/cloudflare-pages/vite'; +import { extendConfig } from '@builder.io/qwik-city/vite'; +import baseConfig from '../../vite.config'; + +export default extendConfig(baseConfig, () => { + return { + build: { + ssr: true, + rollupOptions: { + input: ['src/entry.cloudflare-pages.tsx', 'src/entry.ssr.tsx', '@qwik-city-plan'], + }, + }, + ssr: { + target: 'webworker', + noExternal: true, + }, + plugins: [ + cloudflarePagesAdaptor({ + staticGenerate: true, + }), + ], + }; +}); diff --git a/starters/servers/cloudflare-pages/functions/[[path]].ts b/starters/adaptors/cloudflare-pages/functions/[[path]].ts similarity index 100% rename from starters/servers/cloudflare-pages/functions/[[path]].ts rename to starters/adaptors/cloudflare-pages/functions/[[path]].ts diff --git a/starters/servers/cloudflare-pages/gitignore b/starters/adaptors/cloudflare-pages/gitignore similarity index 100% rename from starters/servers/cloudflare-pages/gitignore rename to starters/adaptors/cloudflare-pages/gitignore diff --git a/starters/adaptors/cloudflare-pages/package.json b/starters/adaptors/cloudflare-pages/package.json new file mode 100644 index 00000000000..65408c95498 --- /dev/null +++ b/starters/adaptors/cloudflare-pages/package.json @@ -0,0 +1,13 @@ +{ + "description": "Cloudflare Pages", + "scripts": { + "build.server": "vite build -c adaptors/cloudflare-pages/vite.config.ts", + "serve": "wrangler pages dev ./dist" + }, + "devDependencies": { + "wrangler": "beta" + }, + "__qwik__": { + "priority": -1 + } +} diff --git a/starters/servers/netlify-edge/public/_headers b/starters/adaptors/cloudflare-pages/public/_headers similarity index 57% rename from starters/servers/netlify-edge/public/_headers rename to starters/adaptors/cloudflare-pages/public/_headers index 08e5e27d5e0..0690cb4204a 100644 --- a/starters/servers/netlify-edge/public/_headers +++ b/starters/adaptors/cloudflare-pages/public/_headers @@ -1,2 +1,4 @@ +# https://developers.cloudflare.com/pages/platform/headers/ + /build/* Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable diff --git a/starters/adaptors/cloudflare-pages/public/_redirects b/starters/adaptors/cloudflare-pages/public/_redirects new file mode 100644 index 00000000000..e2746108330 --- /dev/null +++ b/starters/adaptors/cloudflare-pages/public/_redirects @@ -0,0 +1 @@ +# https://developers.cloudflare.com/pages/platform/redirects/ diff --git a/starters/adaptors/cloudflare-pages/public/_routes.json b/starters/adaptors/cloudflare-pages/public/_routes.json new file mode 100644 index 00000000000..62c81391e78 --- /dev/null +++ b/starters/adaptors/cloudflare-pages/public/_routes.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "include": ["/*"], + "exclude": [ + "/assets/*", + "/build/*", + "/favicon.ico", + "/manifest.json", + "/robots.txt", + "/service-worker.js", + "/sitemap.xml" + ] +} diff --git a/starters/adaptors/cloudflare-pages/src/entry.cloudflare-pages.tsx b/starters/adaptors/cloudflare-pages/src/entry.cloudflare-pages.tsx new file mode 100644 index 00000000000..80683a417b4 --- /dev/null +++ b/starters/adaptors/cloudflare-pages/src/entry.cloudflare-pages.tsx @@ -0,0 +1,7 @@ +import { createQwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'; +import qwikCityPlan from '@qwik-city-plan'; +import render from './entry.ssr'; + +const onRequest = createQwikCity({ render, qwikCityPlan }); + +export { onRequest }; diff --git a/starters/servers/express/README.md b/starters/adaptors/express/README.md similarity index 100% rename from starters/servers/express/README.md rename to starters/adaptors/express/README.md diff --git a/starters/adaptors/express/adaptors/express/vite.config.ts b/starters/adaptors/express/adaptors/express/vite.config.ts new file mode 100644 index 00000000000..5b06f2d274a --- /dev/null +++ b/starters/adaptors/express/adaptors/express/vite.config.ts @@ -0,0 +1,23 @@ +import { expressAdaptor } from '@builder.io/qwik-city/adaptors/express/vite'; +import { extendConfig } from '@builder.io/qwik-city/vite'; +import baseConfig from '../../vite.config'; + +export default extendConfig(baseConfig, () => { + return { + build: { + ssr: true, + rollupOptions: { + input: ['src/entry.express.tsx', 'src/entry.ssr.tsx', '@qwik-city-plan'], + }, + }, + ssr: { + target: 'webworker', + noExternal: true, + }, + plugins: [ + expressAdaptor({ + staticGenerate: true, + }), + ], + }; +}); diff --git a/starters/servers/express/package.json b/starters/adaptors/express/package.json similarity index 73% rename from starters/servers/express/package.json rename to starters/adaptors/express/package.json index a1c1ecda707..bb99edba5e7 100644 --- a/starters/servers/express/package.json +++ b/starters/adaptors/express/package.json @@ -1,7 +1,7 @@ { "description": "Express.js server", "scripts": { - "build.server": "vite build --ssr src/entry.express.tsx", + "build.server": "vite build -c adaptors/express/vite.config.ts", "serve": "node server/entry.express" }, "devDependencies": { diff --git a/starters/servers/express/src/entry.express.tsx b/starters/adaptors/express/src/entry.express.tsx similarity index 83% rename from starters/servers/express/src/entry.express.tsx rename to starters/adaptors/express/src/entry.express.tsx index 043fc9940ed..6c98b733a96 100644 --- a/starters/servers/express/src/entry.express.tsx +++ b/starters/adaptors/express/src/entry.express.tsx @@ -1,15 +1,16 @@ -import { qwikCity } from '@builder.io/qwik-city/middleware/node'; +import { createQwikCity } from '@builder.io/qwik-city/middleware/node'; +import qwikCityPlan from '@qwik-city-plan'; +import render from './entry.ssr'; import express from 'express'; import { fileURLToPath } from 'url'; import { join } from 'path'; -import render from './entry.ssr'; // Directories where the static assets are located const distDir = join(fileURLToPath(import.meta.url), '..', '..', 'dist'); const buildDir = join(distDir, 'build'); // Create the Qwik City express middleware -const { router, notFound } = qwikCity(render); +const { router, notFound } = createQwikCity({ render, qwikCityPlan }); // Allow for dynamic port const PORT = process.env.PORT ?? 3000; // Create the express server diff --git a/starters/servers/netlify-edge/.node-version b/starters/adaptors/netlify-edge/.node-version similarity index 100% rename from starters/servers/netlify-edge/.node-version rename to starters/adaptors/netlify-edge/.node-version diff --git a/starters/servers/netlify-edge/README.md b/starters/adaptors/netlify-edge/README.md similarity index 100% rename from starters/servers/netlify-edge/README.md rename to starters/adaptors/netlify-edge/README.md diff --git a/starters/adaptors/netlify-edge/adaptors/netlify-edge/vite.config.ts b/starters/adaptors/netlify-edge/adaptors/netlify-edge/vite.config.ts new file mode 100644 index 00000000000..29ff725aa49 --- /dev/null +++ b/starters/adaptors/netlify-edge/adaptors/netlify-edge/vite.config.ts @@ -0,0 +1,26 @@ +import netlifyEdge from '@netlify/vite-plugin-netlify-edge'; +import { netifyEdgeAdaptor } from '@builder.io/qwik-city/adaptors/netlify-edge/vite'; +import { extendConfig } from '@builder.io/qwik-city/vite'; +import baseConfig from '../../vite.config'; + +export default extendConfig(baseConfig, () => { + return { + build: { + ssr: true, + rollupOptions: { + input: ['src/entry.netlify-edge.tsx', 'src/entry.ssr.tsx', '@qwik-city-plan'], + }, + outDir: 'netlify/edge-functions/entry.netlify-edge', + }, + ssr: { + target: 'webworker', + noExternal: true, + }, + plugins: [ + netifyEdgeAdaptor({ + staticGenerate: true, + }), + netlifyEdge({ functionName: 'entry.netlify-edge' }), + ], + }; +}); diff --git a/starters/servers/netlify-edge/netlify.toml b/starters/adaptors/netlify-edge/netlify.toml similarity index 100% rename from starters/servers/netlify-edge/netlify.toml rename to starters/adaptors/netlify-edge/netlify.toml diff --git a/starters/adaptors/netlify-edge/package.json b/starters/adaptors/netlify-edge/package.json new file mode 100644 index 00000000000..835311a3d0c --- /dev/null +++ b/starters/adaptors/netlify-edge/package.json @@ -0,0 +1,13 @@ +{ + "description": "Netlify Edge Functions", + "scripts": { + "build.server": "vite build -c adaptors/netlify-edge/vite.config.ts", + "serve": "netlify dev" + }, + "devDependencies": { + "@netlify/vite-plugin-netlify-edge": "1.1.0" + }, + "__qwik__": { + "priority": -1 + } +} diff --git a/starters/servers/cloudflare-pages/public/_headers b/starters/adaptors/netlify-edge/public/_headers similarity index 100% rename from starters/servers/cloudflare-pages/public/_headers rename to starters/adaptors/netlify-edge/public/_headers diff --git a/starters/adaptors/netlify-edge/src/entry.netlify-edge.tsx b/starters/adaptors/netlify-edge/src/entry.netlify-edge.tsx new file mode 100644 index 00000000000..5758fd5c615 --- /dev/null +++ b/starters/adaptors/netlify-edge/src/entry.netlify-edge.tsx @@ -0,0 +1,5 @@ +import { createQwikCity } from '@builder.io/qwik-city/middleware/netlify-edge'; +import qwikCityPlan from '@qwik-city-plan'; +import render from './entry.ssr'; + +export default createQwikCity({ render, qwikCityPlan }); diff --git a/starters/static-generators/static-node/README.md b/starters/adaptors/static/README.md similarity index 100% rename from starters/static-generators/static-node/README.md rename to starters/adaptors/static/README.md diff --git a/starters/adaptors/static/adaptors/static/vite.config.ts b/starters/adaptors/static/adaptors/static/vite.config.ts new file mode 100644 index 00000000000..d22dffef151 --- /dev/null +++ b/starters/adaptors/static/adaptors/static/vite.config.ts @@ -0,0 +1,20 @@ +import { staticAdaptor } from '@builder.io/qwik-city/adaptors/static/vite'; +import { extendConfig } from '@builder.io/qwik-city/vite'; +import baseConfig from '../../vite.config'; + +export default extendConfig(baseConfig, () => { + return { + build: { + ssr: true, + rollupOptions: { + input: ['src/entry.ssr.tsx', '@qwik-city-plan'], + }, + }, + ssr: {}, + plugins: [ + staticAdaptor({ + origin: 'https://yoursite.qwik.dev', + }), + ], + }; +}); diff --git a/starters/adaptors/static/package.json b/starters/adaptors/static/package.json new file mode 100644 index 00000000000..3fabf110de1 --- /dev/null +++ b/starters/adaptors/static/package.json @@ -0,0 +1,9 @@ +{ + "description": "Static Site Generator", + "scripts": { + "build.static": "vite build -c adaptors/static/vite.config.ts" + }, + "__qwik__": { + "priority": -1 + } +} diff --git a/starters/adaptors/static/src/entry.static.tsx b/starters/adaptors/static/src/entry.static.tsx new file mode 100644 index 00000000000..b781af625e3 --- /dev/null +++ b/starters/adaptors/static/src/entry.static.tsx @@ -0,0 +1,5 @@ +import { generate } from '@builder.io/qwik-city/static'; +import qwikCityPlan from '@qwik-city-plan'; +import render from './entry.ssr'; + +generate({ render, qwikCityPlan }); diff --git a/starters/servers/cloudflare-pages/package.json b/starters/servers/cloudflare-pages/package.json deleted file mode 100644 index 9c2566e04ab..00000000000 --- a/starters/servers/cloudflare-pages/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "description": "Cloudflare Pages", - "scripts": { - "build.server": "vite build --ssr src/entry.cloudflare-pages.tsx", - "serve": "wrangler pages dev ./dist" - }, - "devDependencies": { - "wrangler": "beta" - }, - "__qwik__": { - "priority": -1, - "viteConfig": { - "viteConfig": { - "ssr": "{ target: 'webworker', noExternal: true }" - } - } - } -} diff --git a/starters/servers/cloudflare-pages/src/entry.cloudflare-pages.tsx b/starters/servers/cloudflare-pages/src/entry.cloudflare-pages.tsx deleted file mode 100644 index 75ea6b61a86..00000000000 --- a/starters/servers/cloudflare-pages/src/entry.cloudflare-pages.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { qwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'; -import render from './entry.ssr'; - -export const onRequest = qwikCity(render); diff --git a/starters/servers/netlify-edge/package.json b/starters/servers/netlify-edge/package.json deleted file mode 100644 index 4cb98b918dd..00000000000 --- a/starters/servers/netlify-edge/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "description": "Netlify Edge Functions", - "scripts": { - "build.server": "vite build --ssr src/entry.netlify-edge.tsx", - "serve": "netlify dev" - }, - "devDependencies": { - "@netlify/vite-plugin-netlify-edge": "1.1.0" - }, - "__qwik__": { - "priority": -1, - "viteConfig": { - "imports": [ - { - "defaultImport": "netlifyEdge", - "importPath": "@netlify/vite-plugin-netlify-edge" - } - ], - "qwikViteConfig": { - "ssr": "{ outDir: 'netlify/edge-functions/entry.netlify-edge' }" - }, - "vitePlugins": [ - "netlifyEdge({ functionName: 'entry.netlify-edge' })" - ] - } - } -} diff --git a/starters/servers/netlify-edge/src/entry.netlify-edge.tsx b/starters/servers/netlify-edge/src/entry.netlify-edge.tsx deleted file mode 100644 index 8f2e64b0c72..00000000000 --- a/starters/servers/netlify-edge/src/entry.netlify-edge.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { qwikCity } from '@builder.io/qwik-city/middleware/netlify-edge'; -import render from './entry.ssr'; - -const qwikCityHandler = qwikCity(render); - -export default qwikCityHandler; diff --git a/starters/static-generators/static-node/package.json b/starters/static-generators/static-node/package.json deleted file mode 100644 index ce69b11d876..00000000000 --- a/starters/static-generators/static-node/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "description": "Static Site Generator (Node.js)", - "scripts": { - "build.static": "vite build --ssr src/entry.static.tsx", - "ssg": "node server/entry.static" - }, - "__qwik__": { - "priority": -1, - "viteConfig": { - "viteConfig": { - "ssr": "{ target: 'node', format: 'cjs' }" - } - } - } -} diff --git a/starters/static-generators/static-node/src/entry.static.tsx b/starters/static-generators/static-node/src/entry.static.tsx deleted file mode 100644 index 563093fe331..00000000000 --- a/starters/static-generators/static-node/src/entry.static.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { qwikCityGenerate } from '@builder.io/qwik-city/static/node'; -import render from './entry.ssr'; -import { fileURLToPath } from 'url'; -import { join } from 'path'; - -// Execute Qwik City Static Site Generator -qwikCityGenerate(render, { - origin: 'https://qwik.builder.io', - outDir: join(fileURLToPath(import.meta.url), '..', '..', 'dist'), -}); diff --git a/tsconfig.json b/tsconfig.json index c20e49e137b..84314fa803b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,14 +33,23 @@ "@builder.io/qwik-city/service-worker": [ "packages/qwik-city/runtime/src/library/service-worker/index.ts" ], - "@builder.io/qwik-city/middleware/cloudflare-pages": [ - "packages/qwik-city/middleware/cloudflare-pages" + "@builder.io/qwik-city/adaptors/cloudflare-pages/vite": [ + "packages/qwik-city/adaptors/cloudflare-pages/vite/index.ts" + ], + "@builder.io/qwik-city/adaptors/express/vite": [ + "packages/qwik-city/adaptors/express/vite/index.ts" + ], + "@builder.io/qwik-city/adaptors/netlify-edge/vite": [ + "packages/qwik-city/adaptors/netlify-edge/vite/index.ts" + ], + "@builder.io/qwik-city/adaptors/static/vite": [ + "packages/qwik-city/adaptors/static/vite/index.ts" ], "@builder.io/qwik-city/middleware/node": ["packages/qwik-city/middleware/node"], "@builder.io/qwik-city/middleware/netlify-edge": [ "packages/qwik-city/middleware/netlify-edge" ], - "@builder.io/qwik-city/static/node": ["packages/qwik-city/static/node"], + "@builder.io/qwik-city/static": ["packages/qwik-city/static/index.ts"], "@builder.io/qwik-city/vite": ["packages/qwik-city/buildtime/vite/index"], "@examples-data": ["packages/docs/src/routes/examples/apps/examples-data.ts"], "@playground-data": ["packages/docs/src/routes/playground/playground-data.ts"], @@ -63,7 +72,7 @@ }, "include": [ "packages/create-qwik", - "packages/docs", + // "packages/docs", "packages/qwik/src", "packages/qwik-city", "packages/qwik-react/src",