diff options
author | 2023-11-30 22:42:29 +0800 | |
---|---|---|
committer | 2023-11-30 22:42:29 +0800 | |
commit | 05628aaa3c9a3702b59cbf3d0e99abf66df651df (patch) | |
tree | a5212b8b8f12d734579db8a2bb90dd4c772cba44 | |
parent | 9c2342c327a13d2f7d1eb387b743e81f431b9813 (diff) | |
parent | ff8eadb95d34833baaf3ec7575bf4f293eae97da (diff) | |
download | astro-05628aaa3c9a3702b59cbf3d0e99abf66df651df.tar.gz astro-05628aaa3c9a3702b59cbf3d0e99abf66df651df.tar.zst astro-05628aaa3c9a3702b59cbf3d0e99abf66df651df.zip |
Merge branch 'main' into next
46 files changed, 443 insertions, 83 deletions
diff --git a/.changeset/config.json b/.changeset/config.json index 7920f52b8..030941db2 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -4,7 +4,7 @@ "commit": false, "linked": [], "access": "public", - "baseBranch": "next", + "baseBranch": "main", "updateInternalDependencies": "patch", "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true diff --git a/.changeset/wild-boats-wait.md b/.changeset/wild-boats-wait.md new file mode 100644 index 000000000..6300e839e --- /dev/null +++ b/.changeset/wild-boats-wait.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Returns the updated config in the integration `astro:config:setup` hook's `updateConfig()` API diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feecf12a5..30fd8867b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,7 @@ on: - main - "1-legacy" - "2-legacy" + - "3-legacy" - next defaults: diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index f5e1280f8..8f9d1a225 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -128,6 +128,106 @@ - Updated dependencies [[`abf601233`](https://github.com/withastro/astro/commit/abf601233f8188d118a8cb063c777478d8d9f1a3), [`addb57c8e`](https://github.com/withastro/astro/commit/addb57c8e80b7b67ec61224666f3a1db5c44410c), [`c7953645e`](https://github.com/withastro/astro/commit/c7953645eeaaf9e87c6db4494b0023d2c1878ff0)]: - @astrojs/markdown-remark@4.0.0-beta.0 +## 3.6.4 + +### Patch Changes + +- [#9226](https://github.com/withastro/astro/pull/9226) [`8f8a40e93`](https://github.com/withastro/astro/commit/8f8a40e93d6a0774ba84a6f5db8c42cd81db005e) Thanks [@outofambit](https://github.com/outofambit)! - Fix i18n fallback routing with routing strategy of always-prefix + +- [#9179](https://github.com/withastro/astro/pull/9179) [`3f28336d9`](https://github.com/withastro/astro/commit/3f28336d9a52d7e4364d455ee3128d14d10a078a) Thanks [@lilnasy](https://github.com/lilnasy)! - Fixes an issue where the presence of a slot in a page led to an error. + +- [#9219](https://github.com/withastro/astro/pull/9219) [`067a65f5b`](https://github.com/withastro/astro/commit/067a65f5b4d163bf1944cf47e6bf891f0b93553f) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix edge case where `<style>` updates inside of `.astro` files would ocassionally fail to update without reloading the page. + +- [#9236](https://github.com/withastro/astro/pull/9236) [`27d3e86e4`](https://github.com/withastro/astro/commit/27d3e86e4c8d04101113ab7a53477f26a4fb0619) Thanks [@ematipico](https://github.com/ematipico)! - The configuration `i18n.routingStrategy` has been replaced with an object called `routing`. + + ```diff + export default defineConfig({ + experimental: { + i18n: { + - routingStrategy: "prefix-always", + + routing: { + + prefixDefaultLocale: true, + + } + } + } + }) + ``` + + ```diff + export default defineConfig({ + experimental: { + i18n: { + - routingStrategy: "prefix-other-locales", + + routing: { + + prefixDefaultLocale: false, + + } + } + } + }) + ``` + +## 3.6.3 + +### Patch Changes + +- [#9193](https://github.com/withastro/astro/pull/9193) [`0dc99c9a2`](https://github.com/withastro/astro/commit/0dc99c9a28fcb6b46db49eefac6afa415875edcb) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Prevents the Code component from crashing if the lang isn't supported by falling back to `plaintext`. + +## 3.6.2 + +### Patch Changes + +- [#9189](https://github.com/withastro/astro/pull/9189) [`d90714fc3`](https://github.com/withastro/astro/commit/d90714fc3dd7c3eab0a6b29319b0b666bb04b678) Thanks [@SpencerWhitehead7](https://github.com/SpencerWhitehead7)! - Fixes an issue where links with the same pathname as the current page, but different search params, were not prefetched. + +## 3.6.4 + +### Patch Changes + +- [#9226](https://github.com/withastro/astro/pull/9226) [`8f8a40e93`](https://github.com/withastro/astro/commit/8f8a40e93d6a0774ba84a6f5db8c42cd81db005e) Thanks [@outofambit](https://github.com/outofambit)! - Fix i18n fallback routing with routing strategy of always-prefix + +- [#9179](https://github.com/withastro/astro/pull/9179) [`3f28336d9`](https://github.com/withastro/astro/commit/3f28336d9a52d7e4364d455ee3128d14d10a078a) Thanks [@lilnasy](https://github.com/lilnasy)! - Fixes an issue where the presence of a slot in a page led to an error. + +- [#9219](https://github.com/withastro/astro/pull/9219) [`067a65f5b`](https://github.com/withastro/astro/commit/067a65f5b4d163bf1944cf47e6bf891f0b93553f) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix edge case where `<style>` updates inside of `.astro` files would ocassionally fail to update without reloading the page. + +- [#9236](https://github.com/withastro/astro/pull/9236) [`27d3e86e4`](https://github.com/withastro/astro/commit/27d3e86e4c8d04101113ab7a53477f26a4fb0619) Thanks [@ematipico](https://github.com/ematipico)! - The configuration `i18n.routingStrategy` has been replaced with an object called `routing`. + + ```diff + export default defineConfig({ + experimental: { + i18n: { + - routingStrategy: "prefix-always", + + routing: { + + prefixDefaultLocale: true, + + } + } + } + }) + ``` + + ```diff + export default defineConfig({ + experimental: { + i18n: { + - routingStrategy: "prefix-other-locales", + + routing: { + + prefixDefaultLocale: false, + + } + } + } + }) + ``` + +## 3.6.3 + +### Patch Changes + +- [#9193](https://github.com/withastro/astro/pull/9193) [`0dc99c9a2`](https://github.com/withastro/astro/commit/0dc99c9a28fcb6b46db49eefac6afa415875edcb) Thanks [@florian-lefebvre](https://github.com/florian-lefebvre)! - Prevents the Code component from crashing if the lang isn't supported by falling back to `plaintext`. + +## 3.6.2 + +### Patch Changes + +- [#9189](https://github.com/withastro/astro/pull/9189) [`d90714fc3`](https://github.com/withastro/astro/commit/d90714fc3dd7c3eab0a6b29319b0b666bb04b678) Thanks [@SpencerWhitehead7](https://github.com/SpencerWhitehead7)! - Fixes an issue where links with the same pathname as the current page, but different search params, were not prefetched. + ## 3.6.1 ### Patch Changes diff --git a/packages/astro/components/Code.astro b/packages/astro/components/Code.astro index 93da72add..44d81b714 100644 --- a/packages/astro/components/Code.astro +++ b/packages/astro/components/Code.astro @@ -7,6 +7,7 @@ import type { ThemeRegistration, ThemeRegistrationRaw, } from 'shikiji'; +import { bundledLanguages } from 'shikiji/langs'; import { getCachedHighlighter } from '../dist/core/shiki.js'; interface Props { @@ -72,7 +73,13 @@ if (typeof lang === 'object') { } const highlighter = await getCachedHighlighter({ - langs: [lang], + langs: [ + typeof lang === 'string' + ? Object.keys(bundledLanguages).includes(lang) + ? lang + : 'plaintext' + : lang, + ], theme, experimentalThemes, wrap, diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 0acea76e4..5cbf4faf8 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -33,7 +33,7 @@ import type { DevOverlayWindow, } from '../runtime/client/dev-overlay/ui-library/index.js'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js'; -import type { OmitIndexSignature, Simplify } from '../type-utils.js'; +import type { DeepPartial, OmitIndexSignature, Simplify } from '../type-utils.js'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; import type { AstroPreferences } from '../preferences/index.js'; @@ -1484,23 +1484,43 @@ export interface AstroUserConfig { /** * @docs * @kind h4 - * @name experimental.i18n.routingStrategy - * @type {'prefix-always' | 'prefix-other-locales'} - * @default 'prefix-other-locales' - * @version 3.5.0 + * @name experimental.i18n.routing + * @type {Routing} + * @version 3.7.0 * @description * - * Controls the routing strategy to determine your site URLs. Set this based on your folder/URL path configuration for your default language: - * - * - `prefix-other-locales`(default): Only non-default languages will display a language prefix. - * The `defaultLocale` will not show a language prefix and content files do not exist in a localized folder. - * URLs will be of the form `example.com/[locale]/content/` for all non-default languages, but `example.com/content/` for the default locale. - * - `prefix-always`: All URLs will display a language prefix. - * URLs will be of the form `example.com/[locale]/content/` for every route, including the default language. - * Localized folders are used for every language, including the default. - * + * Controls the routing strategy to determine your site URLs. Set this based on your folder/URL path configuration for your default language. */ - routingStrategy?: 'prefix-always' | 'prefix-other-locales'; + routing?: { + /** + * @docs + * @name experimental.i18n.routing.prefixDefaultLocale + * @type {boolean} + * @default `false` + * @version 3.7.0 + * @description + * + * When `false`, only non-default languages will display a language prefix. + * The `defaultLocale` will not show a language prefix and content files do not exist in a localized folder. + * URLs will be of the form `example.com/[locale]/content/` for all non-default languages, but `example.com/content/` for the default locale. + * + * When `true`, all URLs will display a language prefix. + * URLs will be of the form `example.com/[locale]/content/` for every route, including the default language. + * Localized folders are used for every language, including the default. + */ + prefixDefaultLocale: boolean; + + /** + * @name experimental.i18n.routing.strategy + * @type {"pathname"} + * @default `"pathname"` + * @version 3.7.0 + * @description + * + * - `"pathanme": The strategy is applied to the pathname of the URLs + */ + strategy: 'pathname'; + }; }; /** * @docs @@ -2212,6 +2232,12 @@ export interface APIContext< currentLocale: string | undefined; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type Routing = { + prefixDefaultLocale: boolean; + strategy: 'pathname'; +}; + export type APIRoute<Props extends Record<string, any> = Record<string, any>> = ( context: APIContext<Props> ) => Response | Promise<Response>; @@ -2260,7 +2286,7 @@ export interface AstroIntegration { config: AstroConfig; command: 'dev' | 'build' | 'preview'; isRestart: boolean; - updateConfig: (newConfig: Record<string, any>) => void; + updateConfig: (newConfig: DeepPartial<AstroConfig>) => AstroConfig; addRenderer: (renderer: AstroRenderer) => void; addWatchFile: (path: URL | string) => void; injectScript: (stage: InjectedScriptStage, content: string) => void; diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index f3fa3fa7b..6abd2f734 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -282,7 +282,7 @@ export class App { env: this.#pipeline.env, mod: handler as any, locales: this.#manifest.i18n?.locales, - routingStrategy: this.#manifest.i18n?.routingStrategy, + routing: this.#manifest.i18n?.routing, defaultLocale: this.#manifest.i18n?.defaultLocale, }); } else { @@ -319,7 +319,7 @@ export class App { mod, env: this.#pipeline.env, locales: this.#manifest.i18n?.locales, - routingStrategy: this.#manifest.i18n?.routingStrategy, + routing: this.#manifest.i18n?.routing, defaultLocale: this.#manifest.i18n?.defaultLocale, }); } diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 9f9d80f44..67ed77123 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -55,7 +55,7 @@ export type SSRManifest = { export type SSRManifestI18n = { fallback?: Record<string, string>; - routingStrategy?: 'prefix-always' | 'prefix-other-locales'; + routing?: 'prefix-always' | 'prefix-other-locales'; locales: string[]; defaultLocale: string; }; diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index a7d286b40..e35585ca7 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -552,7 +552,7 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli env: pipeline.getEnvironment(), mod, locales: i18n?.locales, - routingStrategy: i18n?.routingStrategy, + routing: i18n?.routing, defaultLocale: i18n?.defaultLocale, }); @@ -626,7 +626,7 @@ export function createBuildManifest( if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, defaultLocale: settings.config.experimental.i18n.defaultLocale, locales: settings.config.experimental.i18n.locales, }; diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index 0ac0db028..6f9ec6326 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -243,7 +243,7 @@ function buildManifest( if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, locales: settings.config.experimental.i18n.locales, defaultLocale: settings.config.experimental.i18n.defaultLocale, }; diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 30dafb8da..c12689654 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -62,6 +62,8 @@ const ASTRO_CONFIG_DEFAULTS = { }, } satisfies AstroUserConfig & { server: { open: boolean } }; +type RoutingStrategies = 'prefix-always' | 'prefix-other-locales'; + export const AstroConfigSchema = z.object({ root: z .string() @@ -316,11 +318,25 @@ export const AstroConfigSchema = z.object({ defaultLocale: z.string(), locales: z.string().array(), fallback: z.record(z.string(), z.string()).optional(), - // TODO: properly add default when the feature goes of experimental - routingStrategy: z - .enum(['prefix-always', 'prefix-other-locales']) - .optional() - .default('prefix-other-locales'), + routing: z + .object({ + prefixDefaultLocale: z.boolean().default(false), + strategy: z.enum(['pathname']).default('pathname'), + }) + .default({}) + .transform((routing) => { + let strategy: RoutingStrategies; + switch (routing.strategy) { + case 'pathname': { + if (routing.prefixDefaultLocale === true) { + strategy = 'prefix-always'; + } else { + strategy = 'prefix-other-locales'; + } + } + } + return strategy; + }), }) .optional() .superRefine((i18n, ctx) => { diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index c0490a740..eeaec2244 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -144,7 +144,7 @@ export async function callEndpoint( props: ctx.props, site: env.site, adapterName: env.adapterName, - routingStrategy: ctx.routingStrategy, + routingStrategy: ctx.routing, defaultLocale: ctx.defaultLocale, locales: ctx.locales, }); diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts index b3493b7c0..67081a47e 100644 --- a/packages/astro/src/core/pipeline.ts +++ b/packages/astro/src/core/pipeline.ts @@ -122,7 +122,7 @@ export class Pipeline { site: env.site, adapterName: env.adapterName, locales: renderContext.locales, - routingStrategy: renderContext.routingStrategy, + routingStrategy: renderContext.routing, defaultLocale: renderContext.defaultLocale, }); diff --git a/packages/astro/src/core/render/context.ts b/packages/astro/src/core/render/context.ts index 0f0bf39b0..efb73d766 100644 --- a/packages/astro/src/core/render/context.ts +++ b/packages/astro/src/core/render/context.ts @@ -30,7 +30,7 @@ export interface RenderContext { locals?: object; locales: string[] | undefined; defaultLocale: string | undefined; - routingStrategy: 'prefix-always' | 'prefix-other-locales' | undefined; + routing: 'prefix-always' | 'prefix-other-locales' | undefined; } export type CreateRenderContextArgs = Partial< @@ -62,7 +62,7 @@ export async function createRenderContext( params, props, locales: options.locales, - routingStrategy: options.routingStrategy, + routing: options.routing, defaultLocale: options.defaultLocale, }; diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index c73306de0..1175f55d7 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -61,14 +61,14 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag locals: renderContext.locals ?? {}, locales: renderContext.locales, defaultLocale: renderContext.defaultLocale, - routingStrategy: renderContext.routingStrategy, + routingStrategy: renderContext.routing, }); const response = await runtimeRenderPage( result, Component, renderContext.props, - null, + {}, env.streaming, renderContext.route ); diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 7c04638ac..c9a49cbbe 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -532,7 +532,7 @@ export function createRouteManifest( // Work done, now we start creating "fallback" routes based on the configuration - if (i18n.routingStrategy === 'prefix-always') { + if (i18n.routing === 'prefix-always') { // we attempt to retrieve the index page of the default locale const defaultLocaleRoutes = routesByLocale.get(i18n.defaultLocale); if (defaultLocaleRoutes) { diff --git a/packages/astro/src/i18n/middleware.ts b/packages/astro/src/i18n/middleware.ts index 79ac2c962..efd9f1e54 100644 --- a/packages/astro/src/i18n/middleware.ts +++ b/packages/astro/src/i18n/middleware.ts @@ -48,14 +48,14 @@ export function createI18nMiddleware( const separators = url.pathname.split('/'); const pathnameContainsDefaultLocale = url.pathname.includes(`/${defaultLocale}`); const isLocaleFree = checkIsLocaleFree(url.pathname, i18n.locales); - if (i18n.routingStrategy === 'prefix-other-locales' && pathnameContainsDefaultLocale) { + if (i18n.routing === 'prefix-other-locales' && pathnameContainsDefaultLocale) { const newLocation = url.pathname.replace(`/${defaultLocale}`, ''); response.headers.set('Location', newLocation); return new Response(null, { status: 404, headers: response.headers, }); - } else if (i18n.routingStrategy === 'prefix-always') { + } else if (i18n.routing === 'prefix-always') { if (url.pathname === base + '/' || url.pathname === base) { if (trailingSlash === 'always') { return context.redirect(`${appendForwardSlash(joinPaths(base, i18n.defaultLocale))}`); @@ -81,8 +81,8 @@ export function createI18nMiddleware( const fallbackLocale = fallback[urlLocale]; let newPathname: string; // If a locale falls back to the default locale, we want to **remove** the locale because - // the default locale doesn't have a prefix - if (fallbackLocale === defaultLocale) { + // the default locale doesn't have a prefix, but _only_ if prefix-always is false + if (fallbackLocale === defaultLocale && i18n.routing !== 'prefix-always') { newPathname = url.pathname.replace(`/${urlLocale}`, ``); } else { newPathname = url.pathname.replace(`/${urlLocale}`, `/${fallbackLocale}`); diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index f21fd088b..18dfa5354 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -125,6 +125,7 @@ export async function runHookConfigSetup({ }, updateConfig: (newConfig) => { updatedConfig = mergeConfig(updatedConfig, newConfig) as AstroConfig; + return { ...updatedConfig }; }, injectRoute: (injectRoute) => { if (injectRoute.entrypoint == null && 'entryPoint' in injectRoute) { @@ -393,6 +394,7 @@ export async function runHookBuildSetup({ target, updateConfig: (newConfig) => { updatedConfig = mergeConfig(updatedConfig, newConfig); + return { ...updatedConfig }; }, logger: getLogger(integration, logger), }), diff --git a/packages/astro/src/runtime/server/render/page.ts b/packages/astro/src/runtime/server/render/page.ts index 8bc5366cf..fbfe567a8 100644 --- a/packages/astro/src/runtime/server/render/page.ts +++ b/packages/astro/src/runtime/server/render/page.ts @@ -25,7 +25,7 @@ export async function renderPage( componentFactory.name, componentFactory, pageProps, - null, + {}, true, route ); diff --git a/packages/astro/src/type-utils.ts b/packages/astro/src/type-utils.ts index d777a9f28..98f2c9864 100644 --- a/packages/astro/src/type-utils.ts +++ b/packages/astro/src/type-utils.ts @@ -30,3 +30,13 @@ export type ValueOf<T> = T[keyof T]; // Gets the type of the values of a Map export type MapValue<T> = T extends Map<any, infer V> ? V : never; + +// Allow the user to create a type where all keys are optional. +// Useful for functions where props are merged. +export type DeepPartial<T> = { + [P in keyof T]?: T[P] extends (infer U)[] + ? DeepPartial<U>[] + : T[P] extends object | undefined + ? DeepPartial<T[P]> + : T[P]; +}; diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index b8f4ab661..d324dfdc6 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -90,7 +90,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest if (settings.config.experimental.i18n) { i18nManifest = { fallback: settings.config.experimental.i18n.fallback, - routingStrategy: settings.config.experimental.i18n.routingStrategy, + routing: settings.config.experimental.i18n.routing, defaultLocale: settings.config.experimental.i18n.defaultLocale, locales: settings.config.experimental.i18n.locales, }; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index aa9036810..9f64af31f 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -227,7 +227,7 @@ export async function handleRoute({ mod, route, locales: manifest.i18n?.locales, - routingStrategy: manifest.i18n?.routingStrategy, + routing: manifest.i18n?.routing, defaultLocale: manifest.i18n?.defaultLocale, }); } else { @@ -286,7 +286,7 @@ export async function handleRoute({ mod, env, locales: i18n?.locales, - routingStrategy: i18n?.routingStrategy, + routing: i18n?.routing, defaultLocale: i18n?.defaultLocale, }); } diff --git a/packages/astro/src/vite-plugin-astro/hmr.ts b/packages/astro/src/vite-plugin-astro/hmr.ts index 50e1bd25a..dc820e9f6 100644 --- a/packages/astro/src/vite-plugin-astro/hmr.ts +++ b/packages/astro/src/vite-plugin-astro/hmr.ts @@ -95,8 +95,8 @@ export async function handleHotUpdate( // If only styles are changed, remove the component file from the update list if (isStyleOnlyChange) { logger.debug('watch', 'style-only change'); - // remove base file and hoisted scripts - return mods.filter((mod) => mod.id !== ctx.file && !mod.id?.endsWith('.ts')); + // Only return the Astro styles that have changed! + return mods.filter((mod) => mod.id?.includes('astro&type=style')); } // Add hoisted scripts so these get invalidated diff --git a/packages/astro/test/astro-slots.test.js b/packages/astro/test/astro-slots.test.js index 34f1d8225..69a0025e1 100644 --- a/packages/astro/test/astro-slots.test.js +++ b/packages/astro/test/astro-slots.test.js @@ -40,13 +40,20 @@ describe('Slots', () => { expect($('#default').text().trim()).to.equal('Default'); }); - it('Slots render fallback content by default', async () => { + it('Slots of a component render fallback content by default', async () => { const html = await fixture.readFile('/fallback/index.html'); const $ = cheerio.load(html); expect($('#default')).to.have.lengthOf(1); }); + it('Slots of a page render fallback content', async () => { + const html = await fixture.readFile('/fallback-own/index.html'); + const $ = cheerio.load(html); + + expect($('#default')).to.have.lengthOf(1); + }); + it('Slots override fallback content', async () => { const html = await fixture.readFile('/fallback-override/index.html'); const $ = cheerio.load(html); diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/fallback-own.astro b/packages/astro/test/fixtures/astro-slots/src/pages/fallback-own.astro new file mode 100644 index 000000000..89319f8b1 --- /dev/null +++ b/packages/astro/test/fixtures/astro-slots/src/pages/fallback-own.astro @@ -0,0 +1,10 @@ +<html> + <head> + <!-- Head Stuff --> + </head> + <body> + <slot> + <div id="default"></div> + </slot> + </body> +</html> diff --git a/packages/astro/test/fixtures/astro-slots/src/pages/fallback.astro b/packages/astro/test/fixtures/astro-slots/src/pages/fallback.astro index 88aba06e9..aa1a47958 100644 --- a/packages/astro/test/fixtures/astro-slots/src/pages/fallback.astro +++ b/packages/astro/test/fixtures/astro-slots/src/pages/fallback.astro @@ -7,8 +7,6 @@ import Fallback from '../components/Fallback.astro'; <!-- Head Stuff --> </head> <body> - <div id="fallback"> - <Fallback /> - </div> + <Fallback /> </body> </html> diff --git a/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs index d20245efb..1aae961c7 100644 --- a/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-base/astro.config.mjs @@ -8,7 +8,9 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-always" + routing: { + prefixDefaultLocale: true + } } } }) diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs index f2aac5899..6152d1039 100644 --- a/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-prefix-always/astro.config.mjs @@ -7,7 +7,9 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-always" + routing: { + prefixDefaultLocale: true + } } }, base: "/new-site" diff --git a/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs b/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs index 3b8911f6d..4eb0abf1b 100644 --- a/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs +++ b/packages/astro/test/fixtures/i18n-routing-prefix-other-locales/astro.config.mjs @@ -7,7 +7,6 @@ export default defineConfig({ locales: [ 'en', 'pt', 'it' ], - routingStrategy: "prefix-other-locales" }, }, diff --git a/packages/astro/test/i18n-routing.test.js b/packages/astro/test/i18n-routing.test.js index 52ebeda23..53f82d16f 100644 --- a/packages/astro/test/i18n-routing.test.js +++ b/packages/astro/test/i18n-routing.test.js @@ -291,7 +291,9 @@ describe('[DEV] i18n routing', () => { fallback: { it: 'en', }, - routingStrategy: 'prefix-other-locales', + routing: { + prefixDefaultLocale: false, + }, }, }, }); @@ -661,7 +663,9 @@ describe('[SSG] i18n routing', () => { fallback: { it: 'en', }, - routingStrategy: 'prefix-always', + routing: { + prefixDefaultLocale: true, + }, }, }, }); @@ -961,6 +965,37 @@ describe('[SSR] i18n routing', () => { let response = await app.render(request); expect(response.status).to.equal(404); }); + + describe('with routing strategy [prefix-always]', () => { + before(async () => { + fixture = await loadFixture({ + root: './fixtures/i18n-routing-fallback/', + output: 'server', + adapter: testAdapter(), + experimental: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'pt', 'it'], + fallback: { + it: 'en', + }, + routing: { + prefixDefaultLocale: true, + }, + }, + }, + }); + await fixture.build(); + app = await fixture.loadTestAdapterApp(); + }); + + it('should redirect to the english locale, which is the first fallback', async () => { + let request = new Request('http://example.com/new-site/it/start'); + let response = await app.render(request); + expect(response.status).to.equal(302); + expect(response.headers.get('location')).to.equal('/new-site/en/start'); + }); + }); }); describe('preferred locale', () => { diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index 8960e662d..ff700833e 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -30,6 +30,33 @@ describe('Integration API', () => { expect(updatedViteConfig).to.haveOwnProperty('define'); }); + it('runHookBuildSetup should return updated config', async () => { + let updatedInternalConfig; + const updatedViteConfig = await runHookBuildSetup({ + config: { + integrations: [ + { + name: 'test', + hooks: { + 'astro:build:setup'({ updateConfig }) { + updatedInternalConfig = updateConfig({ + define: { + foo: 'bar', + }, + }); + }, + }, + }, + ], + }, + vite: {}, + logger: defaultLogger, + pages: new Map(), + target: 'server', + }); + expect(updatedViteConfig).to.be.deep.equal(updatedInternalConfig); + }); + it('runHookConfigSetup can update Astro config', async () => { const site = 'https://test.com/'; const updatedSettings = await runHookConfigSetup({ diff --git a/packages/integrations/node/CHANGELOG.md b/packages/integrations/node/CHANGELOG.md index 732122ecf..e4cf76b85 100644 --- a/packages/integrations/node/CHANGELOG.md +++ b/packages/integrations/node/CHANGELOG.md @@ -13,6 +13,18 @@ - Updated dependencies [[`abf601233`](https://github.com/withastro/astro/commit/abf601233f8188d118a8cb063c777478d8d9f1a3), [`6201bbe96`](https://github.com/withastro/astro/commit/6201bbe96c2a083fb201e4a43a9bd88499821a3e), [`cdabf6ef0`](https://github.com/withastro/astro/commit/cdabf6ef02be7220fd2b6bdcef924ceca089381e), [`1c48ed286`](https://github.com/withastro/astro/commit/1c48ed286538ab9e354eca4e4dcd7c6385c96721), [`37697a2c5`](https://github.com/withastro/astro/commit/37697a2c5511572dc29c0a4ea46f90c2f62be8e6), [`bd0c2e9ae`](https://github.com/withastro/astro/commit/bd0c2e9ae3389a9d3085050c1e8134ae98dff299), [`0fe3a7ed5`](https://github.com/withastro/astro/commit/0fe3a7ed5d7bb1a9fce1623e84ba14104b51223c), [`710be505c`](https://github.com/withastro/astro/commit/710be505c9ddf416e77a75343d8cae9c497d72c6), [`153a5abb9`](https://github.com/withastro/astro/commit/153a5abb905042ac68b712514dc9ec387d3e6b17)]: - astro@4.0.0-beta.0 +## 6.1.0 + +### Minor Changes + +- [#9125](https://github.com/withastro/astro/pull/9125) [`8f1d50957`](https://github.com/withastro/astro/commit/8f1d509574f5ee5d77816a13d89ce452dce403ff) Thanks [@matthewp](https://github.com/matthewp)! - Automatically sets immutable cache headers for assets served from the `/_astro` directory. + +## 6.1.0 + +### Minor Changes + +- [#9125](https://github.com/withastro/astro/pull/9125) [`8f1d50957`](https://github.com/withastro/astro/commit/8f1d509574f5ee5d77816a13d89ce452dce403ff) Thanks [@matthewp](https://github.com/matthewp)! - Automatically sets immutable cache headers for assets served from the `/_astro` directory. + ## 6.0.4 ### Patch Changes diff --git a/packages/integrations/node/README.md b/packages/integrations/node/README.md index 5753a59bc..af11405c0 100644 --- a/packages/integrations/node/README.md +++ b/packages/integrations/node/README.md @@ -190,6 +190,16 @@ In the case of multiple run-time variables, store them in a seperate file (e.g. export $(cat .env.runtime) && astro build ``` +#### Assets + +In standalone mode, assets in your `dist/client/` folder are served via the standalone server. You might be deploying these assets to a CDN, in which case the server will never actually be serving them. But in some cases, such as intranet sites, it's fine to serve static assets directly from the application server. + +Assets in the `dist/client/_astro/` folder are the ones that Astro has built. These assets are all named with a hash and therefore can be given long cache headers. Internally the adapter adds this header for these assets: + +``` +Cache-Control: public, max-age=31536000, immutable +``` + ## Troubleshooting ### SyntaxError: Named export 'compile' not found diff --git a/packages/integrations/node/src/http-server.ts b/packages/integrations/node/src/http-server.ts index 2f2339cdf..904937601 100644 --- a/packages/integrations/node/src/http-server.ts +++ b/packages/integrations/node/src/http-server.ts @@ -10,6 +10,7 @@ interface CreateServerOptions { port: number; host: string | undefined; removeBase: (pathname: string) => string; + assets: string; } function parsePathname(pathname: string, host: string | undefined, port: number) { @@ -22,9 +23,16 @@ function parsePathname(pathname: string, host: string | undefined, port: number) } export function createServer( - { client, port, host, removeBase }: CreateServerOptions, + { client, port, host, removeBase, assets }: CreateServerOptions, handler: http.RequestListener ) { + // The `base` is removed before passed to this function, so we don't + // need to check for it here. + const assetsPrefix = `/${assets}/`; + function isImmutableAsset(pathname: string) { + return pathname.startsWith(assetsPrefix); + } + const listener: http.RequestListener = (req, res) => { if (req.url) { let pathname: string | undefined = removeBase(req.url); @@ -54,6 +62,12 @@ export function createServer( // File not found, forward to the SSR handler handler(req, res); }); + stream.on('headers', (_res: http.ServerResponse<http.IncomingMessage>) => { + if (isImmutableAsset(encodedURI)) { + // Taken from https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#immutable + _res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); + } + }); stream.on('directory', () => { // On directory find, redirect to the trailing slash let location: string; diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 1f3707949..bac5c25ef 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -6,7 +6,7 @@ export function getAdapter(options: Options): AstroAdapter { name: '@astrojs/node', serverEntrypoint: '@astrojs/node/server.js', previewEntrypoint: '@astrojs/node/preview.js', - exports: ['handler', 'startServer'], + exports: ['handler', 'startServer', 'options'], args: options, supportedAstroFeatures: { hybridOutput: 'stable', @@ -49,6 +49,7 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr server: config.build.server?.toString(), host: config.server.host, port: config.server.port, + assets: config.build.assets, }; setAdapter(getAdapter(_options)); diff --git a/packages/integrations/node/src/preview.ts b/packages/integrations/node/src/preview.ts index 70ed54698..89baa1897 100644 --- a/packages/integrations/node/src/preview.ts +++ b/packages/integrations/node/src/preview.ts @@ -17,11 +17,13 @@ const preview: CreatePreviewServer = async function ({ type ServerModule = ReturnType<typeof createExports>; type MaybeServerModule = Partial<ServerModule>; let ssrHandler: ServerModule['handler']; + let options: ServerModule['options']; try { process.env.ASTRO_NODE_AUTOSTART = 'disabled'; const ssrModule: MaybeServerModule = await import(serverEntrypoint.toString()); if (typeof ssrModule.handler === 'function') { ssrHandler = ssrModule.handler; + options = ssrModule.options!; } else { throw new AstroError( `The server entrypoint doesn't have a handler. Are you sure this is the right file?` @@ -59,6 +61,7 @@ const preview: CreatePreviewServer = async function ({ port, host, removeBase, + assets: options.assets, }, handler ); diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index 90bf8c44c..88bcd7d62 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -8,6 +8,7 @@ applyPolyfills(); export function createExports(manifest: SSRManifest, options: Options) { const app = new NodeApp(manifest); return { + options: options, handler: middleware(app, options.mode), startServer: () => startServer(app, options), }; diff --git a/packages/integrations/node/src/standalone.ts b/packages/integrations/node/src/standalone.ts index abe40ff5c..e167e8ab6 100644 --- a/packages/integrations/node/src/standalone.ts +++ b/packages/integrations/node/src/standalone.ts @@ -52,6 +52,7 @@ export default function startServer(app: NodeApp, options: Options) { port, host, removeBase: app.removeBase.bind(app), + assets: options.assets, }, handler ); diff --git a/packages/integrations/node/src/types.ts b/packages/integrations/node/src/types.ts index 85f4f4fbc..1917d8cf3 100644 --- a/packages/integrations/node/src/types.ts +++ b/packages/integrations/node/src/types.ts @@ -15,6 +15,7 @@ export interface Options extends UserOptions { port: number; server: string; client: string; + assets: string; } export type RequestHandlerParams = [ diff --git a/packages/integrations/node/test/assets.test.js b/packages/integrations/node/test/assets.test.js new file mode 100644 index 000000000..9e44ab31d --- /dev/null +++ b/packages/integrations/node/test/assets.test.js @@ -0,0 +1,43 @@ +import { expect } from 'chai'; +import nodejs from '../dist/index.js'; +import { loadFixture } from './test-utils.js'; +import * as cheerio from 'cheerio'; + +describe('Assets', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let devPreview; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/image/', + output: 'server', + adapter: nodejs({ mode: 'standalone' }), + vite: { + build: { + assetsInlineLimit: 0, + }, + }, + }); + await fixture.build(); + devPreview = await fixture.preview(); + }); + + after(async () => { + await devPreview.stop(); + }); + + it('Assets within the _astro folder should be given immutable headers', async () => { + let response = await fixture.fetch('/text-file'); + let cacheControl = response.headers.get('cache-control'); + expect(cacheControl).to.equal(null); + const html = await response.text(); + const $ = cheerio.load(html); + + // Fetch the asset + const fileURL = $('a').attr('href'); + response = await fixture.fetch(fileURL); + cacheControl = response.headers.get('cache-control'); + expect(cacheControl).to.equal('public, max-age=31536000, immutable'); + }); +}); diff --git a/packages/integrations/node/test/fixtures/image/src/assets/file.txt b/packages/integrations/node/test/fixtures/image/src/assets/file.txt new file mode 100644 index 000000000..e9ea42a12 --- /dev/null +++ b/packages/integrations/node/test/fixtures/image/src/assets/file.txt @@ -0,0 +1 @@ +this is a text file diff --git a/packages/integrations/node/test/fixtures/image/src/pages/text-file.astro b/packages/integrations/node/test/fixtures/image/src/pages/text-file.astro new file mode 100644 index 000000000..893250360 --- /dev/null +++ b/packages/integrations/node/test/fixtures/image/src/pages/text-file.astro @@ -0,0 +1,14 @@ +--- +import txt from '../assets/file.txt?url'; +--- +<html> + <head> + <title>Testing</title> + </head> + <body> + <h1>Testing</h1> + <main> + <a href={txt} download>Download text file</a> + </main> + </body> +</html> diff --git a/packages/integrations/solid/src/index.ts b/packages/integrations/solid/src/index.ts index 127d9ddb6..8707c006f 100644 --- a/packages/integrations/solid/src/index.ts +++ b/packages/integrations/solid/src/index.ts @@ -1,4 +1,4 @@ -import type { AstroIntegration, AstroRenderer } from 'astro'; +import type { AstroConfig, AstroIntegration, AstroRenderer } from 'astro'; import solid, { type Options as ViteSolidPluginOptions } from 'vite-plugin-solid'; async function getViteConfiguration(isDev: boolean, { include, exclude }: Options = {}) { @@ -34,7 +34,7 @@ async function getViteConfiguration(isDev: boolean, { include, exclude }: Option ssr: { external: ['babel-preset-solid'], }, - }; + } satisfies AstroConfig['vite']; } function getRenderer(): AstroRenderer { diff --git a/packages/upgrade/CHANGELOG.md b/packages/upgrade/CHANGELOG.md index 825420dad..969c359b6 100644 --- a/packages/upgrade/CHANGELOG.md +++ b/packages/upgrade/CHANGELOG.md @@ -27,3 +27,37 @@ ```bash pnpm dlx @astrojs/upgrade ``` + +## 0.1.1 + +### Patch Changes + +- [#9213](https://github.com/withastro/astro/pull/9213) [`54e57fe9d`](https://github.com/withastro/astro/commit/54e57fe9d7600c888fc7b0bc3f5dbca5543f36cd) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Fix unhandled error when running `@astrojs/upgrade beta` outside of a monorepo + +## 0.1.0 + +### Minor Changes + +- [#8525](https://github.com/withastro/astro/pull/8525) [`5a3875018`](https://github.com/withastro/astro/commit/5a38750188d1af30ea5277cea70f454c363b5062) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Initial release! + + `@astrojs/upgrade` is an automated command-line tool for upgrading Astro and your official Astro integrations together. + + Inside of your existing `astro` project, run the following command to install the `latest` version of your integrations. + + **With NPM:** + + ```bash + npx @astrojs/upgrade + ``` + + **With Yarn:** + + ```bash + yarn dlx @astrojs/upgrade + ``` + + **With PNPM:** + + ```bash + pnpm dlx @astrojs/upgrade + ``` diff --git a/packages/upgrade/src/messages.ts b/packages/upgrade/src/messages.ts index e159a6f06..ae2ec49eb 100644 --- a/packages/upgrade/src/messages.ts +++ b/packages/upgrade/src/messages.ts @@ -1,7 +1,6 @@ /* eslint no-console: 'off' */ import { color, label, spinner as load } from '@astrojs/cli-kit'; import { align } from '@astrojs/cli-kit/utils'; -import semverParse from 'semver/functions/parse.js'; import terminalLink from 'terminal-link'; import detectPackageManager from 'which-pm-runs'; import type { PackageInfo } from './actions/context.js'; @@ -110,8 +109,8 @@ export const upgrade = async (packageInfo: PackageInfo, text: string) => { const bg = isMajor ? (v: string) => color.bgYellow(color.black(` ${v} `)) : color.green; const style = isMajor ? color.yellow : color.green; const symbol = isMajor ? '▲' : '●'; - const toVersion = semverParse(targetVersion)!; - const version = `v${toVersion.version}`; + const toVersion = targetVersion.replace(/^\D+/, ''); + const version = `v${toVersion}`; const length = 12 + name.length + text.length + version.length; if (length > stdout.columns) { diff --git a/packages/upgrade/test/utils.js b/packages/upgrade/test/utils.js index d57ceacd3..ff5d5dd83 100644 --- a/packages/upgrade/test/utils.js +++ b/packages/upgrade/test/utils.js @@ -1,4 +1,3 @@ -import fs from 'node:fs'; import { setStdout } from '../dist/index.js'; import stripAnsi from 'strip-ansi'; @@ -30,23 +29,3 @@ export function setup() { }, }; } - -const resetBasicFixture = async () => { - const packagePath = new URL('./fixtures/basic/package.json', import.meta.url); - const packageJsonData = JSON.parse( - await fs.promises.readFile(packagePath, { encoding: 'utf-8' }) - ); - const overriddenPackageJson = Object.assign(packageJsonData, { - dependencies: { - astro: '1.0.0', - }, - }); - - return Promise.all([ - fs.promises.writeFile(packagePath, JSON.stringify(overriddenPackageJson, null, 2), { - encoding: 'utf-8', - }), - ]); -}; - -export const resetFixtures = () => Promise.allSettled([resetBasicFixture()]); |