diff --git a/.changeset/nine-jobs-sip.md b/.changeset/nine-jobs-sip.md new file mode 100644 index 00000000..741d0fec --- /dev/null +++ b/.changeset/nine-jobs-sip.md @@ -0,0 +1,5 @@ +--- +"@qwikdev/astro": patch +--- + +fix: improved manifest handling for deployment providers using older node versions diff --git a/libs/qwikdev-astro/package.json b/libs/qwikdev-astro/package.json index aa58031a..15441c90 100644 --- a/libs/qwikdev-astro/package.json +++ b/libs/qwikdev-astro/package.json @@ -61,6 +61,7 @@ }, "bugs": "/~https://github.com/thejackshelton/@qwikdev/astro/issues", "dependencies": { + "@inox-tools/inline-mod": "^2.0.2", "astro-integration-kit": "^0.18.0" }, "devDependencies": { diff --git a/libs/qwikdev-astro/q-astro-manifest.json b/libs/qwikdev-astro/q-astro-manifest.json index f00f6d21..f4b092d3 100644 --- a/libs/qwikdev-astro/q-astro-manifest.json +++ b/libs/qwikdev-astro/q-astro-manifest.json @@ -1 +1 @@ -{"manifestHash":"dxopp8","symbols":{"s_Jkq4JnSu3vc":{"origin":"components/qwik/counter.tsx","displayName":"counter.tsx_s","canonicalFilename":"counter.tsx_s_Jkq4JnSu3vc","hash":"Jkq4JnSu3vc","ctxKind":"function","ctxName":"component$","captures":false,"loc":[929,942]},"s_o0dE1oWgGK4":{"origin":"../../../libs/qwikdev-astro/src/root.tsx","displayName":"root.tsx_root_component","canonicalFilename":"root.tsx_root_component_o0dE1oWgGK4","hash":"o0dE1oWgGK4","ctxKind":"function","ctxName":"component$","captures":false,"loc":[81,136]},"s_WTj7Z5OCKjk":{"origin":"components/qwik/counter.tsx","displayName":"counter.tsx_s","canonicalFilename":"counter.tsx_s_WTj7Z5OCKjk","hash":"WTj7Z5OCKjk","ctxKind":"function","ctxName":"_jsxQ","captures":true,"loc":[626,639]}},"mapping":{"s_Jkq4JnSu3vc":"q-CbgFd24z.js","s_o0dE1oWgGK4":"q-BVmDhJRY.js","s_WTj7Z5OCKjk":"q-BUgo_UOC.js"},"bundles":{"q-BUgo_UOC.js":{"size":93,"imports":["q-BVmDhJRY.js","q-CbgFd24z.js"],"origins":["src/components/qwik/counter.tsx_s_WTj7Z5OCKjk.js"],"symbols":["s_WTj7Z5OCKjk"]},"q-BVmDhJRY.js":{"size":61649,"origins":["../../libs/qwikdev-astro/src/root.tsx_root_component_o0dE1oWgGK4.js","../../node_modules/.pnpm/@builder.io+qwik@1.12.0_vite@6.0.6_@types+node@22.10.2_jiti@2.4.2_yaml@2.6.1_/node_modules/@builder.io/qwik/dist/core.prod.mjs","@builder.io/qwik/build"],"symbols":["s_o0dE1oWgGK4"]},"q-BZLo5_WJ.js":{"size":146,"imports":["q-BVmDhJRY.js","q-CbgFd24z.js"]},"q-C3ZwI676.js":{"size":184,"imports":["q-BVmDhJRY.js","q-CbgFd24z.js"],"dynamicImports":["q-BVmDhJRY.js"],"origins":["../../libs/qwikdev-astro/src/root.tsx"]},"q-CbgFd24z.js":{"size":1694,"imports":["q-BVmDhJRY.js"],"dynamicImports":["q-BUgo_UOC.js"],"origins":["src/components/qwik/counter.tsx","src/components/qwik/counter.tsx_s_Jkq4JnSu3vc.js"],"symbols":["s_Jkq4JnSu3vc"]}},"injections":[],"version":"1","options":{"target":"client","buildMode":"production","entryStrategy":{"type":"smart"}},"platform":{"qwik":"1.12.0","vite":"","rollup":"4.29.1","env":"node","os":"darwin","node":"22.12.0"}} \ No newline at end of file +{"manifestHash":"ksjrps","symbols":{"s_0DHRa0FqCj4":{"origin":"../../../libs/qwikdev-astro/src/root.tsx","displayName":"root.tsx_root_component","canonicalFilename":"root.tsx_root_component_0DHRa0FqCj4","hash":"0DHRa0FqCj4","ctxKind":"function","ctxName":"component$","captures":false,"loc":[81,136]},"s_qY02pQHKSts":{"origin":"components/qwik/counter.tsx","displayName":"counter.tsx_Counter_component","canonicalFilename":"counter.tsx_Counter_component_qY02pQHKSts","hash":"qY02pQHKSts","ctxKind":"function","ctxName":"component$","captures":false,"loc":[121,322]},"s_WDqUvWziK5k":{"origin":"components/qwik/counter.tsx","displayName":"counter.tsx_Counter_component_Fragment_button_onClick","canonicalFilename":"counter.tsx_Counter_component_Fragment_button_onClick_WDqUvWziK5k","hash":"WDqUvWziK5k","ctxKind":"eventHandler","ctxName":"onClick$","captures":true,"parent":"s_qY02pQHKSts","loc":[235,256]}},"mapping":{"s_0DHRa0FqCj4":"q-CXpka_yH.js","s_qY02pQHKSts":"q-4LT-psvh.js","s_WDqUvWziK5k":"q-Cf-M9i3S.js"},"bundles":{"q-4LT-psvh.js":{"size":88,"imports":["q-Cf-M9i3S.js","q-CXpka_yH.js"],"symbols":["s_qY02pQHKSts"]},"q-Cf-M9i3S.js":{"size":1635,"imports":["q-CXpka_yH.js"],"origins":["src/components/qwik/counter.tsx_Counter_component_Fragment_button_onClick_WDqUvWziK5k.js","src/components/qwik/counter.tsx_Counter_component_qY02pQHKSts.js"],"symbols":["s_WDqUvWziK5k"]},"q-CXpka_yH.js":{"size":61657,"origins":["../../libs/qwikdev-astro/src/root.tsx_root_component_0DHRa0FqCj4.js","../../node_modules/.pnpm/@builder.io+qwik@1.12.0_vite@6.0.6_@types+node@22.10.2_jiti@2.4.2_yaml@2.6.1_/node_modules/@builder.io/qwik/dist/core.prod.mjs","@builder.io/qwik/build"],"symbols":["s_0DHRa0FqCj4"]},"q-FfniopH8.js":{"size":184,"imports":["q-Cf-M9i3S.js","q-CXpka_yH.js"],"dynamicImports":["q-CXpka_yH.js"],"origins":["../../libs/qwikdev-astro/src/root.tsx"]},"q-fzuY5JUw.js":{"size":171,"imports":["q-Cf-M9i3S.js","q-CXpka_yH.js"],"dynamicImports":["q-4LT-psvh.js"],"origins":["src/components/qwik/counter.tsx"]}},"injections":[],"version":"1","options":{"target":"client","buildMode":"production","entryStrategy":{"type":"smart"}},"platform":{"qwik":"1.12.0","vite":"","rollup":"4.29.1","env":"node","os":"darwin","node":"22.12.0"}} \ No newline at end of file diff --git a/libs/qwikdev-astro/server.ts b/libs/qwikdev-astro/server.ts index ac609417..b1470558 100644 --- a/libs/qwikdev-astro/server.ts +++ b/libs/qwikdev-astro/server.ts @@ -1,5 +1,4 @@ -import type { SSRResult } from "astro"; - +import { isNode, qAstroManifestPath } from "inox:inline-mod:mod_0"; import { type JSXNode, jsx } from "@builder.io/qwik"; import { isDev } from "@builder.io/qwik/build"; import type { QwikManifest } from "@builder.io/qwik/optimizer"; @@ -8,6 +7,7 @@ import { getQwikLoaderScript, renderToStream } from "@builder.io/qwik/server"; +import type { SSRResult } from "astro"; const isQwikLoaderAddedMap = new WeakMap(); const modulePreloadScript = `window.addEventListener("load",()=>{(async()=>{window.requestIdleCallback||(window.requestIdleCallback=(e,t)=>{const n=t||{},o=1,i=n.timeout||o,a=performance.now();return setTimeout(()=>{e({get didTimeout(){return!n.timeout&&performance.now()-a-o>i},timeRemaining:()=>Math.max(0,o+(performance.now()-a))})},o)});const e=async()=>{const e=new Set,t=document.querySelectorAll('script[q\\\\:type="prefetch-bundles"]');t.forEach(t=>{if(!t.textContent)return;const n=t.textContent,o=n.match(/\\["prefetch","[/]build[/]","(.*?)"\\]/);o&&o[1]&&o[1].split('","').forEach(t=>{t.startsWith("q-")&&e.add(t)})}),document.querySelectorAll('script[type="qwik/json"]').forEach(t=>{if(!t.textContent)return;const n=t.textContent.match(/q-[A-Za-z0-9_-]+\\.js/g);n&&n.forEach(t=>e.add(t))}),e.forEach(e=>{const t=document.createElement("link");t.rel="modulepreload",t.href="/build/"+e,t.fetchPriority="low",document.head.appendChild(t)})};await requestIdleCallback(await e)})()});`; @@ -63,25 +63,37 @@ export async function renderToStaticMarkup( let html = ""; - // Get the manifest from the integration directory - const qwikRenderer = this.result.renderers.find( - (r) => r.name === "@qwikdev/astro" - ) as any; - - const manifestPath = qwikRenderer?.serverEntrypoint?.replace( - "server.ts", - "q-astro-manifest.json" - ); - let integrationManifest = null; - if (manifestPath) { + + /** + * fallback to dynamic import if node is false. Node is preferred because most deployment providers still use older versions of node by default, so dynamic json imports will fail. + * + * Until this improves, we'll use node's readFileSync, with dynamic json imports for those not using node. + */ + if (isNode) { + try { + const { readFileSync } = await import("node:fs"); + const manifestContent = readFileSync(qAstroManifestPath, "utf-8"); + integrationManifest = JSON.parse(manifestContent); + } catch (error) { + throw new Error( + `@qwikdev/astro: Failed to read the q-astro-manifest.json file. This file is required for the @qwikdev/astro integration to work. + + It seems like you're using node. If this is not the case, please set the isNode option to false in the integration options in astro.config.mjs. + + Also make sure this is the case with both your local and deployed environment.` + ); + } + } else { try { - integrationManifest = await import(/* @vite-ignore */ manifestPath, { + integrationManifest = await import(/* @vite-ignore */ qAstroManifestPath, { with: { type: "json" } }); } catch (error) { throw new Error( - `@qwikdev/astro: This integration requires Node version 22 or higher. If this is local, check your node version with node -v. If this is a deployment, check your deployment provider's environment variables.` + `@qwikdev/astro: Failed to read the q-astro-manifest.json file. This file is required for the @qwikdev/astro integration to work. + + Because isNode is set to false, the integration will use dynamic json imports to read the q-astro-manifest.json file. Check to make sure this environment supports dynamic json imports.` ); } } @@ -95,7 +107,8 @@ export async function renderToStaticMarkup( symbolMapper: globalThis.symbolMapperFn } : { - manifest: globalThis.qManifest || integrationManifest?.default + manifest: + globalThis.qManifest || integrationManifest?.default || integrationManifest }), serverData: props, qwikPrefetchServiceWorker: { @@ -158,7 +171,7 @@ export async function renderToStaticMarkup( dangerouslySetInnerHTML: String(value), style: "display: contents", ...namedSlot, - "q:key": globalThis.hash + "q:key": Math.random().toString(26).split(".").pop() }); if (key === "default") { @@ -213,5 +226,6 @@ export async function renderToStaticMarkup( export default { renderToStaticMarkup, supportsAstroStaticSlot: true, + testing123: true, check }; diff --git a/libs/qwikdev-astro/src/index.ts b/libs/qwikdev-astro/src/index.ts index 33aaaf16..b547dc19 100644 --- a/libs/qwikdev-astro/src/index.ts +++ b/libs/qwikdev-astro/src/index.ts @@ -1,11 +1,12 @@ import { writeFileSync } from "node:fs"; -import path from "node:path"; import { qwikVite, symbolMapper } from "@builder.io/qwik/optimizer"; import type { QwikManifest, QwikVitePluginOptions, SymbolMapperFn } from "@builder.io/qwik/optimizer"; +import { inlineModule } from "@inox-tools/inline-mod/vite"; +import inlineMod from "@inox-tools/inline-mod/vite"; import type { AstroConfig, AstroIntegration } from "astro"; import { createResolver, defineIntegration, watchDirectory } from "astro-integration-kit"; import { z } from "astro/zod"; @@ -14,7 +15,6 @@ import type { InlineConfig } from "vite"; declare global { var symbolMapperFn: SymbolMapperFn; - var hash: string | undefined; var relativeClientPath: string; var qManifest: QwikManifest; } @@ -48,7 +48,12 @@ export default defineIntegration({ /** * Enable debug mode with the qwikVite plugin. */ - debug: z.boolean().optional() + debug: z.boolean().optional(), + + /** + * Use node's readFileSync to read the manifest. Common for deployment providers that don't support dynamic json imports. When false, please ensure your deployment provider supports dynamic json imports, through environment variables or other means. + */ + isNode: z.boolean().optional().default(true) }) .optional(), @@ -70,19 +75,24 @@ export default defineIntegration({ let astroConfig: AstroConfig | null = null; const { resolve: resolver } = createResolver(import.meta.url); const filter = createFilter(options?.include, options?.exclude); + const qAstroManifestPath = resolver("../q-astro-manifest.json"); const lifecycleHooks: AstroIntegration["hooks"] = { "astro:config:setup": async (setupProps) => { const { addRenderer, updateConfig, config, command } = setupProps; astroConfig = config; + // passes config values to other runtimes with a virtual module + inlineModule({ + constExports: { + isNode: options?.isNode ?? true, + qAstroManifestPath + } + }); + /* q-astro-manifest.json doesn't error in dev */ if (command === "dev") { - writeFileSync( - path.join(resolver("../"), "q-astro-manifest.json"), - "{}", - "utf-8" - ); + writeFileSync(qAstroManifestPath, "{}", "utf-8"); } // integration HMR support @@ -225,7 +235,12 @@ export default defineIntegration({ } } }, - plugins: [astroQwikPlugin, qwikVite(qwikSetupConfig), overrideEsbuildPlugin] + plugins: [ + inlineMod(), + astroQwikPlugin, + qwikVite(qwikSetupConfig), + overrideEsbuildPlugin + ] } }); }, @@ -254,11 +269,7 @@ export default defineIntegration({ outDir: finalDir, manifestOutput: (manifest) => { globalThis.qManifest = manifest; - writeFileSync( - path.join(resolver("../"), "q-astro-manifest.json"), - JSON.stringify(manifest), - "utf-8" - ); + writeFileSync(qAstroManifestPath, JSON.stringify(manifest), "utf-8"); } }, debug: options?.debug ?? false diff --git a/libs/qwikdev-astro/src/utils.ts b/libs/qwikdev-astro/src/utils.ts deleted file mode 100644 index 289277da..00000000 --- a/libs/qwikdev-astro/src/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function newHash() { - const hash = Math.random().toString(26).split(".").pop(); - globalThis.hash = hash; - return hash; -} diff --git a/libs/qwikdev-astro/src/virtual.d.ts b/libs/qwikdev-astro/src/virtual.d.ts new file mode 100644 index 00000000..8d74bd10 --- /dev/null +++ b/libs/qwikdev-astro/src/virtual.d.ts @@ -0,0 +1,4 @@ +declare module "inox:inline-mod:mod_0" { + export const isNode: boolean; + export const qAstroManifestPath: string; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7893163c..260036ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,6 +201,9 @@ importers: libs/qwikdev-astro: dependencies: + '@inox-tools/inline-mod': + specifier: ^2.0.2 + version: 2.0.2(vite@5.4.11(@types/node@22.10.2)) astro-integration-kit: specifier: ^0.18.0 version: 0.18.0(astro@5.1.1(@types/node@22.10.2)(jiti@2.4.2)(rollup@4.29.1)(typescript@5.7.2)(yaml@2.6.1)) @@ -1166,6 +1169,14 @@ packages: '@inox-tools/astro-tests@0.2.1': resolution: {integrity: sha512-Yg7ZuCw58mLwjscgLjgNdNgpY+n1gpLcpAWo2nLZQW61QjT6msBdLGM4GCgUDb0fBkeDhe332c/bTkVYLoc58A==} + '@inox-tools/inline-mod@2.0.2': + resolution: {integrity: sha512-TLzqeZ0/IBaszyhylg2Q4J6+5S704YpWKUrWvoCvkCnOxH0j7EcFWBphB4TCfIZnsXhswuhZkUTRWwdvaArT0w==} + peerDependencies: + vite: ^4 || ^5 || ^6 + peerDependenciesMeta: + vite: + optional: true + '@inox-tools/utils@0.3.0': resolution: {integrity: sha512-2Brt/vpn3Likf0L7qydvxoBw5yZFBrqO4tF5HhB1fMC4DYsgk+lQn4pB7RBdqVFqy52GjzXj7j2i5EFh1I/EPw==} @@ -6031,6 +6042,16 @@ snapshots: - typescript - yaml + '@inox-tools/inline-mod@2.0.2(vite@5.4.11(@types/node@22.10.2))': + dependencies: + '@inox-tools/utils': 0.3.0 + debug: 4.4.0 + typescript: 5.7.2 + optionalDependencies: + vite: 5.4.11(@types/node@22.10.2) + transitivePeerDependencies: + - supports-color + '@inox-tools/utils@0.3.0': {} '@isaacs/cliui@8.0.2':