0
Fork 0
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:
bluwy 2023-11-30 22:42:29 +08:00
commit 05628aaa3c
46 changed files with 443 additions and 83 deletions

View file

@ -4,7 +4,7 @@
"commit": false,
"linked": [],
"access": "public",
"baseBranch": "next",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Returns the updated config in the integration `astro:config:setup` hook's `updateConfig()` API

View file

@ -6,6 +6,7 @@ on:
- main
- "1-legacy"
- "2-legacy"
- "3-legacy"
- next
defaults:

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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,
});
}

View file

@ -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;
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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) => {

View file

@ -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,
});

View file

@ -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,
});

View file

@ -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,
};

View file

@ -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
);

View file

@ -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) {

View file

@ -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}`);

View file

@ -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),
}),

View file

@ -25,7 +25,7 @@ export async function renderPage(
componentFactory.name,
componentFactory,
pageProps,
null,
{},
true,
route
);

View file

@ -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];
};

View file

@ -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,
};

View file

@ -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,
});
}

View file

@ -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

View file

@ -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);

View file

@ -0,0 +1,10 @@
<html>
<head>
<!-- Head Stuff -->
</head>
<body>
<slot>
<div id="default"></div>
</slot>
</body>
</html>

View file

@ -7,8 +7,6 @@ import Fallback from '../components/Fallback.astro';
<!-- Head Stuff -->
</head>
<body>
<div id="fallback">
<Fallback />
</div>
<Fallback />
</body>
</html>

View file

@ -8,7 +8,9 @@ export default defineConfig({
locales: [
'en', 'pt', 'it'
],
routingStrategy: "prefix-always"
routing: {
prefixDefaultLocale: true
}
}
}
})

View file

@ -7,7 +7,9 @@ export default defineConfig({
locales: [
'en', 'pt', 'it'
],
routingStrategy: "prefix-always"
routing: {
prefixDefaultLocale: true
}
}
},
base: "/new-site"

View file

@ -7,7 +7,6 @@ export default defineConfig({
locales: [
'en', 'pt', 'it'
],
routingStrategy: "prefix-other-locales"
},
},

View file

@ -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', () => {

View file

@ -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({

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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));

View file

@ -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
);

View file

@ -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),
};

View file

@ -52,6 +52,7 @@ export default function startServer(app: NodeApp, options: Options) {
port,
host,
removeBase: app.removeBase.bind(app),
assets: options.assets,
},
handler
);

View file

@ -15,6 +15,7 @@ export interface Options extends UserOptions {
port: number;
server: string;
client: string;
assets: string;
}
export type RequestHandlerParams = [

View 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');
});
});

View file

@ -0,0 +1 @@
this is a text file

View 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>

View file

@ -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 {

View file

@ -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
```

View file

@ -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) {

View file

@ -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()]);