mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
Merge branch 'main' into next
This commit is contained in:
commit
05628aaa3c
46 changed files with 443 additions and 83 deletions
|
@ -4,7 +4,7 @@
|
|||
"commit": false,
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "next",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
|
|
5
.changeset/wild-boats-wait.md
Normal file
5
.changeset/wild-boats-wait.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Returns the updated config in the integration `astro:config:setup` hook's `updateConfig()` API
|
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
|
@ -6,6 +6,7 @@ on:
|
|||
- main
|
||||
- "1-legacy"
|
||||
- "2-legacy"
|
||||
- "3-legacy"
|
||||
- next
|
||||
|
||||
defaults:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}`);
|
||||
|
|
|
@ -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),
|
||||
}),
|
||||
|
|
|
@ -25,7 +25,7 @@ export async function renderPage(
|
|||
componentFactory.name,
|
||||
componentFactory,
|
||||
pageProps,
|
||||
null,
|
||||
{},
|
||||
true,
|
||||
route
|
||||
);
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
10
packages/astro/test/fixtures/astro-slots/src/pages/fallback-own.astro
vendored
Normal file
10
packages/astro/test/fixtures/astro-slots/src/pages/fallback-own.astro
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<!-- Head Stuff -->
|
||||
</head>
|
||||
<body>
|
||||
<slot>
|
||||
<div id="default"></div>
|
||||
</slot>
|
||||
</body>
|
||||
</html>
|
|
@ -7,8 +7,6 @@ import Fallback from '../components/Fallback.astro';
|
|||
<!-- Head Stuff -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="fallback">
|
||||
<Fallback />
|
||||
</div>
|
||||
<Fallback />
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,7 +8,9 @@ export default defineConfig({
|
|||
locales: [
|
||||
'en', 'pt', 'it'
|
||||
],
|
||||
routingStrategy: "prefix-always"
|
||||
routing: {
|
||||
prefixDefaultLocale: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -7,7 +7,9 @@ export default defineConfig({
|
|||
locales: [
|
||||
'en', 'pt', 'it'
|
||||
],
|
||||
routingStrategy: "prefix-always"
|
||||
routing: {
|
||||
prefixDefaultLocale: true
|
||||
}
|
||||
}
|
||||
},
|
||||
base: "/new-site"
|
||||
|
|
|
@ -7,7 +7,6 @@ export default defineConfig({
|
|||
locales: [
|
||||
'en', 'pt', 'it'
|
||||
],
|
||||
routingStrategy: "prefix-other-locales"
|
||||
},
|
||||
|
||||
},
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -52,6 +52,7 @@ export default function startServer(app: NodeApp, options: Options) {
|
|||
port,
|
||||
host,
|
||||
removeBase: app.removeBase.bind(app),
|
||||
assets: options.assets,
|
||||
},
|
||||
handler
|
||||
);
|
||||
|
|
|
@ -15,6 +15,7 @@ export interface Options extends UserOptions {
|
|||
port: number;
|
||||
server: string;
|
||||
client: string;
|
||||
assets: string;
|
||||
}
|
||||
|
||||
export type RequestHandlerParams = [
|
||||
|
|
43
packages/integrations/node/test/assets.test.js
Normal file
43
packages/integrations/node/test/assets.test.js
Normal file
|
@ -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');
|
||||
});
|
||||
});
|
1
packages/integrations/node/test/fixtures/image/src/assets/file.txt
vendored
Normal file
1
packages/integrations/node/test/fixtures/image/src/assets/file.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
this is a text file
|
14
packages/integrations/node/test/fixtures/image/src/pages/text-file.astro
vendored
Normal file
14
packages/integrations/node/test/fixtures/image/src/pages/text-file.astro
vendored
Normal file
|
@ -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>
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()]);
|
||||
|
|
Loading…
Add table
Reference in a new issue