0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-13 22:11:20 -05:00

Remove deprecated features from Astro 3.0 (#9168)

This commit is contained in:
Bjorn Lu 2023-11-28 00:44:20 +08:00 committed by GitHub
parent c7953645ee
commit 153a5abb90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 95 additions and 398 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/rss': major
---
Removes the `drafts` option as the feature is deprecated in Astro 3.0

View file

@ -0,0 +1,11 @@
---
'astro': major
---
Removes deprecated features from Astro 3.0
- Adapters are now required to pass `supportedAstroFeatures` to specify a list of features they support.
- The `build.split` and `build.excludeMiddleware` options are removed. Use `functionPerRoute` and `edgeMiddleware` from adapters instead.
- The `markdown.drafts` option and draft feature is removed. Use content collections instead.
- Lowercase endpoint names are no longer supported. Use uppercase endpoint names instead.
- `getHeaders()` exported from markdown files is removed. Use `getHeadings()` instead.

View file

@ -6,6 +6,7 @@ export function getAdapter(): AstroAdapter {
serverEntrypoint: '@benchmark/timer/server.js', serverEntrypoint: '@benchmark/timer/server.js',
previewEntrypoint: '@benchmark/timer/preview.js', previewEntrypoint: '@benchmark/timer/preview.js',
exports: ['handler'], exports: ['handler'],
supportedAstroFeatures: {},
}; };
} }

View file

@ -28,7 +28,7 @@ Start by [adding a `site` to your project's `astro.config` for link generation](
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
export async function get(context) { export async function GET(context) {
const posts = await getCollection('blog'); const posts = await getCollection('blog');
return rss({ return rss({
title: 'Buzzs Blog', title: 'Buzzs Blog',
@ -55,7 +55,7 @@ Read **[Astro's RSS docs][astro-rss]** for more on using content collections, an
The `rss` default export offers a number of configuration options. Here's a quick reference: The `rss` default export offers a number of configuration options. Here's a quick reference:
```js ```js
export function get(context) { export function GET(context) {
return rss({ return rss({
// `<title>` field in output xml // `<title>` field in output xml
title: 'Buzzs Blog', title: 'Buzzs Blog',
@ -98,7 +98,7 @@ The base URL to use when generating RSS item links. We recommend using the [endp
```ts ```ts
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
export const get = (context) => export const GET = (context) =>
rss({ rss({
site: context.site, site: context.site,
// ... // ...
@ -113,14 +113,6 @@ A list of formatted RSS feed items. See [Astro's RSS items documentation](https:
When providing a formatted RSS item list, see the [`RSSFeedItem` type reference](#rssfeeditem). When providing a formatted RSS item list, see the [`RSSFeedItem` type reference](#rssfeeditem).
### drafts
Type: `boolean (optional)`
**Deprecated**: Manually filter `items` instead.
Set `drafts: true` to include [draft posts](https://docs.astro.build/en/guides/markdown-content/#draft-pages) in the feed output. By default, this option is `false` and draft posts are not included.
### stylesheet ### stylesheet
Type: `string (optional)` Type: `string (optional)`
@ -136,7 +128,7 @@ A string of valid XML to be injected between your feed's `<description>` and `<i
```js ```js
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
export const get = () => rss({ export const GET = () => rss({
... ...
customData: '<language>en-us</language>', customData: '<language>en-us</language>',
}); });
@ -181,7 +173,7 @@ By default, the library will add trailing slashes to the emitted URLs. To preven
```js ```js
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
export const get = () => export const GET = () =>
rss({ rss({
trailingSlash: false, trailingSlash: false,
}); });
@ -361,7 +353,7 @@ This function assumes, but does not verify, you are globbing for items inside `s
// src/pages/rss.xml.js // src/pages/rss.xml.js
import rss, { pagesGlobToRssItems } from '@astrojs/rss'; import rss, { pagesGlobToRssItems } from '@astrojs/rss';
export async function get(context) { export async function GET(context) {
return rss({ return rss({
title: 'Buzzs Blog', title: 'Buzzs Blog',
description: 'A humble Astronauts guide to the stars', description: 'A humble Astronauts guide to the stars',
@ -379,7 +371,7 @@ As `rss()` returns a `Response`, you can also use `getRssString()` to get the RS
// src/pages/rss.xml.js // src/pages/rss.xml.js
import { getRssString } from '@astrojs/rss'; import { getRssString } from '@astrojs/rss';
export async function get(context) { export async function GET(context) {
const rssString = await getRssString({ const rssString = await getRssString({
title: 'Buzzs Blog', title: 'Buzzs Blog',
... ...

View file

@ -27,11 +27,6 @@ export type RSSOptions = {
stylesheet?: z.infer<typeof rssOptionsValidator>['stylesheet']; stylesheet?: z.infer<typeof rssOptionsValidator>['stylesheet'];
/** Specify custom data in opening of file */ /** Specify custom data in opening of file */
customData?: z.infer<typeof rssOptionsValidator>['customData']; customData?: z.infer<typeof rssOptionsValidator>['customData'];
/**
* Whether to include drafts or not
* @deprecated Deprecated since version 3.0. Use content collections instead.
*/
drafts?: z.infer<typeof rssOptionsValidator>['drafts'];
trailingSlash?: z.infer<typeof rssOptionsValidator>['trailingSlash']; trailingSlash?: z.infer<typeof rssOptionsValidator>['trailingSlash'];
}; };
@ -48,11 +43,6 @@ export type RSSFeedItem = {
description?: z.infer<typeof rssSchema>['description']; description?: z.infer<typeof rssSchema>['description'];
/** Append some other XML-valid data to this item */ /** Append some other XML-valid data to this item */
customData?: z.infer<typeof rssSchema>['customData']; customData?: z.infer<typeof rssSchema>['customData'];
/**
* Whether draft or not
* @deprecated Deprecated since version 3.0. Use content collections instead.
*/
draft?: z.infer<typeof rssSchema>['draft'];
/** Categories or tags related to the item */ /** Categories or tags related to the item */
categories?: z.infer<typeof rssSchema>['categories']; categories?: z.infer<typeof rssSchema>['categories'];
/** The item author's email address */ /** The item author's email address */
@ -92,7 +82,6 @@ const rssOptionsValidator = z.object({
return items; return items;
}), }),
xmlns: z.record(z.string()).optional(), xmlns: z.record(z.string()).optional(),
drafts: z.boolean().default(false),
stylesheet: z.union([z.string(), z.boolean()]).optional(), stylesheet: z.union([z.string(), z.boolean()]).optional(),
customData: z.string().optional(), customData: z.string().optional(),
trailingSlash: z.boolean().default(true), trailingSlash: z.boolean().default(true),
@ -159,10 +148,7 @@ export function pagesGlobToRssItems(items: GlobResult): Promise<ValidatedRSSFeed
/** Generate RSS 2.0 feed */ /** Generate RSS 2.0 feed */
async function generateRSS(rssOptions: ValidatedRSSOptions): Promise<string> { async function generateRSS(rssOptions: ValidatedRSSOptions): Promise<string> {
const { site } = rssOptions; const { items, site } = rssOptions;
const items = rssOptions.drafts
? rssOptions.items
: rssOptions.items.filter((item) => !item.draft);
const xmlOptions = { const xmlOptions = {
ignoreAttributes: false, ignoreAttributes: false,

View file

@ -8,7 +8,6 @@ export const rssSchema = z.object({
.refine((value) => !isNaN(value.getTime())), .refine((value) => !isNaN(value.getTime())),
description: z.string().optional(), description: z.string().optional(),
customData: z.string().optional(), customData: z.string().optional(),
draft: z.boolean().optional(),
categories: z.array(z.string()).optional(), categories: z.array(z.string()).optional(),
author: z.string().optional(), author: z.string().optional(),
commentsUrl: z.string().optional(), commentsUrl: z.string().optional(),

View file

@ -156,36 +156,12 @@ describe('getRssString', () => {
chai.expect(str).to.contain(customData); chai.expect(str).to.contain(customData);
}); });
it('should filter out entries marked as `draft`', async () => {
const str = await getRssString({
title,
description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
site,
});
chai.expect(str).xml.to.equal(validXmlWithoutWeb1FeedResult);
});
it('should respect drafts option', async () => {
const str = await getRssString({
title,
description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
site,
drafts: true,
});
chai.expect(str).xml.to.equal(validXmlResult);
});
it('should not append trailing slash to URLs with the given option', async () => { it('should not append trailing slash to URLs with the given option', async () => {
const str = await getRssString({ const str = await getRssString({
title, title,
description, description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }], items: [phpFeedItem],
site, site,
drafts: true,
trailingSlash: false, trailingSlash: false,
}); });

View file

@ -242,8 +242,6 @@ interface ExportedMarkdownModuleEntities {
file: MD['file']; file: MD['file'];
url: MD['url']; url: MD['url'];
getHeadings: MD['getHeadings']; getHeadings: MD['getHeadings'];
/** @deprecated Renamed to `getHeadings()` */
getHeaders: () => void;
Content: MD['Content']; Content: MD['Content'];
rawContent: MD['rawContent']; rawContent: MD['rawContent'];
compiledContent: MD['compiledContent']; compiledContent: MD['compiledContent'];

View file

@ -143,7 +143,6 @@ export interface CLIFlags {
host?: string | boolean; host?: string | boolean;
port?: number; port?: number;
config?: string; config?: string;
drafts?: boolean;
open?: boolean; open?: boolean;
} }
@ -886,33 +885,6 @@ export interface AstroUserConfig {
* ``` * ```
*/ */
inlineStylesheets?: 'always' | 'auto' | 'never'; inlineStylesheets?: 'always' | 'auto' | 'never';
/**
* @docs
* @name build.split
* @type {boolean}
* @default `false`
* @deprecated Deprecated since version 3.0.
* @description
* The build config option `build.split` has been replaced by the adapter configuration option [`functionPerRoute`](/en/reference/adapter-reference/#functionperroute).
*
* Please see your [SSR adapter's documentation](/en/guides/integrations-guide/#official-integrations) for using `functionPerRoute` to define how your SSR code is bundled.
*
*/
split?: boolean;
/**
* @docs
* @name build.excludeMiddleware
* @type {boolean}
* @default `false`
* @deprecated Deprecated since version 3.0.
* @description
* The build config option `build.excludeMiddleware` has been replaced by the adapter configuration option [`edgeMiddleware`](/en/reference/adapter-reference/#edgemiddleware).
*
* Please see your [SSR adapter's documentation](/en/guides/integrations-guide/#official-integrations) for using `edgeMiddleware` to define whether or not any SSR middleware code will be bundled when built.
*/
excludeMiddleware?: boolean;
}; };
/** /**
@ -1183,28 +1155,6 @@ export interface AstroUserConfig {
* @name Markdown Options * @name Markdown Options
*/ */
markdown?: { markdown?: {
/**
* @docs
* @name markdown.drafts
* @type {boolean}
* @default `false`
* @deprecated Deprecated since version 3.0. Use content collections instead.
* @description
* Control whether Markdown draft pages should be included in the build.
*
* A Markdown page is considered a draft if it includes `draft: true` in its frontmatter. Draft pages are always included & visible during development (`astro dev`) but by default they will not be included in your final build.
*
* ```js
* {
* markdown: {
* // Example: Include all drafts in your final build
* drafts: true,
* }
* }
* ```
*/
drafts?: boolean;
/** /**
* @docs * @docs
* @name markdown.shikiConfig * @name markdown.shikiConfig
@ -1749,10 +1699,6 @@ export interface ComponentInstance {
css?: string[]; css?: string[];
partial?: boolean; partial?: boolean;
prerender?: boolean; prerender?: boolean;
/**
* Only used for logging if deprecated drafts feature is used
*/
frontmatter?: Record<string, any>;
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult; getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
} }
@ -2056,7 +2002,7 @@ export interface AstroAdapter {
* *
* If the adapter is not able to handle certain configurations, Astro will throw an error. * If the adapter is not able to handle certain configurations, Astro will throw an error.
*/ */
supportedAstroFeatures?: AstroFeatureMap; supportedAstroFeatures: AstroFeatureMap;
} }
type Body = string; type Body = string;
@ -2145,7 +2091,7 @@ export interface APIContext<
* ]; * ];
* } * }
* *
* export async function get({ params }) { * export async function GET({ params }) {
* return { * return {
* body: `Hello user ${params.id}!`, * body: `Hello user ${params.id}!`,
* } * }
@ -2168,7 +2114,7 @@ export interface APIContext<
* ]; * ];
* } * }
* *
* export function get({ props }) { * export function GET({ props }) {
* return { * return {
* body: `Hello ${props.name}!`, * body: `Hello ${props.name}!`,
* } * }
@ -2184,7 +2130,7 @@ export interface APIContext<
* Example usage: * Example usage:
* ```ts * ```ts
* // src/pages/secret.ts * // src/pages/secret.ts
* export function get({ redirect }) { * export function GET({ redirect }) {
* return redirect('/login'); * return redirect('/login');
* } * }
* ``` * ```

View file

@ -14,7 +14,6 @@ export async function build({ flags }: BuildOptions) {
usage: '[...flags]', usage: '[...flags]',
tables: { tables: {
Flags: [ Flags: [
['--drafts', `Include Markdown draft pages in the build.`],
['--outDir <directory>', `Specify the output directory for the build.`], ['--outDir <directory>', `Specify the output directory for the build.`],
['--help (-h)', 'See all available flags.'], ['--help (-h)', 'See all available flags.'],
], ],

View file

@ -15,9 +15,6 @@ export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig {
site: typeof flags.site === 'string' ? flags.site : undefined, site: typeof flags.site === 'string' ? flags.site : undefined,
base: typeof flags.base === 'string' ? flags.base : undefined, base: typeof flags.base === 'string' ? flags.base : undefined,
outDir: typeof flags.outDir === 'string' ? flags.outDir : undefined, outDir: typeof flags.outDir === 'string' ? flags.outDir : undefined,
markdown: {
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : undefined,
},
server: { server: {
port: typeof flags.port === 'number' ? flags.port : undefined, port: typeof flags.port === 'number' ? flags.port : undefined,
host: host:

View file

@ -111,16 +111,6 @@ async function getEntryForFallbackRoute(
return RedirectSinglePageBuiltModule; return RedirectSinglePageBuiltModule;
} }
function shouldSkipDraft(pageModule: ComponentInstance, settings: AstroSettings): boolean {
return (
// Drafts are disabled
!settings.config.markdown.drafts &&
// This is a draft post
'frontmatter' in pageModule &&
(pageModule as any).frontmatter?.draft === true
);
}
// Gives back a facadeId that is relative to the root. // Gives back a facadeId that is relative to the root.
// ie, src/pages/index.astro instead of /Users/name..../src/pages/index.astro // ie, src/pages/index.astro instead of /Users/name..../src/pages/index.astro
export function rootRelativeFacadeId(facadeId: string, settings: AstroSettings): string { export function rootRelativeFacadeId(facadeId: string, settings: AstroSettings): string {
@ -185,11 +175,7 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
if (pageData.route.prerender) { if (pageData.route.prerender) {
const ssrEntryURLPage = createEntryURL(filePath, outFolder); const ssrEntryURLPage = createEntryURL(filePath, outFolder);
const ssrEntryPage = await import(ssrEntryURLPage.toString()); const ssrEntryPage = await import(ssrEntryURLPage.toString());
if ( if (opts.settings.adapter?.adapterFeatures?.functionPerRoute) {
// TODO: remove in Astro 4.0
opts.settings.config.build.split ||
opts.settings.adapter?.adapterFeatures?.functionPerRoute
) {
// forcing to use undefined, so we fail in an expected way if the module is not even there. // forcing to use undefined, so we fail in an expected way if the module is not even there.
const ssrEntry = ssrEntryPage?.pageModule; const ssrEntry = ssrEntryPage?.pageModule;
if (ssrEntry) { if (ssrEntry) {
@ -244,10 +230,7 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
await queue.onIdle(); await queue.onIdle();
const assetsTimeEnd = performance.now(); const assetsTimeEnd = performance.now();
logger.info( logger.info(null, green(`✓ Completed in ${getTimeStat(assetsTimer, assetsTimeEnd)}.\n`));
null,
green(`✓ Completed in ${getTimeStat(assetsTimer, assetsTimeEnd)}.\n`)
);
delete globalThis?.astroAsset?.addStaticImage; delete globalThis?.astroAsset?.addStaticImage;
} }
@ -302,16 +285,6 @@ async function generatePage(
); );
} }
const pageModule = await pageModulePromise(); const pageModule = await pageModulePromise();
// TODO: Remove in Astro 4.0
if (shouldSkipDraft(pageModule, pipeline.getSettings())) {
logger.info(null, `${magenta('⚠️')} Skipping draft ${pageData.route.component}`);
logger.warn(
null,
`The drafts feature is deprecated. You should migrate to content collections instead. See https://docs.astro.build/en/guides/content-collections/#filtering-collection-queries for more information.`
);
return;
}
const generationOptions: Readonly<GeneratePathOptions> = { const generationOptions: Readonly<GeneratePathOptions> = {
pageData, pageData,
linkIds, linkIds,
@ -390,13 +363,13 @@ async function getPathsForRoute(
throw err; throw err;
}); });
const label = staticPaths.length === 1 ? 'page' : 'pages'; const label = staticPaths.length === 1 ? 'page' : 'pages';
logger.debug( logger.debug(
'build', 'build',
`├── ${bold(green('✔'))} ${route.component}${magenta( `├── ${bold(green('✔'))} ${route.component}${magenta(
`[${staticPaths.length} ${label}]` `[${staticPaths.length} ${label}]`
)}` )}`
); );
paths.push( paths.push(
...staticPaths ...staticPaths

View file

@ -161,7 +161,7 @@ class AstroBuilder {
this.logger.info('build', `output: ${blue('"' + this.settings.config.output + '"')}`); this.logger.info('build', `output: ${blue('"' + this.settings.config.output + '"')}`);
this.logger.info('build', `directory: ${blue(fileURLToPath(this.settings.config.outDir))}`); this.logger.info('build', `directory: ${blue(fileURLToPath(this.settings.config.outDir))}`);
if (this.settings.adapter) { if (this.settings.adapter) {
this.logger.info('build', `adapter: ${green(this.settings.adapter.name)}`); this.logger.info('build', `adapter: ${green(this.settings.adapter.name)}`);
} }
this.logger.info('build', 'Collecting build info...'); this.logger.info('build', 'Collecting build info...');
this.timer.loadStart = performance.now(); this.timer.loadStart = performance.now();
@ -253,32 +253,6 @@ class AstroBuilder {
`the outDir cannot be the root folder. Please build to a folder such as dist.` `the outDir cannot be the root folder. Please build to a folder such as dist.`
); );
} }
// TODO: Remove in Astro 4.0
if (config.build.split === true) {
if (config.output === 'static') {
this.logger.warn(
'config',
'The option `build.split` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.'
);
}
this.logger.warn(
'deprecated',
'The option `build.split` is deprecated. Use the adapter options.'
);
}
if (config.build.excludeMiddleware === true) {
if (config.output === 'static') {
this.logger.warn(
'config',
'The option `build.excludeMiddleware` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.'
);
}
this.logger.warn(
'deprecated',
'The option `build.excludeMiddleware` is deprecated. Use the adapter options.'
);
}
} }
/** Stats */ /** Stats */

View file

@ -98,8 +98,6 @@ export function pluginManifest(
const manifest = await createManifest(options, internals); const manifest = await createManifest(options, internals);
const shouldPassMiddlewareEntryPoint = const shouldPassMiddlewareEntryPoint =
// TODO: remove in Astro 4.0
options.settings.config.build.excludeMiddleware ||
options.settings.adapter?.adapterFeatures?.edgeMiddleware; options.settings.adapter?.adapterFeatures?.edgeMiddleware;
await runHookBuildSsr({ await runHookBuildSsr({
config: options.settings.config, config: options.settings.config,

View file

@ -92,10 +92,6 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
} }
export function shouldBundleMiddleware(settings: AstroSettings) { export function shouldBundleMiddleware(settings: AstroSettings) {
// TODO: Remove in Astro 4.0
if (settings.config.build.excludeMiddleware === true) {
return false;
}
if (settings.adapter?.adapterFeatures?.edgeMiddleware === true) { if (settings.adapter?.adapterFeatures?.edgeMiddleware === true) {
return false; return false;
} }

View file

@ -102,10 +102,7 @@ export function pluginSSR(
hooks: { hooks: {
'build:before': () => { 'build:before': () => {
let vitePlugin = let vitePlugin =
ssr && ssr && functionPerRouteEnabled === false
// TODO: Remove in Astro 4.0
options.settings.config.build.split === false &&
functionPerRouteEnabled === false
? vitePluginSSR(internals, options.settings.adapter!, options) ? vitePluginSSR(internals, options.settings.adapter!, options)
: undefined; : undefined;
@ -119,7 +116,7 @@ export function pluginSSR(
return; return;
} }
if (options.settings.config.build.split || functionPerRouteEnabled) { if (functionPerRouteEnabled) {
return; return;
} }
@ -146,7 +143,7 @@ function vitePluginSSRSplit(
name: '@astrojs/vite-plugin-astro-ssr-split', name: '@astrojs/vite-plugin-astro-ssr-split',
enforce: 'post', enforce: 'post',
options(opts) { options(opts) {
if (options.settings.config.build.split || functionPerRouteEnabled) { if (functionPerRouteEnabled) {
const inputs = new Set<string>(); const inputs = new Set<string>();
for (const [path, pageData] of eachPageFromAllPages(options.allPages)) { for (const [path, pageData] of eachPageFromAllPages(options.allPages)) {
@ -223,7 +220,7 @@ export function pluginSSRSplit(
hooks: { hooks: {
'build:before': () => { 'build:before': () => {
let vitePlugin = let vitePlugin =
ssr && (options.settings.config.build.split || functionPerRouteEnabled) ssr && functionPerRouteEnabled
? vitePluginSSRSplit(internals, options.settings.adapter!, options) ? vitePluginSSRSplit(internals, options.settings.adapter!, options)
: undefined; : undefined;
@ -240,7 +237,7 @@ function generateSSRCode(config: AstroConfig, adapter: AstroAdapter) {
const imports: string[] = []; const imports: string[] = [];
const contents: string[] = []; const contents: string[] = [];
let pageMap; let pageMap;
if (config.build.split || isFunctionPerRouteEnabled(adapter)) { if (isFunctionPerRouteEnabled(adapter)) {
pageMap = 'pageModule'; pageMap = 'pageModule';
} else { } else {
pageMap = 'pageMap'; pageMap = 'pageMap';

View file

@ -67,7 +67,6 @@ export function resolveFlags(flags: Partial<Flags>): CLIFlags {
config: typeof flags.config === 'string' ? flags.config : undefined, config: typeof flags.config === 'string' ? flags.config : undefined,
host: host:
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined, typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : undefined,
}; };
} }

View file

@ -38,8 +38,6 @@ const ASTRO_CONFIG_DEFAULTS = {
serverEntry: 'entry.mjs', serverEntry: 'entry.mjs',
redirects: true, redirects: true,
inlineStylesheets: 'auto', inlineStylesheets: 'auto',
split: false,
excludeMiddleware: false,
}, },
image: { image: {
service: { entrypoint: 'astro/assets/services/sharp', config: {} }, service: { entrypoint: 'astro/assets/services/sharp', config: {} },
@ -51,10 +49,7 @@ const ASTRO_CONFIG_DEFAULTS = {
open: false, open: false,
}, },
integrations: [], integrations: [],
markdown: { markdown: markdownConfigDefaults,
drafts: false,
...markdownConfigDefaults,
},
vite: {}, vite: {},
legacy: {}, legacy: {},
redirects: {}, redirects: {},
@ -139,20 +134,6 @@ export const AstroConfigSchema = z.object({
.enum(['always', 'auto', 'never']) .enum(['always', 'auto', 'never'])
.optional() .optional()
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets), .default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
/**
* @deprecated
* Use the adapter feature instead
*/
split: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.split),
/**
* @deprecated
* Use the adapter feature instead
*/
excludeMiddleware: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware),
}) })
.default({}), .default({}),
server: z.preprocess( server: z.preprocess(
@ -245,7 +226,6 @@ export const AstroConfigSchema = z.object({
.default(ASTRO_CONFIG_DEFAULTS.image), .default(ASTRO_CONFIG_DEFAULTS.image),
markdown: z markdown: z
.object({ .object({
drafts: z.boolean().default(false),
syntaxHighlight: z syntaxHighlight: z
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)]) .union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
.default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight), .default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
@ -452,12 +432,6 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
.enum(['always', 'auto', 'never']) .enum(['always', 'auto', 'never'])
.optional() .optional()
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets), .default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
split: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.split),
excludeMiddleware: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware),
}) })
.optional() .optional()
.default({}), .default({}),

View file

@ -64,14 +64,6 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag
routingStrategy: renderContext.routingStrategy, routingStrategy: renderContext.routingStrategy,
}); });
// TODO: Remove in Astro 4.0
if (mod.frontmatter && typeof mod.frontmatter === 'object' && 'draft' in mod.frontmatter) {
env.logger.warn(
null,
`The drafts feature is deprecated and used in ${renderContext.route.component}. You should migrate to content collections instead. See https://docs.astro.build/en/guides/content-collections/#filtering-collection-queries for more information.`
);
}
const response = await runtimeRenderPage( const response = await runtimeRenderPage(
result, result,
Component, Component,

View file

@ -17,17 +17,6 @@ const UNSUPPORTED_ASSETS_FEATURE: AstroAssetsFeature = {
isSharpCompatible: false, isSharpCompatible: false,
}; };
// NOTE: remove for Astro 4.0
const ALL_UNSUPPORTED: Required<AstroFeatureMap> = {
serverOutput: UNSUPPORTED,
staticOutput: UNSUPPORTED,
hybridOutput: UNSUPPORTED,
assets: UNSUPPORTED_ASSETS_FEATURE,
i18n: {
detectBrowserLanguage: UNSUPPORTED,
},
};
type ValidationResult = { type ValidationResult = {
[Property in keyof AstroFeatureMap]: boolean; [Property in keyof AstroFeatureMap]: boolean;
}; };
@ -41,7 +30,7 @@ type ValidationResult = {
*/ */
export function validateSupportedFeatures( export function validateSupportedFeatures(
adapterName: string, adapterName: string,
featureMap: AstroFeatureMap = ALL_UNSUPPORTED, featureMap: AstroFeatureMap,
config: AstroConfig, config: AstroConfig,
logger: Logger logger: Logger
): ValidationResult { ): ValidationResult {
@ -105,18 +94,21 @@ function validateSupportKind(
} }
function featureIsUnsupported(adapterName: string, logger: Logger, featureName: string) { function featureIsUnsupported(adapterName: string, logger: Logger, featureName: string) {
logger.error( logger.error('config', `The feature ${featureName} is not supported (used by ${adapterName}).`);
'config',
`The feature ${featureName} is not supported (used by ${adapterName}).`
);
} }
function featureIsExperimental(adapterName: string, logger: Logger) { function featureIsExperimental(adapterName: string, logger: Logger) {
logger.warn('config', `The feature is experimental and subject to change (used by ${adapterName}).`); logger.warn(
'config',
`The feature is experimental and subject to change (used by ${adapterName}).`
);
} }
function featureIsDeprecated(adapterName: string, logger: Logger) { function featureIsDeprecated(adapterName: string, logger: Logger) {
logger.warn('config', `The feature is deprecated and will be removed in the future (used by ${adapterName}).`); logger.warn(
'config',
`The feature is deprecated and will be removed in the future (used by ${adapterName}).`
);
} }
const SHARP_SERVICE = 'astro/assets/services/sharp'; const SHARP_SERVICE = 'astro/assets/services/sharp';

View file

@ -244,10 +244,8 @@ export async function runHookConfigDone({
); );
} }
if (!adapter.supportedAstroFeatures) { if (!adapter.supportedAstroFeatures) {
// NOTE: throw an error in Astro 4.0 throw new Error(
logger.warn( `The adapter ${adapter.name} doesn't provide a feature map. It is required in Astro 4.0.`
null,
`The adapter ${adapter.name} doesn't provide a feature map. From Astro 3.0, an adapter can provide a feature map. Not providing a feature map will cause an error in Astro 4.0.`
); );
} else { } else {
const validationResult = validateSupportedFeatures( const validationResult = validateSupportedFeatures(

View file

@ -2,35 +2,11 @@ import { bold } from 'kleur/colors';
import type { APIContext, EndpointHandler, Params } from '../../@types/astro.js'; import type { APIContext, EndpointHandler, Params } from '../../@types/astro.js';
import type { Logger } from '../../core/logger/core.js'; import type { Logger } from '../../core/logger/core.js';
function getHandlerFromModule(mod: EndpointHandler, method: string, logger: Logger) { function getHandlerFromModule(mod: EndpointHandler, method: string) {
const lowerCaseMethod = method.toLowerCase();
// TODO: remove in Astro 4.0
if (mod[lowerCaseMethod]) {
logger.warn(
null,
`Lower case endpoint names are deprecated and will not be supported in Astro 4.0. Rename the endpoint ${lowerCaseMethod} to ${method}.`
);
}
// If there was an exact match on `method`, return that function. // If there was an exact match on `method`, return that function.
if (mod[method]) { if (mod[method]) {
return mod[method]; return mod[method];
} }
// TODO: remove in Astro 4.0
if (mod[lowerCaseMethod]) {
return mod[lowerCaseMethod];
}
// TODO: remove in Astro 4.0
// Handle `del` instead of `delete`, since `delete` is a reserved word in JS.
if (method === 'delete' && mod['del']) {
return mod['del'];
}
// TODO: remove in Astro 4.0
// If a single `all` handler was used, return that function.
if (mod['all']) {
return mod['all'];
}
if (mod['ALL']) { if (mod['ALL']) {
return mod['ALL']; return mod['ALL'];
} }
@ -48,9 +24,8 @@ export async function renderEndpoint(
const { request, url } = context; const { request, url } = context;
const chosenMethod = request.method?.toUpperCase(); const chosenMethod = request.method?.toUpperCase();
const handler = getHandlerFromModule(mod, chosenMethod, logger); const handler = getHandlerFromModule(mod, chosenMethod);
// TODO: remove the 'get' check in Astro 4.0 if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET') {
if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET' && chosenMethod !== 'get') {
logger.warn( logger.warn(
null, null,
`${url.pathname} ${bold( `${url.pathname} ${bold(

View file

@ -1,44 +0,0 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Astro Markdown with draft posts disabled', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown-drafts/',
});
await fixture.build();
});
it('Does not render the draft post', async () => {
let renderedDraft = false;
try {
await fixture.readFile('/wip/index.html');
renderedDraft = true;
} catch (err) {
expect(err.code).to.equal('ENOENT');
}
expect(renderedDraft).to.equal(false, 'Rendered a draft post');
});
});
describe('Astro Markdown with draft posts enabled', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-markdown-drafts/',
markdown: {
drafts: true,
},
});
await fixture.build();
});
it('Renders the draft post', async () => {
const html = await fixture.readFile('/wip/index.html');
const $ = cheerio.load(html);
expect($('h1').length).to.be.ok;
expect($('h1').text()).to.equal('WIP');
});
});

View file

@ -105,7 +105,6 @@ describe('Events', () => {
config: 'path/to/config.mjs', config: 'path/to/config.mjs',
experimentalSsr: true, experimentalSsr: true,
experimentalIntegrations: true, experimentalIntegrations: true,
drafts: true,
}; };
const [{ payload }] = events.eventCliSession( const [{ payload }] = events.eventCliSession(
{ {
@ -122,7 +121,6 @@ describe('Events', () => {
'config', 'config',
'experimentalSsr', 'experimentalSsr',
'experimentalIntegrations', 'experimentalIntegrations',
'drafts',
]); ]);
}); });
}); });

View file

@ -10,14 +10,9 @@ describe('Adapter', () => {
fixture = await loadFixture({ fixture = await loadFixture({
root: './fixtures/middleware space/', root: './fixtures/middleware space/',
output: 'server', output: 'server',
build: {
excludeMiddleware: true,
},
adapter: testAdapter({ adapter: testAdapter({
extendAdapter: { extendAdapter: {
supportsFeatures: { supportedAstroFeatures: {},
edgeMiddleware: 'Unsupported',
},
}, },
}), }),
}); });
@ -34,14 +29,9 @@ describe('Adapter', () => {
fixture = await loadFixture({ fixture = await loadFixture({
root: './fixtures/middleware space/', root: './fixtures/middleware space/',
output: 'server', output: 'server',
build: {
split: true,
},
adapter: testAdapter({ adapter: testAdapter({
extendAdapter: { extendAdapter: {
supportsFeatures: { supportedAstroFeatures: {},
functionPerPage: 'Unsupported',
},
}, },
}), }),
}); });

View file

@ -1,8 +0,0 @@
{
"name": "@test/astro-markdown-drafts",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}

View file

@ -1,7 +0,0 @@
---
foo: bar
---
# Hello world
This should be visible.

View file

@ -1,8 +0,0 @@
---
foo: bar
draft: true
---
# WIP
This is a draft. Don't build me!

View file

@ -21,7 +21,7 @@ This should also work outside of code blocks:
// src/pages/rss.xml.js // src/pages/rss.xml.js
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
export const get = () => rss({ export const GET = () => rss({
// Use Vite env vars with import.meta.env // Use Vite env vars with import.meta.env
site: import.meta.env.SITE, site: import.meta.env.SITE,
title: import.meta.env.TITLE, title: import.meta.env.TITLE,

View file

@ -4,7 +4,7 @@ export function GET() {
return new Response('ok') return new Response('ok')
} }
export async function post({ request }) { export async function POST({ request }) {
const data = await request.formData(); const data = await request.formData();
const file = data.get('file'); const file = data.get('file');

View file

@ -1,4 +1,4 @@
export const get = () => { export const GET = () => {
return new Response( return new Response(
undefined, undefined,
{ {

View file

@ -277,6 +277,11 @@ describe('Middleware API in PROD mode, SSR', () => {
excludeMiddleware: true, excludeMiddleware: true,
}, },
adapter: testAdapter({ adapter: testAdapter({
extendAdapter: {
adapterFeatures: {
edgeMiddleware: true,
},
},
setMiddlewareEntryPoint(entryPointsOrMiddleware) { setMiddlewareEntryPoint(entryPointsOrMiddleware) {
middlewarePath = entryPointsOrMiddleware; middlewarePath = entryPointsOrMiddleware;
}, },
@ -317,7 +322,9 @@ describe('Middleware with tailwind', () => {
}); });
}); });
describe('Middleware, split middleware option', () => { // `loadTestAdapterApp()` does not understand how to load the page with `functionPerRoute`
// since there's no `entry.mjs`. Skip for now.
describe.skip('Middleware supports functionPerRoute feature', () => {
/** @type {import('./test-utils').Fixture} */ /** @type {import('./test-utils').Fixture} */
let fixture; let fixture;
@ -325,10 +332,13 @@ describe('Middleware, split middleware option', () => {
fixture = await loadFixture({ fixture = await loadFixture({
root: './fixtures/middleware space/', root: './fixtures/middleware space/',
output: 'server', output: 'server',
build: { adapter: testAdapter({
excludeMiddleware: true, extendAdapter: {
}, adapterFeatures: {
adapter: testAdapter({}), functionPerRoute: true,
},
},
}),
}); });
await fixture.build(); await fixture.build();
}); });

View file

@ -47,6 +47,7 @@ describe('Integration buildConfig hook', () => {
name: 'my-ssr-adapter', name: 'my-ssr-adapter',
serverEntrypoint: '@my-ssr', serverEntrypoint: '@my-ssr',
exports: ['manifest', 'createApp'], exports: ['manifest', 'createApp'],
supportedAstroFeatures: {},
}); });
}, },
}, },

View file

@ -77,15 +77,7 @@ export default function (
name: 'my-ssr-adapter', name: 'my-ssr-adapter',
serverEntrypoint: '@my-ssr', serverEntrypoint: '@my-ssr',
exports: ['manifest', 'createApp'], exports: ['manifest', 'createApp'],
supportedFeatures: { supportedAstroFeatures: {},
assets: {
supportKind: 'Stable',
isNodeCompatible: true,
},
serverOutput: 'Stable',
staticOutput: 'Stable',
hybridOutput: 'Stable',
},
...extendAdapter, ...extendAdapter,
}); });
}, },

View file

@ -106,7 +106,7 @@ describe('Astro feature map', function () {
it('should not support the feature when not provided', () => { it('should not support the feature when not provided', () => {
let result = validateSupportedFeatures( let result = validateSupportedFeatures(
'test', 'test',
undefined, {},
{ {
output: 'hybrid', output: 'hybrid',
}, },

View file

@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
import { stringify } from 'devalue'; import { stringify } from 'devalue';
import { stripAllRenderFn } from '../../utils.js'; import { stripAllRenderFn } from '../../utils.js';
export async function get() { export async function GET() {
const posts = await getCollection('blog'); const posts = await getCollection('blog');
return { return {
body: stringify(stripAllRenderFn(posts)) body: stringify(stripAllRenderFn(posts))

View file

@ -2,7 +2,7 @@ import { getEntryBySlug } from 'astro:content';
import { stringify } from 'devalue'; import { stringify } from 'devalue';
import { stripRenderFn } from '../../utils.js'; import { stripRenderFn } from '../../utils.js';
export async function get() { export async function GET() {
const post = await getEntryBySlug('blog', 'post-1'); const post = await getEntryBySlug('blog', 'post-1');
return { return {
body: stringify(stripRenderFn(post)), body: stringify(stripRenderFn(post)),

View file

@ -83,11 +83,7 @@ You can configure how your MDX is rendered with the following options:
### Options inherited from Markdown config ### Options inherited from Markdown config
All [`markdown` configuration options](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) except `drafts` can be configured separately in the MDX integration. This includes remark and rehype plugins, syntax highlighting, and more. Options will default to those in your Markdown config ([see the `extendMarkdownConfig` option](#extendmarkdownconfig) to modify this). All [`markdown` configuration options](https://docs.astro.build/en/reference/configuration-reference/#markdown-options) can be configured separately in the MDX integration. This includes remark and rehype plugins, syntax highlighting, and more. Options will default to those in your Markdown config ([see the `extendMarkdownConfig` option](#extendmarkdownconfig) to modify this).
:::note
There is no separate MDX configuration for [including pages marked as draft in the build](https://docs.astro.build/en/reference/configuration-reference/#markdowndrafts). This Markdown setting will be respected by both Markdown and MDX files and cannot be overridden for MDX files specifically.
:::
```js ```js
// astro.config.mjs // astro.config.mjs

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
const docs = await import.meta.glob('./*.mdx', { eager: true }); const docs = await import.meta.glob('./*.mdx', { eager: true });
return { return {
body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)), body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
const mdxPages = await import.meta.glob('./*.mdx', { eager: true }); const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
return { return {

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
const mdxPages = await import.meta.glob('./*.mdx', { eager: true }); const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
return { return {

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
const mdxPages = await import.meta.glob('./*.mdx', { eager: true }); const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
return { return {

View file

@ -1,6 +1,6 @@
import { frontmatter } from './vite-env-vars.mdx'; import { frontmatter } from './vite-env-vars.mdx';
export function get() { export function GET() {
return { return {
body: JSON.stringify(frontmatter), body: JSON.stringify(frontmatter),
} }

View file

@ -1,4 +1,4 @@
export async function get() { export async function GET() {
let number = Math.random(); let number = Math.random();
return { return {
body: JSON.stringify({ body: JSON.stringify({

View file

@ -1,4 +1,4 @@
export async function get({}) { export async function GET({}) {
return { return {
body: JSON.stringify({ body: JSON.stringify({
name: 'Astro', name: 'Astro',

View file

@ -29,7 +29,7 @@ export { remarkShiki } from './remark-shiki.js';
export { createShikiHighlighter, replaceCssVariables, type ShikiHighlighter } from './shiki.js'; export { createShikiHighlighter, replaceCssVariables, type ShikiHighlighter } from './shiki.js';
export * from './types.js'; export * from './types.js';
export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'drafts'> = { export const markdownConfigDefaults: Required<AstroMarkdownOptions> = {
syntaxHighlight: 'shiki', syntaxHighlight: 'shiki',
shikiConfig: { shikiConfig: {
langs: [], langs: [],

View file

@ -40,7 +40,6 @@ export interface ShikiConfig {
} }
export interface AstroMarkdownOptions { export interface AstroMarkdownOptions {
drafts?: boolean;
syntaxHighlight?: 'shiki' | 'prism' | false; syntaxHighlight?: 'shiki' | 'prism' | false;
shikiConfig?: ShikiConfig; shikiConfig?: ShikiConfig;
remarkPlugins?: RemarkPlugins; remarkPlugins?: RemarkPlugins;