-
Notifications
You must be signed in to change notification settings - Fork 642
Pro 9551 apos astro v2 #5441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pro 9551 apos astro v2 #5441
Changes from 9 commits
6b21531
9048c2e
f1333cf
a33b84a
a70b170
83272f4
e9d94cd
99644c0
ffaecff
ea8f042
df33277
2ab77f9
93157d9
564aabb
32f7a9a
ffdffdc
c21d5fb
62d710d
f1dd138
3bc03d8
8899103
4e1be7a
ff6cbb6
043c215
6fba442
a6daca5
227b638
6e6b98c
6d48f1f
20a29ea
736d6c4
5fda79e
5094022
7a29be1
56a844a
1937537
57a01ab
334dc61
91ecf47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| "@apostrophecms/apostrophe-astro": major | ||
| --- | ||
|
|
||
| - Replace vite-plugin-apostrophe-config and vite-plugin-apostrophe-doctype with | ||
| vite/vite-plugin-apostrophe-generated-config.js, which writes real files to | ||
| node_modules/.apostrophe-astro-config/ (config.js, doctypes.js) | ||
| - Register Vite aliases for apostrophe-astro-config/config and /doctypes | ||
| - Update all 10 internal virtual: imports to alias specifiers | ||
| - Rename static build cache dir to node_modules/.apostrophe-astro-static/ | ||
| - Add helpers/server/ (aposFetch, aposPageFetch, getAposHost, isStaticBuild) | ||
| - Add helpers/universal/ (URL, slug, styles, attachment helpers) | ||
| - Add helpers/client/index.js (reserved) | ||
| - Reduce lib/aposPageFetch.js, lib/util.js, lib/aposSetQueryParameter.js to deprecated shims | ||
| - Add package.json exports map; bump version to 2.0.0 | ||
| - Add MIGRATION.md |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| # Migrating to @apostrophecms/apostrophe-astro v2 | ||
|
|
||
| ## Overview | ||
|
|
||
| v2 ships two coordinated breaking changes: | ||
|
|
||
| 1. **Generated runtime files** replace Vite virtual modules for Astro 6 / Vite 7 compatibility. | ||
| 2. **Public helper import paths** are formalized. `helpers/server`, `helpers/universal`, and `helpers/client` are the new stable entry points. `lib/` is now internal. | ||
|
|
||
| Most projects need only the import-path changes described below. Integration options, component paths, and injected routes are unchanged. | ||
|
|
||
| --- | ||
|
|
||
| ## What stays the same | ||
|
|
||
| - `apostropheIntegration()` options (`aposHost`, `widgetsMapping`, `templatesMapping`, `onBeforeWidgetRender`, `staticBuild`, etc.) are unchanged. | ||
| - Component import paths (`@apostrophecms/apostrophe-astro/components/*`, `.../components/layouts/*`, `.../widgets/*`) are unchanged. | ||
| - Injected routes (`/apos-frontend/[...slug]`, `/api/v1/[...slug]`, etc.) are unchanged. | ||
|
|
||
| --- | ||
|
|
||
| ## Required changes | ||
|
|
||
| ### 1. Update `lib/aposPageFetch.js` imports | ||
|
|
||
| ```js | ||
| // Before | ||
| import aposPageFetch from '@apostrophecms/apostrophe-astro/lib/aposPageFetch.js'; | ||
|
|
||
| // After | ||
| import { aposPageFetch } from '@apostrophecms/apostrophe-astro/helpers/server'; | ||
| ``` | ||
|
|
||
| ### 2. Update `lib/aposSetQueryParameter.js` imports | ||
|
|
||
| ```js | ||
| // Before | ||
| import setParameter from '@apostrophecms/apostrophe-astro/lib/aposSetQueryParameter.js'; | ||
| // or | ||
| import setParameter from '@apostrophecms/apostrophe-astro/lib/aposSetQueryParameter'; | ||
|
|
||
| // After | ||
| import { aposSetQueryParameter } from '@apostrophecms/apostrophe-astro/helpers/universal'; | ||
| ``` | ||
|
|
||
| ### 3. Update `lib/util` imports | ||
|
|
||
| ```js | ||
| // Before | ||
| import { slugify } from '@apostrophecms/apostrophe-astro/lib/util'; | ||
| // or | ||
| import { slugify } from '@apostrophecms/apostrophe-astro/lib/util.js'; | ||
|
|
||
| // After | ||
| import { slugify } from '@apostrophecms/apostrophe-astro/helpers/universal'; | ||
| ``` | ||
|
|
||
| ### 4. Update `lib/static.js` imports | ||
|
|
||
| ```js | ||
| // Before | ||
| import { getAllStaticPaths } from '@apostrophecms/apostrophe-astro/lib/static.js'; | ||
|
|
||
| // After | ||
| import { getAllStaticPaths } from '@apostrophecms/apostrophe-astro/helpers/server'; | ||
| ``` | ||
|
|
||
| ### 5. Update `lib/aposStyles.js` and `lib/attachment.js` imports | ||
|
|
||
| These files are no longer part of the public API. | ||
|
|
||
| ```js | ||
| // Before | ||
| import { stylesAttributes } from '@apostrophecms/apostrophe-astro/lib/aposStyles.js'; | ||
| import { getAttachmentUrl } from '@apostrophecms/apostrophe-astro/lib/attachment.js'; | ||
|
|
||
| // After | ||
| import { stylesAttributes, getAttachmentUrl } from '@apostrophecms/apostrophe-astro/helpers/universal'; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Deprecated shims (v2) | ||
|
|
||
| The following `lib/` paths remain exported in v2 as compatibility shims. They continue to work, but new and migrated code should use the helper entry points. Migrate before v3. | ||
|
|
||
| | Old path | New path | | ||
| | --- | --- | | ||
| | `@apostrophecms/apostrophe-astro/lib/aposPageFetch.js` | `@apostrophecms/apostrophe-astro/helpers/server` | | ||
| | `@apostrophecms/apostrophe-astro/lib/static.js` | `@apostrophecms/apostrophe-astro/helpers/server` | | ||
| | `@apostrophecms/apostrophe-astro/lib/util` | `@apostrophecms/apostrophe-astro/helpers/universal` | | ||
| | `@apostrophecms/apostrophe-astro/lib/util.js` | `@apostrophecms/apostrophe-astro/helpers/universal` | | ||
| | `@apostrophecms/apostrophe-astro/lib/aposSetQueryParameter` | `@apostrophecms/apostrophe-astro/helpers/universal` | | ||
| | `@apostrophecms/apostrophe-astro/lib/aposSetQueryParameter.js` | `@apostrophecms/apostrophe-astro/helpers/universal` | | ||
|
|
||
| --- | ||
|
|
||
| ## Unsupported usage that must be removed | ||
|
|
||
| ### Virtual module imports | ||
|
|
||
| If your project imports `virtual:apostrophe-config` or `virtual:apostrophe-doctypes` directly, those imports are unsupported and must be removed. These were private implementation details and have been replaced by generated files that are not part of the public API. | ||
|
|
||
| ### Unlisted `lib/` paths | ||
|
|
||
| Any import of a `lib/` path not listed in the deprecated shims table above (e.g. `lib/aposRequest.js`, `lib/aposResponse.js`, `lib/format.js`) will fail under the v2 exports map. These are internal modules with no public equivalent. If you need functionality that was only accessible via an internal path, open an issue to discuss adding a proper public helper. | ||
|
|
||
| --- | ||
|
|
||
| ## Helper import contract | ||
|
|
||
| | Import path | Use in | | ||
| | --- | --- | | ||
| | `@apostrophecms/apostrophe-astro/helpers/server` | Astro frontmatter, server endpoints, SSR routes, prerendering. Depends on generated config and Node.js built-ins — do not use in client scripts. | | ||
| | `@apostrophecms/apostrophe-astro/helpers/universal` | Utilities that work in both server and client contexts. Pure functions only — no generated config, no `process.env`, no Node.js built-ins. | | ||
| | `@apostrophecms/apostrophe-astro/helpers/client` | Reserved for future browser-only helpers. Empty in v2. | | ||
|
|
||
| There is no top-level `helpers` barrel — always use one of the three explicit category paths. | ||
|
|
||
| --- | ||
|
|
||
| ## Static build cache directory | ||
|
|
||
| The static build cache has moved from `node_modules/.apostrophe-astro/` to `node_modules/.apostrophe-astro-static/`. Both directories live under `node_modules/` and require no `.gitignore` changes. This is an internal implementation detail with no user-facing impact. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /** | ||
| * Client-only public helpers for @apostrophecms/apostrophe-astro. | ||
| * | ||
| * Reserved for future browser-only helpers that depend on browser APIs | ||
| * such as `window` or `document`. No helpers are in this category yet. | ||
| * | ||
| * Do not add server-side or Node.js-dependent code here. | ||
| * | ||
| * @module @apostrophecms/apostrophe-astro/helpers/client | ||
| */ |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| import config from 'apostrophe-astro-config/config'; | ||
| import { getAposHost } from './url.js'; | ||
| import aposResponse from '../../lib/aposResponse.js'; | ||
| import aposRequest from '../../lib/aposRequest.js'; | ||
|
|
||
| /** | ||
| * A transparent proxy around the native `fetch` API for **server-side | ||
| * Astro code only** (`.astro` frontmatter, server endpoints, etc.). | ||
| * | ||
| * **Do NOT use in client-side code** — it depends on | ||
| * `apostrophe-astro-config/config` and exposes the internal backend host. | ||
| * For browser requests use plain `fetch` with relative URLs | ||
| * (e.g. `/api/v1/...`). | ||
| * | ||
| * What it does on top of native `fetch`: | ||
| * - Prepends the Apostrophe backend host (`getAposHost()`) to relative | ||
| * URLs (paths starting with `/`). | ||
| * - Injects the `x-apos-static-base-url: 1` header during static builds | ||
| * so the backend returns path-only URLs in its responses. | ||
| * | ||
| * Accepts the same arguments as `fetch(input, init?)` and returns a | ||
| * standard `Response`. All `init` options (method, body, headers, signal, | ||
| * etc.) are preserved and merged. | ||
| * | ||
| * @param {string|URL|Request} input - URL or Request object. Relative | ||
| * paths (starting with `/`) are resolved against `getAposHost()`. | ||
| * @param {RequestInit} [init] - Optional fetch init options. | ||
| * @returns {Promise<Response>} | ||
| * | ||
| * @example | ||
| * ```astro | ||
| * --- | ||
| * import { aposFetch } from '@apostrophecms/apostrophe-astro/helpers/server'; | ||
| * const response = await aposFetch('/api/v1/article?perPage=5'); | ||
| * const data = await response.json(); | ||
| * --- | ||
| * ``` | ||
| */ | ||
| export async function aposFetch(input, init) { | ||
| let url = input; | ||
|
|
||
| if (typeof url === 'string' && url.startsWith('/')) { | ||
| url = getAposHost() + url; | ||
| } | ||
| const headers = new Headers(init?.headers); | ||
| if (config.staticBuild) { | ||
| headers.set('x-apos-static-base-url', '1'); | ||
| } | ||
|
|
||
| return fetch(url, { | ||
| ...init || {}, | ||
| headers | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Fetch a full Apostrophe page data object for the given Astro request. | ||
| * | ||
| * This is the primary entry point for SSR and static-build page routes. | ||
| * It wraps `aposRequest` and `aposResponse` to forward the incoming | ||
| * request to the Apostrophe backend and return the parsed JSON page data, | ||
| * including automatic handling of trailing-slash redirects. | ||
| * | ||
| * For static builds, use this inside `getStaticPaths` / your page | ||
| * frontmatter to retrieve the `aposData` prop. | ||
| * | ||
| * @param {Request} req - The incoming Astro request (`Astro.request`). | ||
| * @returns {Promise<object>} The Apostrophe page data object. On error, | ||
| * returns an object with `errorFetchingPage` set to the caught error | ||
| * and `page.type` set to `'apos-fetch-error'`. | ||
| * | ||
| * @example | ||
| * ```astro | ||
| * --- | ||
| * import { aposPageFetch } from '@apostrophecms/apostrophe-astro/helpers/server'; | ||
| * const aposData = await aposPageFetch(Astro.request); | ||
| * --- | ||
| * ``` | ||
| */ | ||
| export async function aposPageFetch(req) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be part of public documentation and should have explicit "for internal use only" here. I'd argue that this refactoring makes no sense - we can only re-export it from |
||
| let aposData = {}; | ||
| try { | ||
| let request = aposRequest(req); | ||
| if (request.method === 'HEAD') { | ||
| request = new Request(request, { | ||
| method: 'GET' | ||
| }); | ||
| } | ||
| const response = await aposResponse(request); | ||
| let headers = response.headers; | ||
| aposData = await response.json(); | ||
|
|
||
| // Apostrophe's external-front middleware returns redirects as JSON | ||
| // (e.g. { redirect: true, url: '/fr/', status: 302 }). When the | ||
| // redirect only adds or removes a trailing slash we should follow | ||
| // it internally rather than bouncing the browser — otherwise locale | ||
| // home pages like /fr/ cause an infinite redirect loop. | ||
| // Skip the site root "/" — it never needs this treatment. | ||
| // | ||
| // When a prefix is configured, Apostrophe returns redirect URLs | ||
| // without the prefix (it's a routing concern, not stored in page | ||
| // data). Strip the prefix from `from` so both sides compare on | ||
| // the same terms, then re-add it when constructing the retry URL. | ||
| if (aposData.redirect && aposData.url !== '/') { | ||
| const prefix = config.aposPrefix || ''; | ||
| let from = new URL(request.url).pathname.replace(/\/+$/, ''); | ||
| if (prefix && from.startsWith(prefix + '/')) { | ||
| from = from.slice(prefix.length); | ||
| } else if (prefix && from === prefix) { | ||
| from = '/'; | ||
| } | ||
| const to = (aposData.url || '').replace(/\/+$/, ''); | ||
| if (from === to) { | ||
| const retryUrl = prefix + aposData.url; | ||
| const retry = new Request(new URL(retryUrl, request.url), request); | ||
| const retryResponse = await aposResponse(retry); | ||
| headers = retryResponse.headers; | ||
| const retryData = await retryResponse.json(); | ||
| // Safety check: if the retry itself redirects to the same | ||
| // URL we just tried, we've hit an infinite redirect loop. | ||
| // Return an error instead of bouncing forever. | ||
| if (retryData.redirect && retryData.url === aposData.url) { | ||
| throw new Error( | ||
| `Infinite redirect detected: ${aposData.url} redirects back to itself` | ||
| ); | ||
| } | ||
| aposData = retryData; | ||
| } | ||
| } | ||
|
|
||
| aposData.aposResponseHeaders = headers; | ||
| if (aposData.template === '@apostrophecms/page:notFound') { | ||
| aposData.notFound = true; | ||
| } | ||
| } catch (e) { | ||
| console.error('error:', e); | ||
| aposData.errorFetchingPage = e; | ||
| aposData.page = { | ||
| type: 'apos-fetch-error' | ||
| }; | ||
| } | ||
| return aposData; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| /** | ||
| * Server-only public helpers for @apostrophecms/apostrophe-astro. | ||
| * | ||
| * Use these in Astro frontmatter, server endpoints, prerendering routes, | ||
| * and any other server-side code. Do not import this module from | ||
| * client-side scripts — it depends on generated integration config and | ||
| * Node.js internals unavailable in browsers. | ||
| * | ||
| * @module @apostrophecms/apostrophe-astro/helpers/server | ||
| */ | ||
|
|
||
| export { aposFetch, aposPageFetch } from './fetch.js'; | ||
| export { getAposHost, isStaticBuild } from './url.js'; | ||
| export { getAllStaticPaths, getAllUrlMetadata, getLocales } from './static.js'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| /** | ||
| * Static build helpers — server-only. | ||
| * | ||
| * Re-exports the public static-build functions from `lib/static.js`. | ||
| * Use these in `getStaticPaths()` inside your `[...slug].astro` page | ||
| * to fetch all page paths and props from the Apostrophe backend. | ||
| */ | ||
|
|
||
| export { getAllStaticPaths, getAllUrlMetadata, getLocales } from '../../lib/static.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/helpers/server/static.jsre-exports the lib so that the project can import from/helpers/server, however thehelpers/server/fetch.jsnow is reverted (as requested) but doesn't re-export the lib. This sends mixed signals - the project will import static from helpers but the standard page loading from/lib? I thinkaposPageFetch()should be also re-exported here.