| 1 | /** |
| 2 | * The Astro-specific deploy-target contract. |
| 3 | * |
| 4 | * framework-core's generic `DeployTarget` covers the platform-neutral seams |
| 5 | * (`bundle`, `build`, `finish`, `serve`, opaque `config`). Astro needs one |
| 6 | * framework-specific hook on top: the Astro *integration* (the adapter) that |
| 7 | * gets pinned into the in-memory `AstroInlineConfig` for both `dev` and |
| 8 | * `build`. Everything platform-specific — which vite plugins the adapter |
| 9 | * injects, which server entrypoint it selects, how dev SSR executes — lives |
| 10 | * inside that integration, so the framework half of this package never has to |
| 11 | * know the platform. |
| 12 | * |
| 13 | * The Cloudflare implementation ships at `@alchemy.run/frontend-frameworks/astro/cloudflare`; |
| 14 | * a future platform (AWS, ...) implements the same interface as a new subpath |
| 15 | * without any change to the framework module. |
| 16 | */ |
| 17 | import { |
| 18 | isDeployTarget, |
| 19 | type BuildOutput, |
| 20 | type DeployTarget, |
| 21 | type DeployTargetBuildContext, |
| 22 | type DeployTargetError, |
| 23 | type DeployTargetInput, |
| 24 | type DeployTargetServices, |
| 25 | type ServerEntryChunk, |
| 26 | } from "../core/index.ts"; |
| 27 | import type { AstroInlineConfig, AstroIntegration } from "astro"; |
| 28 | import type * as Effect from "effect/Effect"; |
| 29 | |
| 30 | /** |
| 31 | * The context an Astro target's wholesale `build` takeover receives: the |
| 32 | * generic build context plus the inline Astro overlay the framework was |
| 33 | * constructed with (`AstroFrameworkOptions.astro`) — carried so wholesale |
| 34 | * targets that re-run the framework in a child process (the AWS target) |
| 35 | * can reconstruct it. Only JSON-serializable fields survive that process |
| 36 | * boundary. |
| 37 | */ |
| 38 | export interface AstroTargetBuildContext extends DeployTargetBuildContext { |
| 39 | readonly astro?: AstroInlineConfig | undefined; |
| 40 | } |
| 41 | |
| 42 | export interface AstroTarget<Config = unknown> extends DeployTarget<Config> { |
| 43 | /** |
| 44 | * Build the Astro adapter integration pinned into the inline config |
| 45 | * (`AstroInlineConfig.adapter`). Called once per `dev`/`build` operation. |
| 46 | */ |
| 47 | readonly integration: () => AstroIntegration; |
| 48 | /** |
| 49 | * Optional *wholesale* build takeover (see `DeployTarget.build`), with |
| 50 | * the Astro-specific {@link AstroTargetBuildContext}. |
| 51 | */ |
| 52 | readonly build?: |
| 53 | | (( |
| 54 | context: AstroTargetBuildContext, |
| 55 | ) => Effect.Effect<BuildOutput, DeployTargetError, DeployTargetServices>) |
| 56 | | undefined; |
| 57 | /** |
| 58 | * Optional entry-chunk pin for the build-output collector: astro's page |
| 59 | * modules are rolldown inputs of the `ssr` environment, so it emits many |
| 60 | * entry chunks. A target whose adapter entry is not otherwise |
| 61 | * identifiable (e.g. the AWS target's `serverEntrypoint`, which astro |
| 62 | * renames to `config.build.serverEntry`) selects the chunk that becomes |
| 63 | * `serverModules[0]` here. The Cloudflare target does not need it — its |
| 64 | * vite plugin wraps the worker entry with a recognizable facade. |
| 65 | */ |
| 66 | readonly selectServerEntry?: |
| 67 | | ((chunk: ServerEntryChunk) => boolean) |
| 68 | | undefined; |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * How a target reaches the Astro framework module: a target value, a |
| 73 | * `(config) => AstroTarget` factory, or a module specifier resolved from the |
| 74 | * *project's* `node_modules` (whose `default` — or named `target` — export is |
| 75 | * the value or factory). |
| 76 | */ |
| 77 | export type AstroTargetInput<Config = unknown> = DeployTargetInput< |
| 78 | AstroTarget<Config>, |
| 79 | Config |
| 80 | >; |
| 81 | |
| 82 | /** |
| 83 | * The default target module: the Cloudflare implementation shipped by this |
| 84 | * package, resolved from the project's own dependency tree. |
| 85 | */ |
| 86 | export const DEFAULT_TARGET_SPECIFIER = |
| 87 | "@alchemy.run/frontend-frameworks/astro/cloudflare"; |
| 88 | |
| 89 | /** Structural guard: a `DeployTarget` carrying the Astro `integration` hook. */ |
| 90 | export const isAstroTarget = (value: unknown): value is AstroTarget => |
| 91 | isDeployTarget(value) && |
| 92 | typeof (value as { integration?: unknown }).integration === "function"; |