diff options
Diffstat (limited to 'packages/astro')
133 files changed, 1363 insertions, 1291 deletions
diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 5f674acef..62fa23502 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,224 @@ # astro +## 3.0.0-beta.0 + +### Major Changes + +- [`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Remove support for Node 16. The lowest supported version by Astro and all integrations is now v18.14.1. As a reminder, Node 16 will be deprecated on the 11th September 2023. + +- [`76ddef19c`](https://github.com/withastro/astro/commit/76ddef19ccab6e5f7d3a5740cd41acf10e334b38) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Removed automatic flattening of `getStaticPaths` result. `.flatMap` and `.flat` should now be used to ensure that you're returning a flat array. + +- [`3fdf509b2`](https://github.com/withastro/astro/commit/3fdf509b2731a9b2f972d89291e57cf78d62c769) Thanks [@ematipico](https://github.com/ematipico)! - The `build.split` and `build.excludeMiddleware` configuration options are deprecated and have been replaced by options in the adapter config. + + If your config includes the `build.excludeMiddleware` option, replace it with `edgeMiddleware` in your adapter options: + + ```diff + import { defineConfig } from "astro/config"; + import netlify from "@astrojs/netlify/functions"; + + export default defineConfig({ + build: { + - excludeMiddleware: true + }, + adapter: netlify({ + + edgeMiddleware: true + }), + }); + ``` + + If your config includes the `build.split` option, replace it with `functionPerRoute` in your adapter options: + + ```diff + import { defineConfig } from "astro/config"; + import netlify from "@astrojs/netlify/functions"; + + export default defineConfig({ + build: { + - split: true + }, + adapter: netlify({ + + functionPerRoute: true + }), + }); + ``` + +- [`2f951cd40`](https://github.com/withastro/astro/commit/2f951cd403dfcc2c3ca6aae618ae3e1409516e32) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Sharp is now the default image service used for `astro:assets`. If you would prefer to still use Squoosh, you can update your config with the following: + + ```ts + import { defineConfig, squooshImageService } from 'astro/config'; + + // https://astro.build/config + export default defineConfig({ + image: { + service: squooshImageService(), + }, + }); + ``` + + However, not only do we recommend using Sharp as it is faster and more reliable, it is also highly likely that the Squoosh service will be removed in a future release. + +- [`c022a4217`](https://github.com/withastro/astro/commit/c022a4217a805d223c1494e9eda4e48bbf810388) Thanks [@Princesseuh](https://github.com/Princesseuh)! - When using an adapter that supports neither Squoosh or Sharp, Astro will now automatically use an image service that does not support processing, but still provides the other benefits of `astro:assets` such as enforcing `alt`, no CLS etc to users + +- [`67becaa58`](https://github.com/withastro/astro/commit/67becaa580b8f787df58de66b7008b7098f1209c) Thanks [@ematipico](https://github.com/ematipico)! - Removed support for old syntax of the API routes. + +- [`dfc2d93e3`](https://github.com/withastro/astro/commit/dfc2d93e3c645995379358fabbdfa9aab99f43d8) Thanks [@bluwy](https://github.com/bluwy)! - Remove MDX plugin re-ordering hack + +- [`3dc1ca2fa`](https://github.com/withastro/astro/commit/3dc1ca2fac8d9965cc5085a5d09e72ed87b4281a) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Reduced the amount of polyfills provided by Astro. Astro will no longer provide (no-op) polyfills for several web apis such as HTMLElement, Image or Document. If you need access to those APIs on the server, we recommend using more proper polyfills available on npm. + +- [`1be84dfee`](https://github.com/withastro/astro/commit/1be84dfee3ce8e6f5cc624f99aec4e980f6fde37) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Update `tsconfig.json` presets with `moduleResolution: 'bundler'` and other new options from TypeScript 5.0. Astro now assumes that you use TypeScript 5.0 (March 2023), or that your editor includes it, ex: VS Code 1.77 + +- [`35f01df79`](https://github.com/withastro/astro/commit/35f01df797d23315f2bee2fc3fd795adb0559c58) Thanks [@Princesseuh](https://github.com/Princesseuh)! - The `astro check` command now requires an external package `@astrojs/check` and an install of `typescript` in your project. This was done in order to make the main `astro` package smaller and give more flexibility to users in regard to the version of TypeScript they use. + +- [`3fdf509b2`](https://github.com/withastro/astro/commit/3fdf509b2731a9b2f972d89291e57cf78d62c769) Thanks [@ematipico](https://github.com/ematipico)! - The `build.split` and `build.excludeMiddleware` configuration options are deprecated and have been replaced by options in the adapter config. + + If your config includes the `build.excludeMiddleware` option, replace it with `edgeMiddleware` in your adapter options: + + ```diff + import { defineConfig } from "astro/config"; + import vercel from "@astrojs/vercel/serverless"; + + export default defineConfig({ + build: { + - excludeMiddleware: true + }, + adapter: vercel({ + + edgeMiddleware: true + }), + }); + ``` + + If your config includes the `build.split` option, replace it with `functionPerRoute` in your adapter options: + + ```diff + import { defineConfig } from "astro/config"; + import vercel from "@astrojs/vercel/serverless"; + + export default defineConfig({ + build: { + - split: true + }, + adapter: vercel({ + + functionPerRoute: true + }), + }); + ``` + +- [`78de801f2`](https://github.com/withastro/astro/commit/78de801f21fd4ca1653950027d953bf08614566b) Thanks [@ematipico](https://github.com/ematipico)! - Lowercase names for endpoint functions are now deprecated. + + Rename functions to their uppercase equivalent: + + ```diff + - export function get() { + + export function GET() { + return new Response(JSON.stringify({ "title": "Bob's blog" })); + } + + - export function post() { + + export function POST() { + return new Response(JSON.stringify({ "title": "Bob's blog" })); + } + + - export function put() { + + export function PUT() { + return new Response(JSON.stringify({ "title": "Bob's blog" })); + } + + - export function all() { + + export function ALL() { + return new Response(JSON.stringify({ "title": "Bob's blog" })); + } + + // you can use the whole word "DELETE" + - export function del() { + + export function DELETE() { + return new Response(JSON.stringify({ "title": "Bob's blog" })); + } + ``` + +- [`59d6e569f`](https://github.com/withastro/astro/commit/59d6e569f63e175c97e82e94aa7974febfb76f7c) Thanks [@matthewp](https://github.com/matthewp)! - Astro.cookies.get(key) returns undefined if cookie doesn't exist + + With this change, Astro.cookies.get(key) no longer always returns a `AstroCookie` object. Instead it now returns `undefined` if the cookie does not exist. + + You should update your code if you assume that all calls to `get()` return a value. When using with `has()` you still need to assert the value, like so: + + ```astro + --- + if (Astro.cookies.has(id)) { + const id = Astro.cookies.get(id)!; + } + --- + ``` + +- [`7723c4cc9`](https://github.com/withastro/astro/commit/7723c4cc93298c2e6530e55da7afda048f22cf81) Thanks [@ematipico](https://github.com/ematipico)! - The property `compressHTML` is now `true` by default. Setting this value to `true` is no longer required. + + If you do not want to minify your HTML output, you must set this value to `false` in `astro.config.mjs`. + + ```diff + import {defineConfig} from "astro/config"; + export default defineConfig({ + + compressHTML: false + }) + ``` + +- [`fb5cd6b56`](https://github.com/withastro/astro/commit/fb5cd6b56dc27a71366ed5e1ab8bfe9b8f96bac5) Thanks [@ematipico](https://github.com/ematipico)! - Astro's default port when running the dev or preview server is now `4321`. + + This will reduce conflicts with ports used by other tools. + +- [`631b9c410`](https://github.com/withastro/astro/commit/631b9c410d5d66fa384674027ba95d69ebb5063f) Thanks [@bluwy](https://github.com/bluwy)! - Remove MDX special `components` export handling + +### Minor Changes + +- [`9b4f70a62`](https://github.com/withastro/astro/commit/9b4f70a629f55e461759ba46f68af7097a2e9215) Thanks [@ematipico](https://github.com/ematipico)! - Introduced the concept of feature map. A feature map is a list of features that are built-in in Astro, and an Adapter + can tell Astro if it can support it. + + ```ts + import { AstroIntegration } from './astro'; + + function myIntegration(): AstroIntegration { + return { + name: 'astro-awesome-list', + // new feature map + supportedAstroFeatures: { + hybridOutput: 'experimental', + staticOutput: 'stable', + serverOutput: 'stable', + assets: { + supportKind: 'stable', + isSharpCompatible: false, + isSquooshCompatible: false, + }, + }, + }; + } + ``` + +- [`bc37331d8`](https://github.com/withastro/astro/commit/bc37331d8154e3e95a8df9131e4e014e78a7a9e7) Thanks [@ematipico](https://github.com/ematipico)! - Integrations can now log messages using Astro’s built-in logger. + + The logger is available to all hooks as an additional parameter: + + ```ts + import { AstroIntegration } from './astro'; + + // integration.js + export function myIntegration(): AstroIntegration { + return { + name: 'my-integration', + hooks: { + 'astro:config:done': ({ logger }) => { + logger.info('Configure integration...'); + }, + }, + }; + } + ``` + +### Patch Changes + +- Updated dependencies [[`1eae2e3f7`](https://github.com/withastro/astro/commit/1eae2e3f7d693c9dfe91c8ccfbe606d32bf2fb81)]: + - @astrojs/telemetry@3.0.0-beta.0 + - @astrojs/internal-helpers@0.2.0-beta.0 + - @astrojs/markdown-remark@3.0.0-beta.0 + ## 2.10.4 ### Patch Changes diff --git a/packages/astro/astro-jsx.d.ts b/packages/astro/astro-jsx.d.ts index a04d755ec..b82de9388 100644 --- a/packages/astro/astro-jsx.d.ts +++ b/packages/astro/astro-jsx.d.ts @@ -9,6 +9,8 @@ * Adapted from React’s TypeScript definition from DefinitelyTyped. * @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts */ +// BUG! Prettier 3.0 removes `declare`: https://github.com/prettier/prettier/issues/15207 +// prettier-ignore declare namespace astroHTML.JSX { export type Child = Node | Node[] | string | number | boolean | null | undefined | unknown; export type Children = Child | Child[]; diff --git a/packages/astro/astro.js b/packages/astro/astro.js index 37ea869bc..631a7b9c8 100755 --- a/packages/astro/astro.js +++ b/packages/astro/astro.js @@ -13,8 +13,8 @@ const CI_INSTRUCTIONS = { }; // Hardcode supported Node.js version so we don't have to read differently in CJS & ESM. -const engines = '>=16.12.0'; -const skipSemverCheckIfAbove = 16; +const engines = '>=18.14.1'; +const skipSemverCheckIfAbove = 19; /** `astro *` */ async function main() { diff --git a/packages/astro/content-types.template.d.ts b/packages/astro/content-types.template.d.ts index 277d00acf..ba9fe1c2a 100644 --- a/packages/astro/content-types.template.d.ts +++ b/packages/astro/content-types.template.d.ts @@ -44,7 +44,7 @@ declare module 'astro:content' { import('astro/zod').ZodLiteral<'tiff'>, import('astro/zod').ZodLiteral<'webp'>, import('astro/zod').ZodLiteral<'gif'>, - import('astro/zod').ZodLiteral<'svg'> + import('astro/zod').ZodLiteral<'svg'>, ] >; }>; @@ -87,7 +87,7 @@ declare module 'astro:content' { export function getEntryBySlug< C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug<C> | (string & {}) + E extends ValidContentEntrySlug<C> | (string & {}), >( collection: C, // Note that this has to accept a regular string too, for SSR @@ -112,7 +112,7 @@ declare module 'astro:content' { export function getEntry< C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug<C> | (string & {}) + E extends ValidContentEntrySlug<C> | (string & {}), >(entry: { collection: C; slug: E; @@ -121,7 +121,7 @@ declare module 'astro:content' { : Promise<CollectionEntry<C> | undefined>; export function getEntry< C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}) + E extends keyof DataEntryMap[C] | (string & {}), >(entry: { collection: C; id: E; @@ -130,7 +130,7 @@ declare module 'astro:content' { : Promise<CollectionEntry<C> | undefined>; export function getEntry< C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug<C> | (string & {}) + E extends ValidContentEntrySlug<C> | (string & {}), >( collection: C, slug: E @@ -139,7 +139,7 @@ declare module 'astro:content' { : Promise<CollectionEntry<C> | undefined>; export function getEntry< C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}) + E extends keyof DataEntryMap[C] | (string & {}), >( collection: C, id: E diff --git a/packages/astro/e2e/astro-envs.test.js b/packages/astro/e2e/astro-envs.test.js index 1a4f4a1ca..50cff6e89 100644 --- a/packages/astro/e2e/astro-envs.test.js +++ b/packages/astro/e2e/astro-envs.test.js @@ -18,11 +18,11 @@ test.describe('Astro Environment BASE_URL', () => { await page.goto(astro.resolveUrl('/blog/')); const astroBaseUrl = page.locator('id=astro-base-url'); - await expect(astroBaseUrl, 'astroBaseUrl equals to /blog/').toHaveText('/blog/'); + await expect(astroBaseUrl, 'astroBaseUrl equals to /blog').toHaveText('/blog'); const clientComponentBaseUrl = page.locator('id=client-component-base-url'); await expect(clientComponentBaseUrl, 'clientComponentBaseUrl equals to /blog').toHaveText( - '/blog/' + '/blog' ); }); }); diff --git a/packages/astro/package.json b/packages/astro/package.json index 1b6bf218b..8e505e8b0 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "astro", - "version": "2.10.4", + "version": "3.0.0-beta.0", "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.", "type": "module", "author": "withastro", @@ -56,6 +56,7 @@ "./assets/image-endpoint": "./dist/assets/image-endpoint.js", "./assets/services/sharp": "./dist/assets/services/sharp.js", "./assets/services/squoosh": "./dist/assets/services/squoosh.js", + "./assets/services/noop": "./dist/assets/services/noop.js", "./content/runtime": "./dist/content/runtime.js", "./content/runtime-assets": "./dist/content/runtime-assets.js", "./debug": "./components/Debug.astro", @@ -115,12 +116,10 @@ "test:e2e:match": "playwright test -g" }, "dependencies": { - "@astrojs/compiler": "^1.8.0", - "@astrojs/internal-helpers": "^0.1.2", - "@astrojs/language-server": "^1.0.0", - "@astrojs/markdown-remark": "^2.2.1", - "@astrojs/telemetry": "^2.1.1", - "@astrojs/webapi": "^2.2.0", + "@astrojs/compiler": "^1.8.1", + "@astrojs/internal-helpers": "workspace:*", + "@astrojs/markdown-remark": "workspace:*", + "@astrojs/telemetry": "workspace:*", "@babel/core": "^7.22.5", "@babel/generator": "^7.22.5", "@babel/parser": "^7.22.5", @@ -141,8 +140,8 @@ "devalue": "^4.3.2", "diff": "^5.1.0", "es-module-lexer": "^1.3.0", - "esbuild": "^0.17.19", - "estree-walker": "3.0.0", + "esbuild": "^0.18.16", + "estree-walker": "^3.0.3", "execa": "^6.1.0", "fast-glob": "^3.2.12", "github-slugger": "^2.0.0", @@ -154,6 +153,7 @@ "mime": "^3.0.0", "network-information-types": "^0.1.1", "ora": "^6.3.1", + "sharp": "^0.32.1", "p-limit": "^4.0.0", "path-to-regexp": "^6.2.1", "preferred-pm": "^3.0.3", @@ -165,7 +165,6 @@ "string-width": "^5.1.2", "strip-ansi": "^7.1.0", "tsconfig-resolver": "^3.0.1", - "typescript": "*", "unist-util-visit": "^4.1.2", "vfile": "^5.3.7", "vite": "^4.4.6", @@ -197,6 +196,7 @@ "@types/send": "^0.17.1", "@types/server-destroy": "^1.0.1", "@types/unist": "^2.0.6", + "@astrojs/check": "^0.1.0", "astro-scripts": "workspace:*", "chai": "^4.3.7", "cheerio": "1.0.0-rc.12", @@ -210,21 +210,11 @@ "remark-code-titles": "^0.1.2", "rollup": "^3.25.1", "sass": "^1.63.4", - "sharp": "^0.32.1", "srcset-parse": "^1.1.0", - "undici": "^5.22.1", "unified": "^10.1.2" }, - "peerDependencies": { - "sharp": ">=0.31.0" - }, - "peerDependenciesMeta": { - "sharp": { - "optional": true - } - }, "engines": { - "node": ">=16.12.0", + "node": ">=18.14.1", "npm": ">=6.14.0" } } diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index c2a7c3823..4867728b1 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -20,8 +20,10 @@ import type { AstroConfigSchema } from '../core/config'; import type { AstroTimer } from '../core/config/timer'; import type { AstroCookies } from '../core/cookies'; import type { LogOptions, LoggerLevel } from '../core/logger/core'; +import type { AstroIntegrationLogger } from '../core/logger/core'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; + export type { MarkdownHeading, MarkdownMetadata, @@ -326,7 +328,7 @@ type ServerConfig = { /** * @name server.port * @type {number} - * @default `3000` + * @default `4321` * @description * Set which port the dev server should listen on. * @@ -570,12 +572,7 @@ export interface AstroUserConfig { * * When using this option, all of your static asset imports and URLs should add the base as a prefix. You can access this value via `import.meta.env.BASE_URL`. * - * By default, the value of `import.meta.env.BASE_URL` includes a trailing slash. If you have the [`trailingSlash`](https://docs.astro.build/en/reference/configuration-reference/#trailingslash) option set to `'never'`, you will need to add it manually in your static asset imports and URLs. - * - * ```astro - * <a href="/docs/about/">About</a> - * <img src=`${import.meta.env.BASE_URL}image.png`> - * ``` + * The value of `import.meta.env.BASE_URL` respects your `trailingSlash` config and will include a trailing slash if you explicitly include one or if `trailingSlash: "always"` is set. If `trailingSlash: "never"` is set, `BASE_URL` will not include a trailing slash, even if `base` includes one. */ base?: string; @@ -608,19 +605,21 @@ export interface AstroUserConfig { /** * @docs * @name scopedStyleStrategy - * @type {('where' | 'class')} + * @type {('where' | 'class' | 'attribute')} * @default `'where'` * @version 2.4 * @description * * Specify the strategy used for scoping styles within Astro components. Choose from: - * - `'where'` - Use `:where` selectors, causing no specifity increase. - * - `'class'` - Use class-based selectors, causing a +1 specifity increase. + * - `'where'` - Use `:where` selectors, causing no specifity increase. + * - `'class'` - Use class-based selectors, causing a +1 specifity increase. + * - `'attribute'` - Use `data-` attributes, causing no specifity increase. * * Using `'class'` is helpful when you want to ensure that element selectors within an Astro component override global style defaults (e.g. from a global stylesheet). * Using `'where'` gives you more control over specifity, but requires that you use higher-specifity selectors, layers, and other tools to control which selectors are applied. + * Using `'attribute'` is useful in case there's manipulation of the class attributes, so the styling emitted by Astro doesn't go in conflict with the user's business logic. */ - scopedStyleStrategy?: 'where' | 'class'; + scopedStyleStrategy?: 'where' | 'class' | 'attribute'; /** * @docs @@ -914,7 +913,7 @@ export interface AstroUserConfig { * ```js * { * // Example: Use the function syntax to customize based on command - * server: ({ command }) => ({ port: command === 'dev' ? 3000 : 4000 }) + * server: ({ command }) => ({ port: command === 'dev' ? 4321 : 4000 }) * } * ``` */ @@ -936,7 +935,7 @@ export interface AstroUserConfig { * @docs * @name server.port * @type {number} - * @default `3000` + * @default `4321` * @description * Set which port the server should listen on. * @@ -986,7 +985,7 @@ export interface AstroUserConfig { * @docs * @name image.service (Experimental) * @type {{entrypoint: 'astro/assets/services/sharp' | 'astro/assets/services/squoosh' | string, config: Record<string, any>}} - * @default `{entrypoint: 'astro/assets/services/squoosh', config?: {}}` + * @default `{entrypoint: 'astro/assets/services/sharp', config?: {}}` * @version 2.1.0 * @description * Set which image service is used for Astro’s experimental assets support. @@ -1436,6 +1435,17 @@ export interface DataEntryType { export type GetDataEntryInfoReturnType = { data: Record<string, unknown>; rawData?: string }; +export interface AstroAdapterFeatures { + /** + * Creates and edge function that will communiate with the Astro middleware + */ + edgeMiddleware: boolean; + /** + * SSR only. Each route becomes its own function/file. + */ + functionPerRoute: boolean; +} + export interface AstroSettings { config: AstroConfig; adapter: AstroAdapter | undefined; @@ -1552,10 +1562,7 @@ export type GetStaticPathsResultKeyed = GetStaticPathsResult & { */ export type GetStaticPaths = ( options: GetStaticPathsOptions -) => - | Promise<GetStaticPathsResult | GetStaticPathsResult[]> - | GetStaticPathsResult - | GetStaticPathsResult[]; +) => Promise<GetStaticPathsResult> | GetStaticPathsResult; /** * Infers the shape of the `params` property returned by `getStaticPaths()`. @@ -1698,12 +1705,52 @@ export type PaginateFunction = (data: any[], args?: PaginateOptions) => GetStati export type Params = Record<string, string | undefined>; +export type SupportsKind = 'unsupported' | 'stable' | 'experimental' | 'deprecated'; + +export type AstroFeatureMap = { + /** + * The adapter is able serve static pages + */ + staticOutput?: SupportsKind; + /** + * The adapter is able to serve pages that are static or rendered via server + */ + hybridOutput?: SupportsKind; + /** + * The adapter is able to serve SSR pages + */ + serverOutput?: SupportsKind; + /** + * The adapter can emit static assets + */ + assets?: AstroAssetsFeature; +}; + +export interface AstroAssetsFeature { + supportKind?: SupportsKind; + /** + * Whether if this adapter deploys files in an enviroment that is compatible with the library `sharp` + */ + isSharpCompatible?: boolean; + /** + * Whether if this adapter deploys files in an enviroment that is compatible with the library `squoosh` + */ + isSquooshCompatible?: boolean; +} + export interface AstroAdapter { name: string; serverEntrypoint?: string; previewEntrypoint?: string; exports?: string[]; args?: any; + adapterFeatures?: AstroAdapterFeatures; + /** + * List of features supported by an adapter. + * + * If the adapter is not able to handle certain configurations, Astro will throw an error. + */ + supportedAstroFeatures?: AstroFeatureMap; } type Body = string; @@ -1891,7 +1938,7 @@ export interface SSRLoadedRenderer extends AstroRenderer { export type HookParameters< Hook extends keyof AstroIntegration['hooks'], - Fn = AstroIntegration['hooks'][Hook] + Fn = AstroIntegration['hooks'][Hook], > = Fn extends (...args: any) => any ? Parameters<Fn>[0] : never; export interface AstroIntegration { @@ -1909,6 +1956,7 @@ export interface AstroIntegration { injectScript: (stage: InjectedScriptStage, content: string) => void; injectRoute: (injectRoute: InjectedRoute) => void; addClientDirective: (directive: ClientDirectiveConfig) => void; + logger: AstroIntegrationLogger; // TODO: Add support for `injectElement()` for full HTML element injection, not just scripts. // This may require some refactoring of `scripts`, `styles`, and `links` into something // more generalized. Consider the SSR use-case as well. @@ -1917,10 +1965,17 @@ export interface AstroIntegration { 'astro:config:done'?: (options: { config: AstroConfig; setAdapter: (adapter: AstroAdapter) => void; + logger: AstroIntegrationLogger; }) => void | Promise<void>; - 'astro:server:setup'?: (options: { server: vite.ViteDevServer }) => void | Promise<void>; - 'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise<void>; - 'astro:server:done'?: () => void | Promise<void>; + 'astro:server:setup'?: (options: { + server: vite.ViteDevServer; + logger: AstroIntegrationLogger; + }) => void | Promise<void>; + 'astro:server:start'?: (options: { + address: AddressInfo; + logger: AstroIntegrationLogger; + }) => void | Promise<void>; + 'astro:server:done'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>; 'astro:build:ssr'?: (options: { manifest: SerializedSSRManifest; /** @@ -1932,19 +1987,25 @@ export interface AstroIntegration { * File path of the emitted middleware */ middlewareEntryPoint: URL | undefined; + logger: AstroIntegrationLogger; }) => void | Promise<void>; - 'astro:build:start'?: () => void | Promise<void>; + 'astro:build:start'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>; 'astro:build:setup'?: (options: { vite: vite.InlineConfig; pages: Map<string, PageBuildData>; target: 'client' | 'server'; updateConfig: (newConfig: vite.InlineConfig) => void; + logger: AstroIntegrationLogger; + }) => void | Promise<void>; + 'astro:build:generated'?: (options: { + dir: URL; + logger: AstroIntegrationLogger; }) => void | Promise<void>; - 'astro:build:generated'?: (options: { dir: URL }) => void | Promise<void>; 'astro:build:done'?: (options: { pages: { pathname: string }[]; dir: URL; routes: RouteData[]; + logger: AstroIntegrationLogger; }) => void | Promise<void>; }; } diff --git a/packages/astro/src/assets/generate.ts b/packages/astro/src/assets/generate.ts index d6cb02e56..04488ed8f 100644 --- a/packages/astro/src/assets/generate.ts +++ b/packages/astro/src/assets/generate.ts @@ -27,6 +27,11 @@ export async function generateImage( options: ImageTransform, filepath: string ): Promise<GenerationData | undefined> { + if (typeof buildOpts.settings.config.image === 'undefined') { + throw new Error( + "Astro hasn't set a default service for `astro:assets`. This is an internal error and you should report it." + ); + } if (!isESMImportedImage(options.src)) { return undefined; } diff --git a/packages/astro/src/assets/image-endpoint.ts b/packages/astro/src/assets/image-endpoint.ts index 0553272c2..8dc15c36a 100644 --- a/packages/astro/src/assets/image-endpoint.ts +++ b/packages/astro/src/assets/image-endpoint.ts @@ -24,7 +24,7 @@ async function loadRemoteImage(src: URL) { /** * Endpoint used in dev and SSR to serve optimized images by the base image services */ -export const get: APIRoute = async ({ request }) => { +export const GET: APIRoute = async ({ request }) => { try { const imageService = await getConfiguredImageService(); diff --git a/packages/astro/src/assets/services/noop.ts b/packages/astro/src/assets/services/noop.ts new file mode 100644 index 000000000..d57ffeb27 --- /dev/null +++ b/packages/astro/src/assets/services/noop.ts @@ -0,0 +1,17 @@ +import { baseService, type LocalImageService } from './service.js'; + +// Empty service used for platforms that neither support Squoosh or Sharp. +const noopService: LocalImageService = { + validateOptions: baseService.validateOptions, + getURL: baseService.getURL, + parseURL: baseService.parseURL, + getHTMLAttributes: baseService.getHTMLAttributes, + async transform(inputBuffer, transformOptions) { + return { + data: inputBuffer, + format: transformOptions.format, + }; + }, +}; + +export default noopService; diff --git a/packages/astro/src/assets/services/vendor/squoosh/impl.ts b/packages/astro/src/assets/services/vendor/squoosh/impl.ts index 7bb9aeeea..273957e4b 100644 --- a/packages/astro/src/assets/services/vendor/squoosh/impl.ts +++ b/packages/astro/src/assets/services/vendor/squoosh/impl.ts @@ -33,7 +33,7 @@ export async function decodeBuffer( .join('') // TODO (future PR): support more formats if (firstChunkString.includes('GIF')) { - throw Error(`GIF images are not supported, please install the @astrojs/image/sharp plugin`) + throw Error(`GIF images are not supported, please use the Sharp image service`) } const key = Object.entries(supportedFormats).find(([, { detectors }]) => detectors.some((detector) => detector.exec(firstChunkString)) @@ -78,7 +78,7 @@ export async function encodeJpeg( opts: { quality?: number } ): Promise<Uint8Array> { image = ImageData.from(image) - + const e = supportedFormats['mozjpeg'] const m = await e.enc() await maybeDelay() diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 565253001..2ab87b7c1 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -1,10 +1,8 @@ -import { bold } from 'kleur/colors'; import MagicString from 'magic-string'; import { fileURLToPath } from 'node:url'; import type * as vite from 'vite'; import { normalizePath } from 'vite'; import type { AstroPluginOptions, ImageTransform } from '../@types/astro'; -import { error } from '../core/logger/core.js'; import { appendForwardSlash, joinPaths, @@ -23,37 +21,12 @@ const urlRE = /(\?|&)url(?:&|$)/; export default function assets({ settings, - logging, mode, }: AstroPluginOptions & { mode: string }): vite.Plugin[] { let resolvedConfig: vite.ResolvedConfig; globalThis.astroAsset = {}; - const UNSUPPORTED_ADAPTERS = new Set([ - '@astrojs/cloudflare', - '@astrojs/deno', - '@astrojs/netlify/edge-functions', - '@astrojs/vercel/edge', - ]); - - const adapterName = settings.config.adapter?.name; - if ( - ['astro/assets/services/sharp', 'astro/assets/services/squoosh'].includes( - settings.config.image.service.entrypoint - ) && - adapterName && - UNSUPPORTED_ADAPTERS.has(adapterName) - ) { - error( - logging, - 'assets', - `The currently selected adapter \`${adapterName}\` does not run on Node, however the currently used image service depends on Node built-ins. ${bold( - 'Your project will NOT be able to build.' - )}` - ); - } - return [ // Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev { diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index 96bee308d..428027154 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -1,396 +1,43 @@ -import { - AstroCheck, - DiagnosticSeverity, - type GetDiagnosticsResult, -} from '@astrojs/language-server'; -import type { FSWatcher } from 'chokidar'; -import glob from 'fast-glob'; -import { bold, dim, red, yellow } from 'kleur/colors'; -import { createRequire } from 'module'; -import fs from 'node:fs'; -import { join } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import ora from 'ora'; -import type { Arguments as Flags } from 'yargs-parser'; -import type { AstroSettings } from '../../@types/astro'; -import { resolveConfig } from '../../core/config/config.js'; -import { createNodeLogging } from '../../core/config/logging.js'; -import { createSettings } from '../../core/config/settings.js'; -import type { LogOptions } from '../../core/logger/core.js'; -import { debug, info } from '../../core/logger/core.js'; -import { printHelp } from '../../core/messages.js'; -import type { syncInternal } from '../../core/sync'; -import { eventCliSession, telemetry } from '../../events/index.js'; -import { runHookConfigSetup } from '../../integrations/index.js'; -import { flagsToAstroInlineConfig } from '../flags.js'; -import { printDiagnostic } from './print.js'; - -type DiagnosticResult = { - errors: number; - warnings: number; - hints: number; -}; - -export type CheckPayload = { - /** - * Flags passed via CLI - */ - flags: Flags; -}; - -type CheckFlags = { - /** - * Whether the `check` command should watch for `.astro` and report errors - * @default {false} - */ - watch: boolean; -}; - -/** - * - * Types of response emitted by the checker - */ -export enum CheckResult { - /** - * Operation finished without errors - */ - ExitWithSuccess, - /** - * Operation finished with errors - */ - ExitWithError, - /** - * The consumer should not terminate the operation - */ - Listen, -} - -const ASTRO_GLOB_PATTERN = '**/*.astro'; - -/** - * Checks `.astro` files for possible errors. - * - * If the `--watch` flag is provided, the command runs indefinitely and provides diagnostics - * when `.astro` files are modified. - * - * Every time an astro files is modified, content collections are also generated. - * - * @param {CheckPayload} options Options passed {@link AstroChecker} - * @param {Flags} options.flags Flags coming from the CLI - */ -export async function check({ flags }: CheckPayload): Promise<AstroChecker | undefined> { - if (flags.help || flags.h) { - printHelp({ - commandName: 'astro check', - usage: '[...flags]', - tables: { - Flags: [ - ['--watch', 'Watch Astro files for changes and re-run checks.'], - ['--help (-h)', 'See all available flags.'], - ], - }, - description: `Runs diagnostics against your project and reports errors to the console.`, - }); - return; - } - - // Load settings - const inlineConfig = flagsToAstroInlineConfig(flags); - const logging = createNodeLogging(inlineConfig); - const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'check'); - telemetry.record(eventCliSession('check', userConfig, flags)); - const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root)); - - const checkFlags = parseFlags(flags); - if (checkFlags.watch) { - info(logging, 'check', 'Checking files in watch mode'); - } else { - info(logging, 'check', 'Checking files'); - } - - const { syncInternal } = await import('../../core/sync/index.js'); - const root = settings.config.root; - const require = createRequire(import.meta.url); - const diagnosticChecker = new AstroCheck( - root.toString(), - require.resolve('typescript/lib/tsserverlibrary.js', { - paths: [root.toString()], - }) - ); - - return new AstroChecker({ - syncInternal, - settings, - fileSystem: fs, +import path from 'node:path'; +import type { Arguments } from 'yargs-parser'; +import { error, info } from '../../core/logger/core.js'; +import { createLoggingFromFlags, flagsToAstroInlineConfig } from '../flags.js'; +import { getPackage } from '../install-package.js'; + +export async function check(flags: Arguments) { + const logging = createLoggingFromFlags(flags); + const getPackageOpts = { skipAsk: flags.yes || flags.y, cwd: flags.root }; + const checkPackage = await getPackage<typeof import('@astrojs/check')>( + '@astrojs/check', logging, - diagnosticChecker, - isWatchMode: checkFlags.watch, - }); -} - -type CheckerConstructor = { - diagnosticChecker: AstroCheck; - - isWatchMode: boolean; - - syncInternal: typeof syncInternal; - - settings: Readonly<AstroSettings>; - - logging: Readonly<LogOptions>; - - fileSystem: typeof fs; -}; - -/** - * Responsible to check files - classic or watch mode - and report diagnostics. - * - * When in watch mode, the class does a whole check pass, and then starts watching files. - * When a change occurs to an `.astro` file, the checker builds content collections again and lint all the `.astro` files. - */ -export class AstroChecker { - readonly #diagnosticsChecker: AstroCheck; - readonly #shouldWatch: boolean; - readonly #syncInternal: CheckerConstructor['syncInternal']; - - readonly #settings: AstroSettings; - - readonly #logging: LogOptions; - readonly #fs: typeof fs; - #watcher?: FSWatcher; - - #filesCount: number; - #updateDiagnostics: NodeJS.Timeout | undefined; - - constructor({ - diagnosticChecker, - isWatchMode, - syncInternal, - settings, - fileSystem, - logging, - }: CheckerConstructor) { - this.#diagnosticsChecker = diagnosticChecker; - this.#shouldWatch = isWatchMode; - this.#syncInternal = syncInternal; - this.#logging = logging; - this.#settings = settings; - this.#fs = fileSystem; - this.#filesCount = 0; - } - - /** - * Check all `.astro` files once and then finishes the operation. - */ - public async check(): Promise<CheckResult> { - return await this.#checkAllFiles(true); - } - - /** - * Check all `.astro` files and then start watching for changes. - */ - public async watch(): Promise<CheckResult> { - await this.#checkAllFiles(true); - await this.#watch(); - return CheckResult.Listen; - } - - /** - * Stops the watch. It terminates the inner server. - */ - public async stop() { - await this.#watcher?.close(); - } - - /** - * Whether the checker should run in watch mode - */ - public get isWatchMode(): boolean { - return this.#shouldWatch; - } - - async #openDocuments() { - this.#filesCount = await openAllDocuments( - this.#settings.config.root, - [], - this.#diagnosticsChecker - ); - } - - /** - * Lint all `.astro` files, and report the result in console. Operations executed, in order: - * 1. Compile content collections. - * 2. Optionally, traverse the file system for `.astro` files and saves their paths. - * 3. Get diagnostics for said files and print the result in console. - * - * @param openDocuments Whether the operation should open all `.astro` files - */ - async #checkAllFiles(openDocuments: boolean): Promise<CheckResult> { - // Run `astro:config:setup` before syncing to initialize integrations. - // We do this manually as we're calling `syncInternal` directly. - const syncSettings = await runHookConfigSetup({ - settings: this.#settings, - logging: this.#logging, - command: 'build', - }); - const processExit = await this.#syncInternal(syncSettings, { - logging: this.#logging, - fs: this.#fs, - }); - // early exit on sync failure - if (processExit === 1) return processExit; - - let spinner = ora( - ` Getting diagnostics for Astro files in ${fileURLToPath(this.#settings.config.root)}…` - ).start(); - - if (openDocuments) { - await this.#openDocuments(); - } - - let diagnostics = await this.#diagnosticsChecker.getDiagnostics(); - - spinner.succeed(); - - let brokenDownDiagnostics = this.#breakDownDiagnostics(diagnostics); - this.#logDiagnosticsSeverity(brokenDownDiagnostics); - return brokenDownDiagnostics.errors > 0 - ? CheckResult.ExitWithError - : CheckResult.ExitWithSuccess; - } - - #checkForDiagnostics() { - clearTimeout(this.#updateDiagnostics); - // @ematipico: I am not sure of `setTimeout`. I would rather use a debounce but let's see if this works. - // Inspiration from `svelte-check`. - this.#updateDiagnostics = setTimeout(async () => await this.#checkAllFiles(false), 500); - } - - /** - * This function is responsible to attach events to the server watcher - */ - async #watch() { - const { default: chokidar } = await import('chokidar'); - this.#watcher = chokidar.watch( - join(fileURLToPath(this.#settings.config.root), ASTRO_GLOB_PATTERN), - { - ignored: ['**/node_modules/**'], - ignoreInitial: true, - } - ); - - this.#watcher.on('add', (file) => { - this.#addDocument(file); - this.#filesCount += 1; - this.#checkForDiagnostics(); - }); - this.#watcher.on('change', (file) => { - this.#addDocument(file); - this.#checkForDiagnostics(); - }); - this.#watcher.on('unlink', (file) => { - this.#diagnosticsChecker.removeDocument(file); - this.#filesCount -= 1; - this.#checkForDiagnostics(); - }); - } - - /** - * Add a document to the diagnostics checker - * @param filePath Path to the file - */ - #addDocument(filePath: string) { - const text = fs.readFileSync(filePath, 'utf-8'); - this.#diagnosticsChecker.upsertDocument({ - uri: pathToFileURL(filePath).toString(), - text, - }); - } + getPackageOpts, + ['typescript'] + ); + const typescript = await getPackage('typescript', logging, getPackageOpts); - /** - * Logs the result of the various diagnostics - * - * @param result Result emitted by AstroChecker.#breakDownDiagnostics - */ - #logDiagnosticsSeverity(result: Readonly<DiagnosticResult>) { - info( - this.#logging, - 'diagnostics', - [ - bold(`Result (${this.#filesCount} file${this.#filesCount === 1 ? '' : 's'}): `), - bold(red(`${result.errors} ${result.errors === 1 ? 'error' : 'errors'}`)), - bold(yellow(`${result.warnings} ${result.warnings === 1 ? 'warning' : 'warnings'}`)), - dim(`${result.hints} ${result.hints === 1 ? 'hint' : 'hints'}\n`), - ].join(`\n${dim('-')} `) + if (!checkPackage || !typescript) { + error( + logging, + 'check', + 'The `@astrojs/check` and `typescript` packages are required for this command to work. Please manually install them into your project and try again.' ); + return; } - /** - * It loops through all diagnostics and break down diagnostics that are errors, warnings or hints. - */ - #breakDownDiagnostics(diagnostics: Readonly<GetDiagnosticsResult[]>): DiagnosticResult { - let result: DiagnosticResult = { - errors: 0, - warnings: 0, - hints: 0, - }; - - diagnostics.forEach((diag) => { - diag.diagnostics.forEach((d) => { - info(this.#logging, 'diagnostics', `\n ${printDiagnostic(diag.fileUri, diag.text, d)}`); - - switch (d.severity) { - case DiagnosticSeverity.Error: { - result.errors++; - break; - } - case DiagnosticSeverity.Warning: { - result.warnings++; - break; - } - case DiagnosticSeverity.Hint: { - result.hints++; - break; - } - } - }); - }); - - return result; + // Run sync before check to make sure types are generated. + // NOTE: In the future, `@astrojs/check` can expose a `before lint` hook so that this works during `astro check --watch` too. + // For now, we run this once as usually `astro check --watch` is ran alongside `astro dev` which also calls `astro sync`. + const { sync } = await import('../../core/sync/index.js'); + const inlineConfig = flagsToAstroInlineConfig(flags); + const exitCode = await sync(inlineConfig); + if (exitCode !== 0) { + process.exit(exitCode); } -} - -/** - * Open all Astro files in the given directory and return the number of files found. - */ -async function openAllDocuments( - workspaceUri: URL, - filePathsToIgnore: string[], - checker: AstroCheck -): Promise<number> { - const files = await glob(ASTRO_GLOB_PATTERN, { - cwd: fileURLToPath(workspaceUri), - ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)), - absolute: true, - }); - for (const file of files) { - debug('check', `Adding file ${file} to the list of files to check.`); - const text = fs.readFileSync(file, 'utf-8'); - checker.upsertDocument({ - uri: pathToFileURL(file).toString(), - text, - }); - } + const { check: checker, parseArgsAsCheckConfig } = checkPackage; - return files.length; -} + const config = parseArgsAsCheckConfig(process.argv); -/** - * Parse flags and sets defaults - */ -function parseFlags(flags: Flags): CheckFlags { - return { - watch: flags.watch ?? false, - }; + info(logging, 'check', `Getting diagnostics for Astro files in ${path.resolve(config.root)}...`); + return await checker(config); } diff --git a/packages/astro/src/cli/check/print.ts b/packages/astro/src/cli/check/print.ts deleted file mode 100644 index bd8de2ddb..000000000 --- a/packages/astro/src/cli/check/print.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { DiagnosticSeverity, offsetAt, type Diagnostic } from '@astrojs/language-server'; -import { - bgRed, - bgWhite, - bgYellow, - black, - bold, - cyan, - gray, - red, - white, - yellow, -} from 'kleur/colors'; -import { fileURLToPath } from 'node:url'; -import stringWidth from 'string-width'; - -export function printDiagnostic(filePath: string, text: string, diag: Diagnostic): string { - let result = []; - - // Lines and characters are 0-indexed, so we need to add 1 to the offset to get the actual line and character - const realStartLine = diag.range.start.line + 1; - const realStartCharacter = diag.range.start.character + 1; - - // IDE friendly path that user can CTRL+Click to open the file at a specific line / character - const IDEFilePath = `${bold(cyan(fileURLToPath(filePath)))}:${bold(yellow(realStartLine))}:${bold( - yellow(realStartCharacter) - )}`; - result.push( - `${IDEFilePath} ${bold(getColorForSeverity(diag, getStringForSeverity(diag)))}: ${diag.message}` - ); - - // Optionally add the line before the error to add context if not empty - const previousLine = getLine(diag.range.start.line - 1, text); - if (previousLine) { - result.push(`${getPrintableLineNumber(realStartLine - 1)} ${gray(previousLine)}`); - } - - // Add the line with the error - const str = getLine(diag.range.start.line, text); - const lineNumStr = realStartLine.toString().padStart(2, '0'); - const lineNumLen = lineNumStr.length; - result.push(`${getBackgroundForSeverity(diag, lineNumStr)} ${str}`); - - // Adds tildes under the specific range where the diagnostic is - const tildes = generateString('~', diag.range.end.character - diag.range.start.character); - - // NOTE: This is not perfect, if the line include any characters that is made of multiple characters, for example - // regionals flags, but the terminal can't display it, then the number of spaces will be wrong. Not sure how to fix. - const beforeChars = stringWidth(str.substring(0, diag.range.start.character)); - const spaces = generateString(' ', beforeChars + lineNumLen - 1); - result.push(` ${spaces}${bold(getColorForSeverity(diag, tildes))}`); - - const nextLine = getLine(diag.range.start.line + 1, text); - if (nextLine) { - result.push(`${getPrintableLineNumber(realStartLine + 1)} ${gray(nextLine)}`); - } - - // Force a new line at the end - result.push(''); - - return result.join('\n'); -} - -function generateString(str: string, len: number): string { - return Array.from({ length: len }, () => str).join(''); -} - -function getStringForSeverity(diag: Diagnostic): string { - switch (diag.severity) { - case DiagnosticSeverity.Error: - return 'Error'; - case DiagnosticSeverity.Warning: - return 'Warning'; - case DiagnosticSeverity.Hint: - return 'Hint'; - default: - return 'Unknown'; - } -} - -function getColorForSeverity(diag: Diagnostic, text: string): string { - switch (diag.severity) { - case DiagnosticSeverity.Error: - return red(text); - case DiagnosticSeverity.Warning: - return yellow(text); - case DiagnosticSeverity.Hint: - return gray(text); - default: - return text; - } -} - -function getBackgroundForSeverity(diag: Diagnostic, text: string): string { - switch (diag.severity) { - case DiagnosticSeverity.Error: - return bgRed(white(text)); - case DiagnosticSeverity.Warning: - return bgYellow(white(text)); - case DiagnosticSeverity.Hint: - return bgWhite(black(text)); - default: - return text; - } -} - -function getPrintableLineNumber(line: number): string { - return bgWhite(black(line.toString().padStart(2, '0'))); -} - -function getLine(line: number, text: string): string { - return text - .substring( - offsetAt({ line, character: 0 }, text), - offsetAt({ line, character: Number.MAX_SAFE_INTEGER }, text) - ) - .replace(/\t/g, ' ') - .trimEnd(); -} diff --git a/packages/astro/src/cli/dev/index.ts b/packages/astro/src/cli/dev/index.ts index e55496c4a..5db47fb97 100644 --- a/packages/astro/src/cli/dev/index.ts +++ b/packages/astro/src/cli/dev/index.ts @@ -15,7 +15,7 @@ export async function dev({ flags }: DevOptions) { usage: '[...flags]', tables: { Flags: [ - ['--port', `Specify which port to run on. Defaults to 3000.`], + ['--port', `Specify which port to run on. Defaults to 4321.`], ['--host', `Listen on all addresses, including LAN and public addresses.`], ['--host <custom-address>', `Expose on a network IP address at <custom-address>`], ['--open', 'Automatically open the app in the browser on server start'], diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts index d16ea91e2..fdf43201f 100644 --- a/packages/astro/src/cli/index.ts +++ b/packages/astro/src/cli/index.ts @@ -154,18 +154,12 @@ async function runCommand(cmd: string, flags: yargs.Arguments) { } case 'check': { const { check } = await import('./check/index.js'); - // We create a server to start doing our operations - const checkServer = await check({ flags }); - if (checkServer) { - if (checkServer.isWatchMode) { - await checkServer.watch(); - return await new Promise(() => {}); // lives forever - } else { - const checkResult = await checkServer.check(); - return process.exit(checkResult); - } + const checkServer = await check(flags); + if (flags.watch) { + return await new Promise(() => {}); // lives forever + } else { + return process.exit(checkServer ? 1 : 0); } - return; } case 'sync': { const { sync } = await import('./sync/index.js'); diff --git a/packages/astro/src/cli/install-package.ts b/packages/astro/src/cli/install-package.ts new file mode 100644 index 000000000..8793d9985 --- /dev/null +++ b/packages/astro/src/cli/install-package.ts @@ -0,0 +1,124 @@ +import boxen from 'boxen'; +import { execa } from 'execa'; +import { bold, cyan, dim, magenta } from 'kleur/colors'; +import { createRequire } from 'node:module'; +import ora from 'ora'; +import prompts from 'prompts'; +import whichPm from 'which-pm'; +import { debug, info, type LogOptions } from '../core/logger/core.js'; + +type GetPackageOptions = { + skipAsk?: boolean; + cwd?: string; +}; + +export async function getPackage<T>( + packageName: string, + logging: LogOptions, + options: GetPackageOptions, + otherDeps: string[] = [] +): Promise<T | undefined> { + const require = createRequire(options.cwd ?? process.cwd()); + + let packageImport; + try { + require.resolve(packageName); + + // The `require.resolve` is required as to avoid Node caching the failed `import` + packageImport = await import(packageName); + } catch (e) { + info( + logging, + '', + `To continue, Astro requires the following dependency to be installed: ${bold(packageName)}.` + ); + const result = await installPackage([packageName, ...otherDeps], options, logging); + + if (result) { + packageImport = await import(packageName); + } else { + return undefined; + } + } + + return packageImport as T; +} + +function getInstallCommand(packages: string[], packageManager: string) { + switch (packageManager) { + case 'npm': + return { pm: 'npm', command: 'install', flags: [], dependencies: packages }; + case 'yarn': + return { pm: 'yarn', command: 'add', flags: [], dependencies: packages }; + case 'pnpm': + return { pm: 'pnpm', command: 'add', flags: [], dependencies: packages }; + default: + return null; + } +} + +async function installPackage( + packageNames: string[], + options: GetPackageOptions, + logging: LogOptions +): Promise<boolean> { + const cwd = options.cwd ?? process.cwd(); + const packageManager = (await whichPm(cwd)).name ?? 'npm'; + const installCommand = getInstallCommand(packageNames, packageManager); + + if (!installCommand) { + return false; + } + + const coloredOutput = `${bold(installCommand.pm)} ${installCommand.command}${[ + '', + ...installCommand.flags, + ].join(' ')} ${cyan(installCommand.dependencies.join(' '))}`; + const message = `\n${boxen(coloredOutput, { + margin: 0.5, + padding: 0.5, + borderStyle: 'round', + })}\n`; + info( + logging, + null, + `\n ${magenta('Astro will run the following command:')}\n ${dim( + 'If you skip this step, you can always run it yourself later' + )}\n${message}` + ); + + let response; + if (options.skipAsk) { + response = true; + } else { + response = ( + await prompts({ + type: 'confirm', + name: 'askToContinue', + message: 'Continue?', + initial: true, + }) + ).askToContinue; + } + + if (Boolean(response)) { + const spinner = ora('Installing dependencies...').start(); + try { + await execa( + installCommand.pm, + [installCommand.command, ...installCommand.flags, ...installCommand.dependencies], + { cwd: cwd } + ); + spinner.succeed(); + + return true; + } catch (err) { + debug('add', 'Error installing dependencies', err); + spinner.fail(); + + return false; + } + } else { + return false; + } +} diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index bec5368b6..43f7a3926 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -88,7 +88,6 @@ export class App { return createEnvironment({ adapterName: this.#manifest.adapterName, logging: this.#logging, - markdown: this.#manifest.markdown, mode: 'production', compressHTML: this.#manifest.compressHTML, renderers: this.#manifest.renderers, diff --git a/packages/astro/src/core/app/node.ts b/packages/astro/src/core/app/node.ts index 2cfc686a2..f31af36db 100644 --- a/packages/astro/src/core/app/node.ts +++ b/packages/astro/src/core/app/node.ts @@ -6,6 +6,7 @@ import { IncomingMessage } from 'node:http'; import { TLSSocket } from 'node:tls'; import { deserializeManifest } from './common.js'; import { App, type MatchOptions } from './index.js'; +export { apply as applyPolyfills } from '../polyfill.js'; const clientAddressSymbol = Symbol.for('astro.clientAddress'); diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 67d16d457..8812d2c44 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -1,4 +1,3 @@ -import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark'; import type { RouteData, SerializedRouteData, @@ -40,7 +39,6 @@ export type SSRManifest = { base: string; compressHTML: boolean; assetsPrefix?: string; - markdown: MarkdownRenderingOptions; renderers: SSRLoadedRenderer[]; /** * Map of directive name (e.g. `load`) to the directive script code diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 708295296..3e3a44ce0 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -28,6 +28,7 @@ import { } from '../../core/build/internal.js'; import { isRelativePath, + joinPaths, prependForwardSlash, removeLeadingForwardSlash, removeTrailingForwardSlash, @@ -300,7 +301,6 @@ async function getPathsForRoute( mod, route, routeCache: opts.routeCache, - isValidate: false, logging: opts.logging, ssr: isServerLikeOutput(opts.settings.config), }).catch((err) => { @@ -438,11 +438,11 @@ function getUrlForPath( buildPathname = base; } else if (routeType === 'endpoint') { const buildPathRelative = removeLeadingForwardSlash(pathname); - buildPathname = base + buildPathRelative; + buildPathname = joinPaths(base, buildPathRelative); } else { const buildPathRelative = removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending; - buildPathname = base + buildPathRelative; + buildPathname = joinPaths(base, buildPathRelative); } const url = new URL(buildPathname, origin); return url; @@ -508,7 +508,6 @@ async function generatePath( const env = createEnvironment({ adapterName: manifest.adapterName, logging, - markdown: manifest.markdown, mode: opts.mode, renderers: manifest.renderers, clientDirectives: manifest.clientDirectives, @@ -620,7 +619,6 @@ export function createBuildManifest( entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()), routes: [], adapterName: '', - markdown: settings.config.markdown, clientDirectives: settings.clientDirectives, compressHTML: settings.config.compressHTML, renderers, diff --git a/packages/astro/src/core/build/plugins/plugin-analyzer.ts b/packages/astro/src/core/build/plugins/plugin-analyzer.ts index 8483748bc..bb632eb49 100644 --- a/packages/astro/src/core/build/plugins/plugin-analyzer.ts +++ b/packages/astro/src/core/build/plugins/plugin-analyzer.ts @@ -1,3 +1,4 @@ +import type { Node as ESTreeNode } from 'estree-walker'; import type { ModuleInfo, PluginContext } from 'rollup'; import type { Plugin as VitePlugin } from 'vite'; import type { PluginMetadata as AstroPluginMetadata } from '../../../vite-plugin-astro/types'; diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index 2ee438a6a..966426439 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -74,7 +74,11 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V exports.push(`export { renderers };`); // The middleware should not be imported by the pages - if (!opts.settings.config.build.excludeMiddleware) { + if ( + // TODO: remover in Astro 4.0 + !opts.settings.config.build.excludeMiddleware || + opts.settings.adapter?.adapterFeatures?.edgeMiddleware === true + ) { const middlewareModule = await this.resolve(MIDDLEWARE_MODULE_ID); if (middlewareModule) { imports.push(`import { onRequest } from "${middlewareModule.id}";`); diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index 514fe2409..ed4cd7b72 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -3,7 +3,7 @@ import { join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import type { Plugin as VitePlugin } from 'vite'; import type { AstroAdapter, AstroConfig } from '../../../@types/astro'; -import { runHookBuildSsr } from '../../../integrations/index.js'; +import { isFunctionPerRouteEnabled, runHookBuildSsr } from '../../../integrations/index.js'; import { isServerLikeOutput } from '../../../prerender/utils.js'; import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js'; import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types'; @@ -103,12 +103,16 @@ export function pluginSSR( internals: BuildInternals ): AstroBuildPlugin { const ssr = isServerLikeOutput(options.settings.config); + const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter); return { build: 'ssr', hooks: { 'build:before': () => { let vitePlugin = - ssr && !options.settings.config.build.split + ssr && + // TODO: Remove in Astro 4.0 + options.settings.config.build.split === false && + functionPerRouteEnabled === false ? vitePluginSSR(internals, options.settings.adapter!, options) : undefined; @@ -122,7 +126,7 @@ export function pluginSSR( return; } - if (options.settings.config.build.split) { + if (options.settings.config.build.split || functionPerRouteEnabled) { return; } @@ -155,11 +159,12 @@ function vitePluginSSRSplit( adapter: AstroAdapter, options: StaticBuildOptions ): VitePlugin { + const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter); return { name: '@astrojs/vite-plugin-astro-ssr-split', enforce: 'post', options(opts) { - if (options.settings.config.build.split) { + if (options.settings.config.build.split || functionPerRouteEnabled) { const inputs = new Set<string>(); for (const path of Object.keys(options.allPages)) { @@ -229,12 +234,14 @@ export function pluginSSRSplit( internals: BuildInternals ): AstroBuildPlugin { const ssr = isServerLikeOutput(options.settings.config); + const functionPerRouteEnabled = isFunctionPerRouteEnabled(options.settings.adapter); + return { build: 'ssr', hooks: { 'build:before': () => { let vitePlugin = - ssr && options.settings.config.build.split + ssr && (options.settings.config.build.split || functionPerRouteEnabled) ? vitePluginSSRSplit(internals, options.settings.adapter!, options) : undefined; @@ -247,7 +254,7 @@ export function pluginSSRSplit( if (!ssr) { return; } - if (!options.settings.config.build.split) { + if (!options.settings.config.build.split && !functionPerRouteEnabled) { return; } @@ -276,7 +283,7 @@ function generateSSRCode(config: AstroConfig, adapter: AstroAdapter) { const imports: string[] = []; const contents: string[] = []; let pageMap; - if (config.build.split) { + if (config.build.split || isFunctionPerRouteEnabled(adapter)) { pageMap = 'pageModule'; } else { pageMap = 'pageMap'; @@ -337,7 +344,10 @@ export async function createManifest( buildOpts: StaticBuildOptions, internals: BuildInternals ): Promise<SerializedSSRManifest> { - if (buildOpts.settings.config.build.split) { + if ( + buildOpts.settings.config.build.split || + isFunctionPerRouteEnabled(buildOpts.settings.adapter) + ) { if (internals.ssrSplitEntryChunks.size === 0) { throw new Error(`Did not generate an entry chunk for SSR in serverless mode`); } @@ -479,7 +489,6 @@ function buildManifest( base: settings.config.base, compressHTML: settings.config.compressHTML, assetsPrefix: settings.config.build.assetsPrefix, - markdown: settings.config.markdown, componentMetadata: Array.from(internals.componentMetadata), renderers: [], clientDirectives: Array.from(settings.clientDirectives), diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index d4f156ff5..59bd76b53 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -8,7 +8,7 @@ import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { BUNDLED_THEMES } from 'shiki'; import { z } from 'zod'; -import { appendForwardSlash, prependForwardSlash, trimSlashes } from '../path.js'; +import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } from '../path.js'; const ASTRO_CONFIG_DEFAULTS = { root: '.', @@ -29,10 +29,10 @@ const ASTRO_CONFIG_DEFAULTS = { split: false, excludeMiddleware: false, }, - compressHTML: false, + compressHTML: true, server: { host: false, - port: 3000, + port: 4321, open: false, }, integrations: [], @@ -88,9 +88,9 @@ export const AstroConfigSchema = z.object({ .optional() .default('static'), scopedStyleStrategy: z - .union([z.literal('where'), z.literal('class')]) + .union([z.literal('where'), z.literal('class'), z.literal('attribute')]) .optional() - .default('where'), + .default('attribute'), adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(), integrations: z.preprocess( // preprocess @@ -125,7 +125,15 @@ export const AstroConfigSchema = z.object({ .optional() .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() @@ -184,7 +192,7 @@ export const AstroConfigSchema = z.object({ }), }) .default({ - service: { entrypoint: 'astro/assets/services/squoosh', config: {} }, + service: { entrypoint: 'astro/assets/services/sharp', config: {} }, }), markdown: z .object({ @@ -363,22 +371,14 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) { ) { config.build.client = new URL('./dist/client/', config.outDir); } - const trimmedBase = trimSlashes(config.base); - // If there is no base but there is a base for site config, warn. - const sitePathname = config.site && new URL(config.site).pathname; - if (!trimmedBase.length && sitePathname && sitePathname !== '/') { - config.base = sitePathname; - /* eslint-disable no-console */ - console.warn(`The site configuration value includes a pathname of ${sitePathname} but there is no base configuration. - -A future version of Astro will stop using the site pathname when producing <link> and <script> tags. Set your site's base with the base configuration.`); - } - - if (trimmedBase.length && config.trailingSlash === 'never') { - config.base = prependForwardSlash(trimmedBase); + // Handle `base` trailing slash based on `trailingSlash` config + if (config.trailingSlash === 'never') { + config.base = prependForwardSlash(removeTrailingForwardSlash(config.base)); + } else if (config.trailingSlash === 'always') { + config.base = prependForwardSlash(appendForwardSlash(config.base)); } else { - config.base = prependForwardSlash(appendForwardSlash(trimmedBase)); + config.base = prependForwardSlash(config.base); } return config; diff --git a/packages/astro/src/core/cookies/cookies.ts b/packages/astro/src/core/cookies/cookies.ts index 013357f32..604f30e63 100644 --- a/packages/astro/src/core/cookies/cookies.ts +++ b/packages/astro/src/core/cookies/cookies.ts @@ -15,14 +15,14 @@ interface AstroCookieSetOptions { type AstroCookieDeleteOptions = Pick<AstroCookieSetOptions, 'domain' | 'path'>; interface AstroCookieInterface { - value: string | undefined; + value: string; json(): Record<string, any>; number(): number; boolean(): boolean; } interface AstroCookiesInterface { - get(key: string): AstroCookieInterface; + get(key: string): AstroCookieInterface | undefined; has(key: string): boolean; set( key: string, @@ -37,7 +37,7 @@ const DELETED_VALUE = 'deleted'; const responseSentSymbol = Symbol.for('astro.responseSent'); class AstroCookie implements AstroCookieInterface { - constructor(public value: string | undefined) {} + constructor(public value: string) {} json() { if (this.value === undefined) { throw new Error(`Cannot convert undefined to an object.`); @@ -97,20 +97,22 @@ class AstroCookies implements AstroCookiesInterface { * @param key The cookie to get. * @returns An object containing the cookie value as well as convenience methods for converting its value. */ - get(key: string): AstroCookie { + get(key: string): AstroCookie | undefined { // Check for outgoing Set-Cookie values first if (this.#outgoing?.has(key)) { let [serializedValue, , isSetValue] = this.#outgoing.get(key)!; if (isSetValue) { return new AstroCookie(serializedValue); } else { - return new AstroCookie(undefined); + return undefined; } } const values = this.#ensureParsed(); - const value = values[key]; - return new AstroCookie(value); + if (key in values) { + const value = values[key]; + return new AstroCookie(value); + } } /** diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 0fb64ef69..efb78d137 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -234,37 +234,12 @@ export async function createVite( result = vite.mergeConfig(result, settings.config.vite || {}); } result = vite.mergeConfig(result, commandConfig); - if (result.plugins) { - sortPlugins(result.plugins); - } result.customLogger = vite.createLogger(result.logLevel ?? 'warn'); return result; } -function isVitePlugin(plugin: vite.PluginOption): plugin is vite.Plugin { - return Boolean(plugin?.hasOwnProperty('name')); -} - -function findPluginIndexByName(pluginOptions: vite.PluginOption[], name: string): number { - return pluginOptions.findIndex(function (pluginOption) { - // Use isVitePlugin to ignore nulls, booleans, promises, and arrays - // CAUTION: could be a problem if a plugin we're searching for becomes async! - return isVitePlugin(pluginOption) && pluginOption.name === name; - }); -} - -function sortPlugins(pluginOptions: vite.PluginOption[]) { - // HACK: move mdxPlugin to top because it needs to run before internal JSX plugin - const mdxPluginIndex = findPluginIndexByName(pluginOptions, '@mdx-js/rollup'); - if (mdxPluginIndex === -1) return; - const jsxPluginIndex = findPluginIndexByName(pluginOptions, 'astro:jsx'); - const mdxPlugin = pluginOptions[mdxPluginIndex]; - pluginOptions.splice(mdxPluginIndex, 1); - pluginOptions.splice(jsxPluginIndex, 0, mdxPlugin); -} - const COMMON_DEPENDENCIES_NOT_ASTRO = [ 'autoprefixer', 'react', diff --git a/packages/astro/src/core/endpoint/index.ts b/packages/astro/src/core/endpoint/index.ts index 485190e47..97b26e380 100644 --- a/packages/astro/src/core/endpoint/index.ts +++ b/packages/astro/src/core/endpoint/index.ts @@ -7,13 +7,13 @@ import type { Params, } from '../../@types/astro'; import type { Environment, RenderContext } from '../render/index'; - import { renderEndpoint } from '../../runtime/server/index.js'; import { ASTRO_VERSION } from '../constants.js'; import { AstroCookies, attachToResponse } from '../cookies/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; import { warn } from '../logger/core.js'; import { callMiddleware } from '../middleware/callMiddleware.js'; + const clientAddressSymbol = Symbol.for('astro.clientAddress'); const clientLocalsSymbol = Symbol.for('astro.locals'); @@ -117,11 +117,11 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput> onRequest as MiddlewareEndpointHandler, context, async () => { - return await renderEndpoint(mod, context, env.ssr); + return await renderEndpoint(mod, context, env.ssr, env.logging); } ); } else { - response = await renderEndpoint(mod, context, env.ssr); + response = await renderEndpoint(mod, context, env.ssr, env.logging); } if (response instanceof Response) { diff --git a/packages/astro/src/core/logger/console.ts b/packages/astro/src/core/logger/console.ts index dfe732bd7..f39f6b74d 100644 --- a/packages/astro/src/core/logger/console.ts +++ b/packages/astro/src/core/logger/console.ts @@ -15,7 +15,7 @@ export const consoleLogDestination = { function getPrefix() { let prefix = ''; - let type = event.type; + let type = event.label; if (type) { // hide timestamp when type is undefined prefix += dim(dateTimeFormat.format(new Date()) + ' '); diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts index 4f0c281e0..e5d0aee1f 100644 --- a/packages/astro/src/core/logger/core.ts +++ b/packages/astro/src/core/logger/core.ts @@ -6,7 +6,6 @@ interface LogWritable<T> { } export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino -export type LoggerEvent = 'info' | 'warn' | 'error'; export interface LogOptions { dest: LogWritable<LogMessage>; @@ -29,7 +28,7 @@ export const dateTimeFormat = new Intl.DateTimeFormat([], { }); export interface LogMessage { - type: string | null; + label: string | null; level: LoggerLevel; message: string; } @@ -43,11 +42,11 @@ export const levels: Record<LoggerLevel, number> = { }; /** Full logging API */ -export function log(opts: LogOptions, level: LoggerLevel, type: string | null, message: string) { +export function log(opts: LogOptions, level: LoggerLevel, label: string | null, message: string) { const logLevel = opts.level; const dest = opts.dest; const event: LogMessage = { - type, + label, level, message, }; @@ -61,18 +60,18 @@ export function log(opts: LogOptions, level: LoggerLevel, type: string | null, m } /** Emit a user-facing message. Useful for UI and other console messages. */ -export function info(opts: LogOptions, type: string | null, message: string) { - return log(opts, 'info', type, message); +export function info(opts: LogOptions, label: string | null, message: string) { + return log(opts, 'info', label, message); } /** Emit a warning message. Useful for high-priority messages that aren't necessarily errors. */ -export function warn(opts: LogOptions, type: string | null, message: string) { - return log(opts, 'warn', type, message); +export function warn(opts: LogOptions, label: string | null, message: string) { + return log(opts, 'warn', label, message); } /** Emit a error message, Useful when Astro can't recover from some error. */ -export function error(opts: LogOptions, type: string | null, message: string) { - return log(opts, 'error', type, message); +export function error(opts: LogOptions, label: string | null, message: string) { + return log(opts, 'error', label, message); } type LogFn = typeof info | typeof warn | typeof error; @@ -127,3 +126,53 @@ export function timerMessage(message: string, startTime: number = Date.now()) { timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`; return `${message} ${dim(timeDisplay)}`; } + +export class Logger { + options: LogOptions; + constructor(options: LogOptions) { + this.options = options; + } + + info(label: string, message: string) { + info(this.options, label, message); + } + warn(label: string, message: string) { + warn(this.options, label, message); + } + error(label: string, message: string) { + error(this.options, label, message); + } + debug(label: string, message: string) { + debug(this.options, label, message); + } +} + +export class AstroIntegrationLogger { + options: LogOptions; + label: string; + + constructor(logging: LogOptions, label: string) { + this.options = logging; + this.label = label; + } + + /** + * Creates a new logger instance with a new label, but the same log options. + */ + fork(label: string): AstroIntegrationLogger { + return new AstroIntegrationLogger(this.options, label); + } + + info(message: string) { + info(this.options, this.label, message); + } + warn(message: string) { + warn(this.options, this.label, message); + } + error(message: string) { + error(this.options, this.label, message); + } + debug(message: string) { + debug(this.options, this.label, message); + } +} diff --git a/packages/astro/src/core/logger/node.ts b/packages/astro/src/core/logger/node.ts index 513ba257e..aeef4bd84 100644 --- a/packages/astro/src/core/logger/node.ts +++ b/packages/astro/src/core/logger/node.ts @@ -21,19 +21,19 @@ export const nodeLogDestination = new Writable({ function getPrefix() { let prefix = ''; - let type = event.type; - if (type) { + let label = event.label; + if (label) { // hide timestamp when type is undefined prefix += dim(dateTimeFormat.format(new Date()) + ' '); if (event.level === 'info') { - type = bold(cyan(`[${type}]`)); + label = bold(cyan(`[${label}]`)); } else if (event.level === 'warn') { - type = bold(yellow(`[${type}]`)); + label = bold(yellow(`[${label}]`)); } else if (event.level === 'error') { - type = bold(red(`[${type}]`)); + label = bold(red(`[${label}]`)); } - prefix += `${type} `; + prefix += `${label} `; } return reset(prefix); } @@ -87,7 +87,7 @@ export const nodeLogOptions: Required<LogOptions> = { }; export interface LogMessage { - type: string | null; + label: string | null; level: LoggerLevel; message: string; } diff --git a/packages/astro/src/core/polyfill.ts b/packages/astro/src/core/polyfill.ts index 99e0d5cc5..daceb53e2 100644 --- a/packages/astro/src/core/polyfill.ts +++ b/packages/astro/src/core/polyfill.ts @@ -1,8 +1,21 @@ -import { polyfill } from '@astrojs/webapi'; +import { File } from 'node:buffer'; +import crypto from 'node:crypto'; + +// NOTE: This file does not intend to polyfill everything that exists, its main goal is to make life easier +// for users deploying to runtime that do support these features. In the future, we hope for this file to disappear. export function apply() { - // polyfill WebAPIs for Node.js runtime - polyfill(globalThis, { - exclude: 'window document', - }); + // Remove when Node 18 is dropped for Node 20 + if (!globalThis.crypto) { + Object.defineProperty(globalThis, 'crypto', { + value: crypto.webcrypto, + }); + } + + // Remove when Node 18 is dropped for Node 20 + if (!globalThis.File) { + Object.defineProperty(globalThis, 'File', { + value: File, + }); + } } diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index d6228fbbe..eca7552e6 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -42,7 +42,6 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) { links: renderContext.links, styles: renderContext.styles, logging: env.logging, - markdown: env.markdown, params: renderContext.params, pathname: renderContext.pathname, componentMetadata: renderContext.componentMetadata, @@ -59,12 +58,7 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) { locals: renderContext.locals ?? {}, }); - // Support `export const components` for `MDX` pages - if (typeof (mod as any).components === 'object') { - Object.assign(renderContext.props, { components: (mod as any).components }); - } - - let response = await runtimeRenderPage( + const response = await runtimeRenderPage( result, Component, renderContext.props, diff --git a/packages/astro/src/core/render/environment.ts b/packages/astro/src/core/render/environment.ts index de7cbe6a8..32dfb454b 100644 --- a/packages/astro/src/core/render/environment.ts +++ b/packages/astro/src/core/render/environment.ts @@ -1,4 +1,3 @@ -import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark'; import type { AstroSettings, RuntimeMode, SSRLoadedRenderer } from '../../@types/astro'; import type { LogOptions } from '../logger/core.js'; import type { ModuleLoader } from '../module-loader'; @@ -16,10 +15,6 @@ export interface Environment { adapterName?: string; /** logging options */ logging: LogOptions; - /** - * Used to support `Astro.__renderMarkdown` for legacy `<Markdown />` component - */ - markdown: MarkdownRenderingOptions; /** "development" or "production" */ mode: RuntimeMode; compressHTML: boolean; diff --git a/packages/astro/src/core/render/params-and-props.ts b/packages/astro/src/core/render/params-and-props.ts index a5e4fa222..fc08c495e 100644 --- a/packages/astro/src/core/render/params-and-props.ts +++ b/packages/astro/src/core/render/params-and-props.ts @@ -33,7 +33,6 @@ export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise mod, route, routeCache, - isValidate: true, logging, ssr, }); diff --git a/packages/astro/src/core/render/result.ts b/packages/astro/src/core/render/result.ts index 968b232d4..72fa4ddcf 100644 --- a/packages/astro/src/core/render/result.ts +++ b/packages/astro/src/core/render/result.ts @@ -1,4 +1,3 @@ -import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark'; import type { AstroGlobal, AstroGlobalPartial, @@ -27,10 +26,6 @@ export interface CreateResultArgs { */ ssr: boolean; logging: LogOptions; - /** - * Used to support `Astro.__renderMarkdown` for legacy `<Markdown />` component - */ - markdown: MarkdownRenderingOptions; params: Params; pathname: string; renderers: SSRLoadedRenderer[]; @@ -128,10 +123,8 @@ class Slots { } } -let renderMarkdown: any = null; - export function createResult(args: CreateResultArgs): SSRResult { - const { markdown, params, request, resolve, locals } = args; + const { params, request, resolve, locals } = args; const url = new URL(request.url); const headers = new Headers(); @@ -222,31 +215,6 @@ export function createResult(args: CreateResultArgs): SSRResult { slots: astroSlots as unknown as AstroGlobal['slots'], }; - Object.defineProperty(Astro, '__renderMarkdown', { - // Ensure this API is not exposed to users - enumerable: false, - writable: false, - // TODO: Remove this hole "Deno" logic once our plugin gets Deno support - value: async function (content: string, opts: MarkdownRenderingOptions) { - // @ts-expect-error - if (typeof Deno !== 'undefined') { - throw new Error('Markdown is not supported in Deno SSR'); - } - - if (!renderMarkdown) { - // The package is saved in this variable because Vite is too smart - // and will try to inline it in buildtime - let astroRemark = '@astrojs/'; - astroRemark += 'markdown-remark'; - - renderMarkdown = (await import(astroRemark)).renderMarkdown; - } - - const { code } = await renderMarkdown(content, { ...markdown, ...(opts ?? {}) }); - return code; - }, - }); - return Astro; }, resolve, diff --git a/packages/astro/src/core/render/route-cache.ts b/packages/astro/src/core/render/route-cache.ts index 7ad247ef8..804f09183 100644 --- a/packages/astro/src/core/render/route-cache.ts +++ b/packages/astro/src/core/render/route-cache.ts @@ -18,7 +18,6 @@ interface CallGetStaticPathsOptions { mod: ComponentInstance; route: RouteData; routeCache: RouteCache; - isValidate: boolean; logging: LogOptions; ssr: boolean; } @@ -27,7 +26,6 @@ export async function callGetStaticPaths({ mod, route, routeCache, - isValidate, logging, ssr, }: CallGetStaticPathsOptions): Promise<GetStaticPathsResultKeyed> { @@ -58,14 +56,7 @@ export async function callGetStaticPaths({ }, }); - // Flatten the array before validating the content, otherwise users using `.map` will run into errors - if (Array.isArray(staticPaths)) { - staticPaths = staticPaths.flat(); - } - - if (isValidate) { - validateGetStaticPathsResult(staticPaths, logging, route); - } + validateGetStaticPathsResult(staticPaths, logging, route); const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed; keyedStaticPaths.keyed = new Map<string, GetStaticPathsItem>(); diff --git a/packages/astro/src/core/routing/validation.ts b/packages/astro/src/core/routing/validation.ts index 9a562c044..b5c29b16e 100644 --- a/packages/astro/src/core/routing/validation.ts +++ b/packages/astro/src/core/routing/validation.ts @@ -54,6 +54,15 @@ export function validateGetStaticPathsResult( } result.forEach((pathObject) => { + if ((typeof pathObject === 'object' && Array.isArray(pathObject)) || pathObject === null) { + throw new AstroError({ + ...AstroErrorData.InvalidGetStaticPathsEntry, + message: AstroErrorData.InvalidGetStaticPathsEntry.message( + Array.isArray(pathObject) ? 'array' : typeof pathObject + ), + }); + } + if ( pathObject.params === undefined || pathObject.params === null || @@ -67,16 +76,6 @@ export function validateGetStaticPathsResult( }); } - if (typeof pathObject.params !== 'object') { - throw new AstroError({ - ...AstroErrorData.InvalidGetStaticPathParam, - message: AstroErrorData.InvalidGetStaticPathParam.message(typeof pathObject.params), - location: { - file: route.component, - }, - }); - } - // TODO: Replace those with errors? They technically don't crash the build, but users might miss the warning. - erika, 2022-11-07 for (const [key, val] of Object.entries(pathObject.params)) { if (!(typeof val === 'undefined' || typeof val === 'string' || typeof val === 'number')) { diff --git a/packages/astro/src/integrations/astroFeaturesValidation.ts b/packages/astro/src/integrations/astroFeaturesValidation.ts new file mode 100644 index 000000000..c494b35f4 --- /dev/null +++ b/packages/astro/src/integrations/astroFeaturesValidation.ts @@ -0,0 +1,157 @@ +import type { + AstroAssetsFeature, + AstroConfig, + AstroFeatureMap, + SupportsKind, +} from '../@types/astro'; +import { error, warn, type LogOptions } from '../core/logger/core.js'; + +const STABLE = 'stable'; +const DEPRECATED = 'deprecated'; +const UNSUPPORTED = 'unsupported'; +const EXPERIMENTAL = 'experimental'; + +const UNSUPPORTED_ASSETS_FEATURE: AstroAssetsFeature = { + supportKind: UNSUPPORTED, + isSquooshCompatible: false, + isSharpCompatible: false, +}; + +// NOTE: remove for Astro 4.0 +const ALL_UNSUPPORTED: Required<AstroFeatureMap> = { + serverOutput: UNSUPPORTED, + staticOutput: UNSUPPORTED, + hybridOutput: UNSUPPORTED, + assets: UNSUPPORTED_ASSETS_FEATURE, +}; + +type ValidationResult = { + [Property in keyof AstroFeatureMap]: boolean; +}; + +/** + * Checks whether an adapter supports certain features that are enabled via Astro configuration. + * + * If a configuration is enabled and "unlocks" a feature, but the adapter doesn't support, the function + * will throw a runtime error. + * + */ +export function validateSupportedFeatures( + adapterName: string, + featureMap: AstroFeatureMap = ALL_UNSUPPORTED, + config: AstroConfig, + logging: LogOptions +): ValidationResult { + const { + assets = UNSUPPORTED_ASSETS_FEATURE, + serverOutput = UNSUPPORTED, + staticOutput = UNSUPPORTED, + hybridOutput = UNSUPPORTED, + } = featureMap; + const validationResult: ValidationResult = {}; + + validationResult.staticOutput = validateSupportKind( + staticOutput, + adapterName, + logging, + 'staticOutput', + () => config?.output === 'static' + ); + + validationResult.hybridOutput = validateSupportKind( + hybridOutput, + adapterName, + logging, + 'hybridOutput', + () => config?.output === 'hybrid' + ); + + validationResult.serverOutput = validateSupportKind( + serverOutput, + adapterName, + logging, + 'serverOutput', + () => config?.output === 'server' + ); + validationResult.assets = validateAssetsFeature(assets, adapterName, config, logging); + + return validationResult; +} + +function validateSupportKind( + supportKind: SupportsKind, + adapterName: string, + logging: LogOptions, + featureName: string, + hasCorrectConfig: () => boolean +): boolean { + if (supportKind === STABLE) { + return true; + } else if (supportKind === DEPRECATED) { + featureIsDeprecated(adapterName, logging); + } else if (supportKind === EXPERIMENTAL) { + featureIsExperimental(adapterName, logging); + } + + if (hasCorrectConfig() && supportKind === UNSUPPORTED) { + featureIsUnsupported(adapterName, logging, featureName); + return false; + } else { + return true; + } +} + +function featureIsUnsupported(adapterName: string, logging: LogOptions, featureName: string) { + error( + logging, + `${adapterName}`, + `The feature ${featureName} is not supported by the adapter ${adapterName}.` + ); +} + +function featureIsExperimental(adapterName: string, logging: LogOptions) { + warn(logging, `${adapterName}`, 'The feature is experimental and subject to issues or changes.'); +} + +function featureIsDeprecated(adapterName: string, logging: LogOptions) { + warn( + logging, + `${adapterName}`, + 'The feature is deprecated and will be moved in the next release.' + ); +} + +const SHARP_SERVICE = 'astro/assets/services/sharp'; +const SQUOOSH_SERVICE = 'astro/assets/services/squoosh'; + +function validateAssetsFeature( + assets: AstroAssetsFeature, + adapterName: string, + config: AstroConfig, + logging: LogOptions +): boolean { + const { + supportKind = UNSUPPORTED, + isSharpCompatible = false, + isSquooshCompatible = false, + } = assets; + if (config?.image?.service?.entrypoint === SHARP_SERVICE && !isSharpCompatible) { + error( + logging, + 'astro', + `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Sharp".` + ); + return false; + } + + if (config?.image?.service?.entrypoint === SQUOOSH_SERVICE && !isSquooshCompatible) { + error( + logging, + 'astro', + `The currently selected adapter \`${adapterName}\` is not compatible with the image service "Squoosh".` + ); + return false; + } + + return validateSupportKind(supportKind, adapterName, logging, 'assets', () => true); +} diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index cf50df0e1..71c5a5e63 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -4,7 +4,9 @@ import type { AddressInfo } from 'node:net'; import { fileURLToPath } from 'node:url'; import type { InlineConfig, ViteDevServer } from 'vite'; import type { + AstroAdapter, AstroConfig, + AstroIntegration, AstroRenderer, AstroSettings, ContentEntryType, @@ -16,8 +18,9 @@ import type { SerializedSSRManifest } from '../core/app/types'; import type { PageBuildData } from '../core/build/types'; import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js'; import { mergeConfig } from '../core/config/index.js'; -import { info, type LogOptions } from '../core/logger/core.js'; +import { AstroIntegrationLogger, error, info, warn, type LogOptions } from '../core/logger/core.js'; import { isServerLikeOutput } from '../prerender/utils.js'; +import { validateSupportedFeatures } from './astroFeaturesValidation.js'; async function withTakingALongTimeMsg<T>({ name, @@ -38,6 +41,19 @@ async function withTakingALongTimeMsg<T>({ return result; } +// Used internally to store instances of loggers. +const Loggers = new WeakMap<AstroIntegration, AstroIntegrationLogger>(); + +function getLogger(integration: AstroIntegration, logging: LogOptions) { + if (Loggers.has(integration)) { + // SAFETY: we check the existence in the if block + return Loggers.get(integration)!; + } + const logger = new AstroIntegrationLogger(logging, integration.name); + Loggers.set(integration, logger); + return logger; +} + export async function runHookConfigSetup({ settings, command, @@ -72,6 +88,8 @@ export async function runHookConfigSetup({ * ``` */ if (integration.hooks?.['astro:config:setup']) { + const logger = getLogger(integration, logging); + const hooks: HookParameters<'astro:config:setup'> = { config: updatedConfig, command, @@ -107,6 +125,7 @@ export async function runHookConfigSetup({ } addedClientDirectives.set(name, buildClientDirectiveEntrypoint(name, entrypoint)); }, + logger, }; // --- @@ -167,6 +186,7 @@ export async function runHookConfigDone({ logging: LogOptions; }) { for (const integration of settings.config.integrations) { + const logger = getLogger(integration, logging); if (integration?.hooks?.['astro:config:done']) { await withTakingALongTimeMsg({ name: integration.name, @@ -178,8 +198,44 @@ export async function runHookConfigDone({ `Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.` ); } + if (!adapter.supportedAstroFeatures) { + // NOTE: throw an error in Astro 4.0 + warn( + logging, + 'astro', + `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 { + const validationResult = validateSupportedFeatures( + adapter.name, + adapter.supportedAstroFeatures, + settings.config, + logging + ); + for (const [featureName, supported] of Object.entries(validationResult)) { + if (!supported) { + error( + logging, + 'astro', + `The adapter ${adapter.name} doesn't support the feature ${featureName}. Your project won't be built. You should not use it.` + ); + } + } + if (!validationResult.assets) { + info( + logging, + 'astro', + `The selected adapter ${adapter.name} does not support Sharp or Squoosh for image processing. To ensure your project is still able to build, image processing has been disabled.` + ); + settings.config.image.service = { + entrypoint: 'astro/assets/services/noop', + config: {}, + }; + } + } settings.adapter = adapter; }, + logger, }), logging, }); @@ -198,9 +254,10 @@ export async function runHookServerSetup({ }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:server:setup']) { + const logger = getLogger(integration, logging); await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:setup']({ server }), + hookResult: integration.hooks['astro:server:setup']({ server, logger }), logging, }); } @@ -217,10 +274,12 @@ export async function runHookServerStart({ logging: LogOptions; }) { for (const integration of config.integrations) { + const logger = getLogger(integration, logging); + if (integration?.hooks?.['astro:server:start']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:start']({ address }), + hookResult: integration.hooks['astro:server:start']({ address, logger }), logging, }); } @@ -235,10 +294,12 @@ export async function runHookServerDone({ logging: LogOptions; }) { for (const integration of config.integrations) { + const logger = getLogger(integration, logging); + if (integration?.hooks?.['astro:server:done']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:server:done'](), + hookResult: integration.hooks['astro:server:done']({ logger }), logging, }); } @@ -254,9 +315,11 @@ export async function runHookBuildStart({ }) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:start']) { + const logger = getLogger(integration, logging); + await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:start'](), + hookResult: integration.hooks['astro:build:start']({ logger }), logging, }); } @@ -280,6 +343,8 @@ export async function runHookBuildSetup({ for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:setup']) { + const logger = getLogger(integration, logging); + await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:setup']({ @@ -289,6 +354,7 @@ export async function runHookBuildSetup({ updateConfig: (newConfig) => { updatedConfig = mergeConfig(updatedConfig, newConfig); }, + logger, }), logging, }); @@ -315,12 +381,15 @@ export async function runHookBuildSsr({ }: RunHookBuildSsr) { for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:ssr']) { + const logger = getLogger(integration, logging); + await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:ssr']({ manifest, entryPoints, middlewareEntryPoint, + logger, }), logging, }); @@ -338,10 +407,12 @@ export async function runHookBuildGenerated({ const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; for (const integration of config.integrations) { + const logger = getLogger(integration, logging); + if (integration?.hooks?.['astro:build:generated']) { await withTakingALongTimeMsg({ name: integration.name, - hookResult: integration.hooks['astro:build:generated']({ dir }), + hookResult: integration.hooks['astro:build:generated']({ dir, logger }), logging, }); } @@ -361,15 +432,34 @@ export async function runHookBuildDone({ config, pages, routes, logging }: RunHo for (const integration of config.integrations) { if (integration?.hooks?.['astro:build:done']) { + const logger = getLogger(integration, logging); + await withTakingALongTimeMsg({ name: integration.name, hookResult: integration.hooks['astro:build:done']({ pages: pages.map((p) => ({ pathname: p })), dir, routes, + logger, }), logging, }); } } } + +export function isFunctionPerRouteEnabled(adapter: AstroAdapter | undefined): boolean { + if (adapter?.adapterFeatures?.functionPerRoute === true) { + return true; + } else { + return false; + } +} + +export function isEdgeMiddlewareEnabled(adapter: AstroAdapter | undefined): boolean { + if (adapter?.adapterFeatures?.edgeMiddleware === true) { + return true; + } else { + return false; + } +} diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts index c56ab7646..89c35957c 100644 --- a/packages/astro/src/runtime/server/endpoint.ts +++ b/packages/astro/src/runtime/server/endpoint.ts @@ -1,28 +1,56 @@ import type { APIContext, EndpointHandler, Params } from '../../@types/astro'; +import { type LogOptions, warn } from '../../core/logger/core.js'; -function getHandlerFromModule(mod: EndpointHandler, method: string) { +function getHandlerFromModule(mod: EndpointHandler, method: string, logging: LogOptions) { + const lowerCaseMethod = method.toLowerCase(); + + // TODO: remove in Astro 4.0 + if (mod[lowerCaseMethod]) { + warn( + logging, + 'astro', + `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 (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']) { + return mod['ALL']; + } // Otherwise, no handler found. return undefined; } /** Renders an endpoint request to completion, returning the body. */ -export async function renderEndpoint(mod: EndpointHandler, context: APIContext, ssr: boolean) { - const { request, params } = context; - const chosenMethod = request.method?.toLowerCase(); - const handler = getHandlerFromModule(mod, chosenMethod); - if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'get') { +export async function renderEndpoint( + mod: EndpointHandler, + context: APIContext, + ssr: boolean, + logging: LogOptions +) { + const { request } = context; + + const chosenMethod = request.method?.toUpperCase(); + const handler = getHandlerFromModule(mod, chosenMethod, logging); + // TODO: remove the 'get' check in Astro 4.0 + if (!ssr && ssr === false && chosenMethod && chosenMethod !== 'GET' && chosenMethod !== 'get') { // eslint-disable-next-line no-console console.warn(` ${chosenMethod} requests are not available when building a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` with an \`export const prerender = false\` to handle ${chosenMethod} requests.`); @@ -40,35 +68,10 @@ ${chosenMethod} requests are not available when building a static site. Update y return response; } - // TODO: Remove support for old API in Astro 3.0 - if (handler.length > 1) { - // eslint-disable-next-line no-console - console.warn(` -API routes with 2 arguments have been deprecated. Instead they take a single argument in the form of: - -export function get({ params, request }) { - //... -} - -Update your code to remove this warning.`); - } - const proxy = new Proxy(context, { get(target, prop) { if (prop in target) { return Reflect.get(target, prop); - } else if (prop in params) { - // TODO: Remove support for old API in Astro 3.0 - // eslint-disable-next-line no-console - console.warn(` -API routes no longer pass params as the first argument. Instead an object containing a params property is provided in the form of: - -export function get({ params }) { - // ... -} - -Update your code to remove this warning.`); - return Reflect.get(params, prop); } else { return undefined; } diff --git a/packages/astro/src/runtime/server/render/page.ts b/packages/astro/src/runtime/server/render/page.ts index cabbe8dae..74e8a45b7 100644 --- a/packages/astro/src/runtime/server/render/page.ts +++ b/packages/astro/src/runtime/server/render/page.ts @@ -2,7 +2,6 @@ import type { RouteData, SSRResult } from '../../../@types/astro'; import { renderComponentToString, type NonAstroPageComponent } from './component.js'; import type { AstroComponentFactory } from './index'; -import { createResponse } from '../response.js'; import { isAstroComponentFactory } from './astro/index.js'; import { renderToReadableStream, renderToString } from './astro/render.js'; import { encoder } from './common.js'; @@ -64,6 +63,6 @@ export async function renderPage( body = encoder.encode(body); headers.set('Content-Length', body.byteLength.toString()); } - const response = createResponse(body, { ...init, headers }); + const response = new Response(body, { ...init, headers }); return response; } diff --git a/packages/astro/src/runtime/server/response.ts b/packages/astro/src/runtime/server/response.ts deleted file mode 100644 index bcfda19aa..000000000 --- a/packages/astro/src/runtime/server/response.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { streamAsyncIterator } from './util.js'; - -const isNodeJS = - typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]'; - -let StreamingCompatibleResponse: typeof Response | undefined; - -function createResponseClass() { - StreamingCompatibleResponse = class extends Response { - #isStream: boolean; - #body: any; - constructor(body?: BodyInit | null, init?: ResponseInit) { - let isStream = body instanceof ReadableStream; - super(isStream ? null : body, init); - this.#isStream = isStream; - this.#body = body; - } - - get body() { - return this.#body; - } - - async text(): Promise<string> { - if (this.#isStream && isNodeJS) { - let decoder = new TextDecoder(); - let body = this.#body; - let out = ''; - for await (let chunk of streamAsyncIterator(body)) { - out += decoder.decode(chunk); - } - return out; - } - return super.text(); - } - - async arrayBuffer(): Promise<ArrayBuffer> { - if (this.#isStream && isNodeJS) { - let body = this.#body; - let chunks: Uint8Array[] = []; - let len = 0; - for await (let chunk of streamAsyncIterator(body)) { - chunks.push(chunk); - len += chunk.length; - } - let ab = new Uint8Array(len); - let offset = 0; - for (const chunk of chunks) { - ab.set(chunk, offset); - offset += chunk.length; - } - return ab; - } - return super.arrayBuffer(); - } - - clone() { - return new StreamingCompatibleResponse!(this.#body, { - status: this.status, - statusText: this.statusText, - headers: this.headers, - }); - } - }; - - return StreamingCompatibleResponse; -} - -type CreateResponseFn = (body?: BodyInit | null, init?: ResponseInit) => Response; - -export const createResponse: CreateResponseFn = isNodeJS - ? (body, init) => { - if (typeof body === 'string' || ArrayBuffer.isView(body)) { - return new Response(body, init); - } - if (typeof StreamingCompatibleResponse === 'undefined') { - return new (createResponseClass())(body, init); - } - return new StreamingCompatibleResponse(body, init); - } - : (body, init) => new Response(body, init); diff --git a/packages/astro/src/vite-plugin-astro-postprocess/index.ts b/packages/astro/src/vite-plugin-astro-postprocess/index.ts index 9a2e185af..39acd000c 100644 --- a/packages/astro/src/vite-plugin-astro-postprocess/index.ts +++ b/packages/astro/src/vite-plugin-astro-postprocess/index.ts @@ -1,4 +1,5 @@ import { parse } from 'acorn'; +import type { Node as ESTreeNode } from 'estree-walker'; import { walk } from 'estree-walker'; import MagicString from 'magic-string'; import type { Plugin } from 'vite'; @@ -28,7 +29,7 @@ export default function astro(): Plugin { sourceType: 'module', }); - walk(ast, { + walk(ast as ESTreeNode, { enter(node: any) { // Transform `Astro.glob("./pages/*.astro")` to `Astro.glob(import.meta.glob("./pages/*.astro"), () => "./pages/*.astro")` // Also handle for `Astro2.glob()` diff --git a/packages/astro/src/vite-plugin-astro-server/environment.ts b/packages/astro/src/vite-plugin-astro-server/environment.ts index bcf783bf2..ce7b92662 100644 --- a/packages/astro/src/vite-plugin-astro-server/environment.ts +++ b/packages/astro/src/vite-plugin-astro-server/environment.ts @@ -17,7 +17,6 @@ export function createDevelopmentEnvironment( let env = createEnvironment({ adapterName: manifest.adapterName, logging, - markdown: manifest.markdown, mode, // This will be overridden in the dev server renderers: [], diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 681aac341..cb74f9d9a 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -92,7 +92,6 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest entryModules: {}, routes: [], adapterName: '', - markdown: settings.config.markdown, clientDirectives: settings.clientDirectives, renderers: [], base: settings.config.base, diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts index b641503a6..ae476f9be 100644 --- a/packages/astro/src/vite-plugin-astro-server/request.ts +++ b/packages/astro/src/vite-plugin-astro-server/request.ts @@ -48,7 +48,7 @@ export async function handleRequest({ // Add config.base back to url before passing it to SSR url.pathname = removeTrailingForwardSlash(config.base) + url.pathname; - // HACK! @astrojs/image uses query params for the injected route in `dev` + // HACK! astro:assets uses query params for the injected route in `dev` if (!buildingToSSR && pathname !== '/_image') { // Prevent user from depending on search params when not doing SSR. // NOTE: Create an array copy here because deleting-while-iterating diff --git a/packages/astro/src/vite-plugin-head/index.ts b/packages/astro/src/vite-plugin-head/index.ts index 9cfdc739f..ca95a334e 100644 --- a/packages/astro/src/vite-plugin-head/index.ts +++ b/packages/astro/src/vite-plugin-head/index.ts @@ -16,7 +16,7 @@ export default function configHeadVitePlugin(): vite.Plugin { function propagateMetadata< P extends keyof PluginMetadata['astro'], - V extends PluginMetadata['astro'][P] + V extends PluginMetadata['astro'][P], >( this: { getModuleInfo(id: string): ModuleInfo | null }, id: string, diff --git a/packages/astro/src/vite-plugin-inject-env-ts/index.ts b/packages/astro/src/vite-plugin-inject-env-ts/index.ts index 9c2874fb9..8ac7c5281 100644 --- a/packages/astro/src/vite-plugin-inject-env-ts/index.ts +++ b/packages/astro/src/vite-plugin-inject-env-ts/index.ts @@ -85,8 +85,6 @@ export async function setUpEnvTs({ let referenceDefs: string[] = []; if (settings.config.experimental.assets) { referenceDefs.push('/// <reference types="astro/client-image" />'); - } else if (settings.config.integrations.find((i) => i.name === '@astrojs/image')) { - referenceDefs.push('/// <reference types="@astrojs/image/client" />'); } else { referenceDefs.push('/// <reference types="astro/client" />'); } diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 76bfba296..9a05074fb 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -39,15 +39,27 @@ describe('CSS', function () { it('HTML and CSS scoped correctly', async () => { const el1 = $('#dynamic-class'); const el2 = $('#dynamic-vis'); - const classes = $('#class').attr('class').split(' '); - const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name)); + const classes = $('#class'); + let scopedAttribute; + for (const [key] of Object.entries(classes[0].attribs)) { + if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) { + // Ema: this is ugly, but for reasons that I don't want to explore, cheerio + // lower case the hash of the attribute + scopedAttribute = key + .toUpperCase() + .replace('data-astro-cid-'.toUpperCase(), 'data-astro-cid-'); + } + } + if (!scopedAttribute) { + throw new Error("Couldn't find scoped attribute"); + } // 1. check HTML - expect(el1.attr('class')).to.equal(`blue ${scopedClass}`); - expect(el2.attr('class')).to.equal(`visible ${scopedClass}`); + expect(el1.attr('class')).to.equal(`blue`); + expect(el2.attr('class')).to.equal(`visible`); // 2. check CSS - const expected = `.blue:where(.${scopedClass}){color:#b0e0e6}.color\\:blue:where(.${scopedClass}){color:#b0e0e6}.visible:where(.${scopedClass}){display:block}`; + const expected = `.blue[${scopedAttribute}],.color\\:blue[${scopedAttribute}]{color:#b0e0e6}.visible[${scopedAttribute}]{display:block}`; expect(bundledCSS).to.include(expected); }); @@ -60,8 +72,12 @@ describe('CSS', function () { expect($('#no-scope').attr('class')).to.equal(undefined); }); - it('Child inheritance', async () => { - expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/); + it('Child inheritance', (done) => { + for (const [key] of Object.entries($('#passed-in')[0].attribs)) { + if (/^data-astro-cid-[A-Za-z0-9-]+/.test(key)) { + done(); + } + } }); it('Using hydrated components adds astro-island styles', async () => { @@ -70,11 +86,11 @@ describe('CSS', function () { }); it('<style lang="sass">', async () => { - expect(bundledCSS).to.match(new RegExp('h1\\:where\\(.astro-[^{]*{color:#90ee90}')); + expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#90ee90}')); }); it('<style lang="scss">', async () => { - expect(bundledCSS).to.match(new RegExp('h1\\:where\\(.astro-[^{]*{color:#ff69b4}')); + expect(bundledCSS).to.match(new RegExp('h1\\[data-astro-cid-[^{]*{color:#ff69b4}')); }); }); @@ -331,10 +347,10 @@ describe('CSS', function () { it('resolves Astro styles', async () => { const allInjectedStyles = $('style').text(); - expect(allInjectedStyles).to.contain('.linked-css:where(.astro-'); - expect(allInjectedStyles).to.contain('.linked-sass:where(.astro-'); - expect(allInjectedStyles).to.contain('.linked-scss:where(.astro-'); - expect(allInjectedStyles).to.contain('.wrapper:where(.astro-'); + expect(allInjectedStyles).to.contain('.linked-css[data-astro-cid-'); + expect(allInjectedStyles).to.contain('.linked-sass[data-astro-cid-'); + expect(allInjectedStyles).to.contain('.linked-scss[data-astro-cid-'); + expect(allInjectedStyles).to.contain('.wrapper[data-astro-cid-'); }); it('resolves Styles from React', async () => { diff --git a/packages/astro/test/astro-envs.test.js b/packages/astro/test/astro-envs.test.js index 402878da5..c923ae065 100644 --- a/packages/astro/test/astro-envs.test.js +++ b/packages/astro/test/astro-envs.test.js @@ -109,7 +109,7 @@ describe('Environment Variables', () => { expect(res.status).to.equal(200); let indexHtml = await res.text(); let $ = cheerio.load(indexHtml); - expect($('#base-url').text()).to.equal('/blog/'); + expect($('#base-url').text()).to.equal('/blog'); }); it('does render destructured builtin SITE env', async () => { @@ -117,7 +117,7 @@ describe('Environment Variables', () => { expect(res.status).to.equal(200); let indexHtml = await res.text(); let $ = cheerio.load(indexHtml); - expect($('#base-url').text()).to.equal('/blog/'); + expect($('#base-url').text()).to.equal('/blog'); }); }); }); diff --git a/packages/astro/test/astro-get-static-paths.test.js b/packages/astro/test/astro-get-static-paths.test.js index 784ff1718..66aa5b94d 100644 --- a/packages/astro/test/astro-get-static-paths.test.js +++ b/packages/astro/test/astro-get-static-paths.test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { loadFixture } from './test-utils.js'; import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; describe('getStaticPaths - build calls', () => { /** @type {import('./test-utils').Fixture} */ @@ -92,11 +92,6 @@ describe('getStaticPaths - dev calls', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/2022/post-1'); diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js index d49868584..f003bc035 100644 --- a/packages/astro/test/astro-global.test.js +++ b/packages/astro/test/astro-global.test.js @@ -54,10 +54,10 @@ describe('Astro Global', () => { const html = await fixture.readFile('/index.html'); const $ = cheerio.load(html); - expect($('#pathname').text()).to.equal('/blog/'); + expect($('#pathname').text()).to.equal('/blog'); expect($('#searchparams').text()).to.equal('{}'); - expect($('#child-pathname').text()).to.equal('/blog/'); - expect($('#nested-child-pathname').text()).to.equal('/blog/'); + expect($('#child-pathname').text()).to.equal('/blog'); + expect($('#nested-child-pathname').text()).to.equal('/blog'); }); it('Astro.site', async () => { diff --git a/packages/astro/test/astro-partial-html.test.js b/packages/astro/test/astro-partial-html.test.js index 6073f1bd1..162c6985d 100644 --- a/packages/astro/test/astro-partial-html.test.js +++ b/packages/astro/test/astro-partial-html.test.js @@ -26,7 +26,7 @@ describe('Partial HTML', async () => { // test 2: correct CSS present const allInjectedStyles = $('style').text(); - expect(allInjectedStyles).to.match(/\:where\(\.astro-[^{]+{color:red}/); + expect(allInjectedStyles).to.match(/\[data-astro-cid-[^{]+{color:red}/); }); it('injects framework styles', async () => { diff --git a/packages/astro/test/cli.test.js b/packages/astro/test/cli.test.js index 7626cc495..82cf7a12d 100644 --- a/packages/astro/test/cli.test.js +++ b/packages/astro/test/cli.test.js @@ -131,8 +131,8 @@ describe('astro cli', () => { // Note: our tests run in parallel so this could be 3000+! expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual( - 3000, - `Expected Port to be >= 3000` + 4321, + `Expected Port to be >= 4321` ); expect(networkURL.port).to.be.equal( localURL.port, diff --git a/packages/astro/test/config-vite-css-target.test.js b/packages/astro/test/config-vite-css-target.test.js index 1dc2cce32..cb9fa8de2 100644 --- a/packages/astro/test/config-vite-css-target.test.js +++ b/packages/astro/test/config-vite-css-target.test.js @@ -32,7 +32,7 @@ describe('CSS', function () { it('vite.build.cssTarget is respected', async () => { expect(bundledCSS).to.match( - new RegExp('.class\\:where\\(.astro-[^{]*{top:0;right:0;bottom:0;left:0}') + new RegExp('.class\\[data-astro-[^{]*{top:0;right:0;bottom:0;left:0}') ); }); }); diff --git a/packages/astro/test/custom-elements.test.js b/packages/astro/test/custom-elements.test.js deleted file mode 100644 index 53a13b478..000000000 --- a/packages/astro/test/custom-elements.test.js +++ /dev/null @@ -1,70 +0,0 @@ -import { expect } from 'chai'; -import { load as cheerioLoad } from 'cheerio'; -import { loadFixture } from './test-utils.js'; - -describe('Custom Elements', () => { - let fixture; - - before(async () => { - fixture = await loadFixture({ - root: './fixtures/custom-elements/', - }); - await fixture.build(); - }); - - it('Work as constructors', async () => { - const html = await fixture.readFile('/ctr/index.html'); - const $ = cheerioLoad(html); - - // test 1: Element rendered - expect($('my-element')).to.have.lengthOf(1); - - // test 2: shadow rendered - expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1); - }); - - it('Works with exported tagName', async () => { - const html = await fixture.readFile('/index.html'); - const $ = cheerioLoad(html); - - // test 1: Element rendered - expect($('my-element')).to.have.lengthOf(1); - - // test 2: shadow rendered - expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1); - }); - - it.skip('Hydration works with exported tagName', async () => { - const html = await fixture.readFile('/load/index.html'); - const $ = cheerioLoad(html); - - // SSR - // test 1: Element rendered - expect($('my-element')).to.have.lengthOf(1); - - // test 2: shadow rendered - expect($('my-element template[shadowroot=open][shadowrootmode=open]')).to.have.lengthOf(1); - - // Hydration - // test 3: Component and polyfill scripts bundled separately - expect($('script')).to.have.lengthOf(2); - }); - - it('Custom elements not claimed by renderer are rendered as regular HTML', async () => { - const html = await fixture.readFile('/nossr/index.html'); - const $ = cheerioLoad(html); - - // test 1: Rendered the client-only element - expect($('client-element')).to.have.lengthOf(1); - // No children - expect($('client-element').text()).to.equal(''); - }); - - it('Can import a client-only element that is nested in JSX', async () => { - const html = await fixture.readFile('/nested/index.html'); - const $ = cheerioLoad(html); - - // test 1: Element rendered - expect($('client-only-element')).to.have.lengthOf(1); - }); -}); diff --git a/packages/astro/test/dev-routing.test.js b/packages/astro/test/dev-routing.test.js index 186355b43..ff5f3a75d 100644 --- a/packages/astro/test/dev-routing.test.js +++ b/packages/astro/test/dev-routing.test.js @@ -113,9 +113,9 @@ describe('Development Routing', () => { expect(response.status).to.equal(200); }); - it('404 when loading subpath root without trailing slash', async () => { + it('200 when loading subpath root without trailing slash', async () => { const response = await fixture.fetch('/blog'); - expect(response.status).to.equal(404); + expect(response.status).to.equal(200); }); it('200 when loading another page with subpath used', async () => { @@ -163,9 +163,9 @@ describe('Development Routing', () => { expect(response.status).to.equal(200); }); - it('404 when loading subpath root without trailing slash', async () => { + it('200 when loading subpath root without trailing slash', async () => { const response = await fixture.fetch('/blog'); - expect(response.status).to.equal(404); + expect(response.status).to.equal(200); }); it('200 when loading another page with subpath used', async () => { diff --git a/packages/astro/test/featuresSupport.test.js b/packages/astro/test/featuresSupport.test.js new file mode 100644 index 000000000..fba8da475 --- /dev/null +++ b/packages/astro/test/featuresSupport.test.js @@ -0,0 +1,55 @@ +import { loadFixture } from './test-utils.js'; +import { expect } from 'chai'; +import testAdapter from './test-adapter.js'; + +describe('Adapter', () => { + let fixture; + + it("should error if the adapter doesn't support edge middleware", async () => { + try { + fixture = await loadFixture({ + root: './fixtures/middleware-dev/', + output: 'server', + build: { + excludeMiddleware: true, + }, + adapter: testAdapter({ + extendAdapter: { + supportsFeatures: { + edgeMiddleware: 'Unsupported', + }, + }, + }), + }); + await fixture.build(); + } catch (e) { + expect(e.toString()).to.contain( + "The adapter my-ssr-adapter doesn't support the feature build.excludeMiddleware." + ); + } + }); + + it("should error if the adapter doesn't support split build", async () => { + try { + fixture = await loadFixture({ + root: './fixtures/middleware-dev/', + output: 'server', + build: { + split: true, + }, + adapter: testAdapter({ + extendAdapter: { + supportsFeatures: { + functionPerPage: 'Unsupported', + }, + }, + }), + }); + await fixture.build(); + } catch (e) { + expect(e.toString()).to.contain( + "The adapter my-ssr-adapter doesn't support the feature build.split." + ); + } + }); +}); diff --git a/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts b/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts index c73589633..cbd382fc3 100644 --- a/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts +++ b/packages/astro/test/fixtures/api-routes/src/pages/binary.dat.ts @@ -1,5 +1,5 @@ import type { APIRoute } from 'astro'; -export const get: APIRoute = async function () { +export const GET: APIRoute = async function () { return new Response(new Uint8Array([0xff])); }; diff --git a/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js index 2ed42a5ec..d18eb086f 100644 --- a/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js +++ b/packages/astro/test/fixtures/api-routes/src/pages/context/data/[param].json.js @@ -14,7 +14,7 @@ export function getStaticPaths() { ] } -export function get({ params, request }) { +export function GET({ params, request }) { return { body: JSON.stringify({ param: params.param, diff --git a/packages/astro/test/fixtures/astro-basic/astro.config.mjs b/packages/astro/test/fixtures/astro-basic/astro.config.mjs index b7b7dafe6..1b2eb163d 100644 --- a/packages/astro/test/fixtures/astro-basic/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-basic/astro.config.mjs @@ -5,5 +5,5 @@ import preact from '@astrojs/preact'; export default defineConfig({ integrations: [preact()], // make sure CLI flags have precedence - server: () => ({ port: 3000 }) + server: () => ({ port: 4321 }) }); diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro b/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro index 2796b3989..1457cb882 100644 --- a/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro +++ b/packages/astro/test/fixtures/astro-cookies/src/pages/early-return.astro @@ -1,5 +1,5 @@ --- -const mode = Astro.cookies.get('prefs').json().mode; +const mode = Astro.cookies.get('prefs')!.json().mode; Astro.cookies.set('prefs', { mode: mode === 'light' ? 'dark' : 'light' diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro b/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro index 034881d22..44ee024ae 100644 --- a/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro +++ b/packages/astro/test/fixtures/astro-cookies/src/pages/get-json.astro @@ -1,5 +1,5 @@ --- -const cookie = Astro.cookies.get('prefs'); +const cookie = Astro.cookies.get('prefs')!; const prefs = cookie.json(); --- <html> diff --git a/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js b/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js index ccbdceff6..93a6a96de 100644 --- a/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js +++ b/packages/astro/test/fixtures/astro-cookies/src/pages/set-prefs.js @@ -1,5 +1,5 @@ -export function post({ cookies }) { +export function POST({ cookies }) { const mode = cookies.get('prefs').json().mode; cookies.set('prefs', { diff --git a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts index 3c7cc63ba..32a0fd140 100644 --- a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts +++ b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/data/[slug].json.ts @@ -5,7 +5,7 @@ export async function getStaticPaths() { ]; } -export async function get() { +export async function GET() { return { body: JSON.stringify({ title: '[slug]' diff --git a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro deleted file mode 100644 index 9bd7b4f41..000000000 --- a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro +++ /dev/null @@ -1,8 +0,0 @@ ---- - export function getStaticPaths() { - return [ - [ { params: {slug: "slug1"} } ], - [ { params: {slug: "slug2"} } ], - ] - } ---- diff --git a/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js b/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js index a56f5306f..3aae6d89a 100644 --- a/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js +++ b/packages/astro/test/fixtures/astro-markdown-frontmatter-injection/src/pages/glob.json.js @@ -1,4 +1,4 @@ -export async function get() { +export async function GET() { const docs = await import.meta.glob('./*.md', { eager: true }); return { body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)), diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js index 631250c33..b2c9ea6ea 100644 --- a/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js +++ b/packages/astro/test/fixtures/astro-markdown/src/pages/headings-glob.json.js @@ -1,6 +1,6 @@ import { getHeadings } from './with-layout.md'; -export async function get() { +export async function GET() { return { body: JSON.stringify({ headings: getHeadings(), diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js index ef933a373..82977443d 100644 --- a/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js +++ b/packages/astro/test/fixtures/astro-markdown/src/pages/raw-content.json.js @@ -1,6 +1,6 @@ import { rawContent, compiledContent } from './basic.md'; -export async function get() { +export async function GET() { return { body: JSON.stringify({ raw: rawContent(), diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js b/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js index 4a7e4dd78..7a5d00f47 100644 --- a/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js +++ b/packages/astro/test/fixtures/astro-markdown/src/pages/vite-env-vars-glob.json.js @@ -1,6 +1,6 @@ import { frontmatter } from './vite-env-vars.md'; -export async function get() { +export async function GET() { return { body: JSON.stringify(frontmatter), } diff --git a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro index c4cc39739..33df98cf5 100644 --- a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro +++ b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro @@ -1,7 +1,7 @@ --- export async function getStaticPaths({paginate}) { const allPosts = await Astro.glob('../../post/*.md'); - return ['red', 'blue'].map((filter) => { + return ['red', 'blue'].flatMap((filter) => { const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === filter); return paginate(filteredPosts, { params: { slug: filter }, diff --git a/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js b/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js index 4f529dac6..a461a1a65 100644 --- a/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js +++ b/packages/astro/test/fixtures/content-collection-references/src/pages/welcome-data.json.js @@ -1,6 +1,6 @@ import { getEntry, getEntries } from 'astro:content'; -export async function get() { +export async function GET() { const welcomePost = await getEntry('blog', 'welcome'); if (!welcomePost?.data) { diff --git a/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js b/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js index e74d03ad9..e335d2b05 100644 --- a/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js +++ b/packages/astro/test/fixtures/content-collections/src/pages/collections.json.js @@ -2,7 +2,7 @@ import { getCollection } from 'astro:content'; import * as devalue from 'devalue'; import { stripAllRenderFn } from '../utils.js'; -export async function get() { +export async function GET() { const withoutConfig = stripAllRenderFn(await getCollection('without-config')); const withSchemaConfig = stripAllRenderFn(await getCollection('with-schema-config')); const withSlugConfig = stripAllRenderFn(await getCollection('with-custom-slugs')); diff --git a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js index 0d7d22d08..311b76cc8 100644 --- a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js +++ b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js @@ -2,7 +2,7 @@ import { getEntryBySlug } from 'astro:content'; import * as devalue from 'devalue'; import { stripRenderFn } from '../utils.js'; -export async function get() { +export async function GET() { const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia')); const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one')); const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-custom-slugs', 'interesting-two')); diff --git a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts index c10946318..7847baf62 100644 --- a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts +++ b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts @@ -1,6 +1,6 @@ import type { APIRoute } from "../../../../../src/@types/astro"; -export const get = (async ({ params, request }) => { +export const GET = (async ({ params, request }) => { const url = new URL(request.url); const src = url.searchParams.get("src"); diff --git a/packages/astro/test/fixtures/custom-elements/astro.config.mjs b/packages/astro/test/fixtures/custom-elements/astro.config.mjs deleted file mode 100644 index 439334f8f..000000000 --- a/packages/astro/test/fixtures/custom-elements/astro.config.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { defineConfig } from 'astro/config'; -import ceIntegration from '@test/custom-element-renderer'; - -export default defineConfig({ - integrations: [ceIntegration()], -}) diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js deleted file mode 100644 index 665844481..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/hydration-polyfill.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.somePolyfillHere = ''; diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js deleted file mode 100644 index 5b9bba7e6..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/index.js +++ /dev/null @@ -1,31 +0,0 @@ -function getViteConfiguration() { - return { - optimizeDeps: { - include: ['@test/custom-element-renderer/polyfill.js', '@test/custom-element-renderer/hydration-polyfill.js'], - exclude: ['@test/custom-element-renderer/server.js'] - }, - }; -} - -export default function () { - return { - name: '@test/custom-element-renderer', - hooks: { - 'astro:config:setup': ({ updateConfig, addRenderer, injectScript }) => { - // Inject the necessary polyfills on every page - injectScript('head-inline', `import('@test/custom-element-renderer/polyfill.js');`); - // Inject the hydration code, before a component is hydrated. - injectScript('before-hydration', `import('@test/custom-element-renderer/hydration-polyfill.js');`); - // Add the lit renderer so that Astro can understand lit components. - addRenderer({ - name: '@test/custom-element-renderer', - serverEntrypoint: '@test/custom-element-renderer/server.js', - }); - // Update the vite configuration. - updateConfig({ - vite: getViteConfiguration(), - }); - }, - }, - }; -} diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json b/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json deleted file mode 100644 index f1d53b985..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "@test/custom-element-renderer", - "version": "0.1.0", - "private": true, - "main": "index.js", - "type": "module", - "exports": { - ".": "./index.js", - "./server.js": "./server.js", - "./polyfill.js": "./polyfill.js", - "./hydration-polyfill.js": "./hydration-polyfill.js" - } -} diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js deleted file mode 100644 index 92788352b..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/polyfill.js +++ /dev/null @@ -1,2 +0,0 @@ -console.log('this is a polyfill'); -export default {}; diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js deleted file mode 100644 index 688923159..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/server.js +++ /dev/null @@ -1,30 +0,0 @@ -function getConstructor(Component) { - if (typeof Component === 'string') { - const tagName = Component; - Component = customElements.get(tagName); - } - return Component; -} - -function check(component) { - const Component = getConstructor(component); - if (typeof Component === 'function' && globalThis.HTMLElement.isPrototypeOf(Component)) { - return true; - } - return false; -} - -function renderToStaticMarkup(component, props, innerHTML) { - const Component = getConstructor(component); - const el = new Component(); - el.connectedCallback(); - const html = `<${el.localName}><template shadowroot="open" shadowrootmode="open">${el.shadowRoot.innerHTML}</template>${el.innerHTML}</${el.localName}>` - return { - html - }; -} - -export default { - check, - renderToStaticMarkup -}; diff --git a/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js b/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js deleted file mode 100644 index eb969e528..000000000 --- a/packages/astro/test/fixtures/custom-elements/my-component-lib/shim.js +++ /dev/null @@ -1,28 +0,0 @@ -globalThis.customElements = { - _elements: new Map(), - define(name, ctr) { - ctr.tagName = name; - this._elements.set(name, ctr); - }, - get(name) { - return this._elements.get(name); - } -}; - -globalThis.HTMLElement = class { - attachShadow() { - this.shadowRoot = new HTMLElement(); - } - - get localName() { - return this.constructor.tagName; - } - - get innerHTML() { - return this._innerHTML; - } - - set innerHTML(val) { - this._innerHTML = val; - } -}; diff --git a/packages/astro/test/fixtures/custom-elements/package.json b/packages/astro/test/fixtures/custom-elements/package.json deleted file mode 100644 index 80246a3b0..000000000 --- a/packages/astro/test/fixtures/custom-elements/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "@test/custom-elements", - "version": "0.0.0", - "private": true, - "dependencies": { - "astro": "workspace:*", - "@test/custom-element-renderer": "workspace:*" - } -} diff --git a/packages/astro/test/fixtures/custom-elements/src/components/my-element.js b/packages/astro/test/fixtures/custom-elements/src/components/my-element.js deleted file mode 100644 index ffc5e6ffb..000000000 --- a/packages/astro/test/fixtures/custom-elements/src/components/my-element.js +++ /dev/null @@ -1,13 +0,0 @@ -export const tagName = 'my-element'; - -class MyElement extends HTMLElement { - connectedCallback() { - this.attachShadow({ mode: 'open' }); - this.shadowRoot.innerHTML = `<span id="custom">Hello from a custom element!</span>`; - this.innerHTML = `<div id="custom-light">Light dom!</div>` - } -} - -customElements.define(tagName, MyElement); - -export default MyElement; diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro b/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro deleted file mode 100644 index 8b1c83512..000000000 --- a/packages/astro/test/fixtures/custom-elements/src/pages/ctr.astro +++ /dev/null @@ -1,16 +0,0 @@ ---- -import MyElement from '../components/my-element.js'; - -const title = 'My App'; ---- - -<html> -<head> - <title>{title}</title> -</head> -<body> - <h1>{title}</h1> - - <MyElement /> -</body> -</html>
\ No newline at end of file diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro b/packages/astro/test/fixtures/custom-elements/src/pages/index.astro deleted file mode 100644 index b7380624a..000000000 --- a/packages/astro/test/fixtures/custom-elements/src/pages/index.astro +++ /dev/null @@ -1,15 +0,0 @@ ---- -import '../components/my-element.js'; -const title = 'My App'; ---- - -<html> -<head> - <title>{title}</title> -</head> -<body> - <h1>{title}</h1> - - <my-element></my-element> -</body> -</html>
\ No newline at end of file diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro b/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro deleted file mode 100644 index f23e3b6bc..000000000 --- a/packages/astro/test/fixtures/custom-elements/src/pages/nested.astro +++ /dev/null @@ -1,11 +0,0 @@ ---- -let show = true ---- -<html> -<head> - <title>Custom element not imported but nested</title> -</head> -<body> - {show && <client-only-element></client-only-element>} -</body> -</html>
\ No newline at end of file diff --git a/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro b/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro deleted file mode 100644 index 53e580afb..000000000 --- a/packages/astro/test/fixtures/custom-elements/src/pages/nossr.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -const title = 'My App'; ---- - -<html> -<head> - <title>{title}</title> -</head> -<body> - <h1>{title}</h1> - - <client-element></client-element> -</body> -</html>
\ No newline at end of file diff --git a/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js b/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js index 1cc26fb73..76f4d5760 100644 --- a/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js +++ b/packages/astro/test/fixtures/data-collections/src/pages/authors/[id].json.js @@ -7,7 +7,7 @@ export function getStaticPaths() { } /** @param {import('astro').APIContext} params */ -export async function get({ params }) { +export async function GET({ params }) { const { id } = params; const author = await getEntry('authors-without-config', id); if (!author) { diff --git a/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js b/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js index e4c804064..5b5007fed 100644 --- a/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js +++ b/packages/astro/test/fixtures/data-collections/src/pages/authors/all.json.js @@ -1,6 +1,6 @@ import { getCollection } from 'astro:content'; -export async function get() { +export async function GET() { const authors = await getCollection('authors-without-config'); return { diff --git a/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js b/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js index 73c90354d..ab8cc764e 100644 --- a/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js +++ b/packages/astro/test/fixtures/data-collections/src/pages/translations/[lang].json.js @@ -7,7 +7,7 @@ export function getStaticPaths() { } /** @param {import('astro').APIContext} params */ -export async function get({ params }) { +export async function GET({ params }) { const { lang } = params; const translations = await getEntry('i18n', lang); if (!translations) { diff --git a/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js b/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js index 7d953838f..e8a1fee92 100644 --- a/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js +++ b/packages/astro/test/fixtures/data-collections/src/pages/translations/all.json.js @@ -1,6 +1,6 @@ import { getCollection } from 'astro:content'; -export async function get() { +export async function GET() { const translations = await getCollection('i18n'); return { diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts index 8f64c2401..8fccd6695 100644 --- a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts +++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts @@ -2,7 +2,7 @@ import type { APIRoute } from "astro"; const slugs = ["one", undefined]; -export const get: APIRoute = ({ params }) => { +export const GET: APIRoute = ({ params }) => { return { body: JSON.stringify({ slug: params.slug || "index", diff --git a/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js b/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js index dadff6edb..69d989bc0 100644 --- a/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js +++ b/packages/astro/test/fixtures/middleware-dev/src/pages/api/endpoint.js @@ -1,4 +1,4 @@ -export function get() { +export function GET() { const object = { name: 'Endpoint!!', }; diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts index af61847f3..0c3ec18ea 100644 --- a/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts +++ b/packages/astro/test/fixtures/non-html-pages/src/pages/about.json.ts @@ -1,7 +1,7 @@ // Returns the file body for this non-HTML file. // The content type is based off of the extension in the filename, // in this case: about.json. -export async function get() { +export async function GET() { return { body: JSON.stringify({ name: 'Astro', diff --git a/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts index 0c2d3806b..3ee26f0bf 100644 --- a/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts +++ b/packages/astro/test/fixtures/non-html-pages/src/pages/placeholder.png.ts @@ -2,7 +2,7 @@ import { promises as fs } from 'node:fs'; import type { APIRoute } from 'astro'; -export const get: APIRoute = async function get() { +export const GET: APIRoute = async function get() { try { // Image is in the public domain. Sourced from // https://en.wikipedia.org/wiki/File:Portrait_placeholder.png diff --git a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts index 142b11711..bc7e1b774 100644 --- a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts +++ b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[...slug].json.ts @@ -1,6 +1,6 @@ import type { APIRoute } from 'astro'; -export const get: APIRoute = async ({ params }) => { +export const GET: APIRoute = async ({ params }) => { return { body: JSON.stringify({ path: params.slug, diff --git a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts index 2e66a22ae..b06efff6f 100644 --- a/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts +++ b/packages/astro/test/fixtures/routing-priority/src/pages/api/catch/[foo]-[bar].json.ts @@ -1,6 +1,6 @@ import type { APIRoute } from 'astro'; -export const get: APIRoute = async ({ params }) => { +export const GET: APIRoute = async ({ params }) => { return { body: JSON.stringify({ foo: params.foo, diff --git a/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js b/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js index c44461be9..5a1cacc11 100644 --- a/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js +++ b/packages/astro/test/fixtures/ssr-api-route-custom-404/src/pages/api/route.js @@ -1,5 +1,5 @@ -export function post() { +export function POST() { return { body: JSON.stringify({ ok: true }) }; diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js index 3e1c70c81..407c45666 100644 --- a/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js +++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/binary.js @@ -1,6 +1,6 @@ import fs from 'node:fs'; -export function get() { +export function GET() { return { body: 'ok' }; diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js b/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js index 0ff1f625a..ba110ee13 100644 --- a/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js +++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/context/[param].js @@ -1,7 +1,7 @@ /** * @param {import('astro').APIContext} api */ -export function get(ctx) { +export function GET(ctx) { return { body: JSON.stringify({ cookiesExist: !!ctx.cookies, diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js index 1f9ea5f29..f4021c9e5 100644 --- a/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js +++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/food.json.js @@ -1,5 +1,5 @@ -export function get() { +export function GET() { return { body: JSON.stringify([ { name: 'lettuce' }, @@ -9,7 +9,7 @@ export function get() { }; } -export async function post({ params, request }) { +export async function POST({ params, request }) { const body = await request.text(); return new Response(body === `some data` ? `ok` : `not ok`, { status: 200, diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js index dfce0b5d6..0e851df74 100644 --- a/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js +++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js @@ -1,5 +1,5 @@ /** @type {import('astro').APIRoute} */ -export function post({ cookies }) { +export function POST({ cookies }) { cookies.set('foo', 'foo', { httpOnly: true }); diff --git a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js index 4d96b62a5..8c7c39302 100644 --- a/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js +++ b/packages/astro/test/fixtures/ssr-dynamic/src/pages/api/products/[id].js @@ -1,5 +1,5 @@ -export function get({ params }) { +export function GET({ params }) { return { body: JSON.stringify(params) }; diff --git a/packages/astro/test/fixtures/ssr-locals/src/pages/api.js b/packages/astro/test/fixtures/ssr-locals/src/pages/api.js index d4f7386fb..366f26aae 100644 --- a/packages/astro/test/fixtures/ssr-locals/src/pages/api.js +++ b/packages/astro/test/fixtures/ssr-locals/src/pages/api.js @@ -1,5 +1,5 @@ -export async function get({ locals }) { +export async function GET({ locals }) { let out = { ...locals }; return new Response(JSON.stringify(out), { diff --git a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts index 16e2a90ca..d969873c5 100644 --- a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts +++ b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/data/[slug].json.ts @@ -7,7 +7,7 @@ export async function getStaticPaths() { ]; } -export async function get() { +export async function GET() { return { body: JSON.stringify({ title: '[slug]' diff --git a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro deleted file mode 100644 index 25d1bfff4..000000000 --- a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro +++ /dev/null @@ -1,10 +0,0 @@ ---- - export function getStaticPaths() { - return [ - [ { params: {slug: "slug1"} } ], - [ { params: {slug: "slug2"} } ], - ] - } - - export const prerender = true; ---- diff --git a/packages/astro/test/fixtures/ssr-preview/preview.mjs b/packages/astro/test/fixtures/ssr-preview/preview.mjs index 745f22624..d8d57afa8 100644 --- a/packages/astro/test/fixtures/ssr-preview/preview.mjs +++ b/packages/astro/test/fixtures/ssr-preview/preview.mjs @@ -1,7 +1,7 @@ export default () => { // noop return { - port: 3000, + port: 4321, closed() {}, stop() {} } diff --git a/packages/astro/test/fixtures/static-build/src/pages/company.json.ts b/packages/astro/test/fixtures/static-build/src/pages/company.json.ts index ee3f2f1ad..08f45a7b8 100644 --- a/packages/astro/test/fixtures/static-build/src/pages/company.json.ts +++ b/packages/astro/test/fixtures/static-build/src/pages/company.json.ts @@ -1,8 +1,8 @@ -export async function get() { +export async function GET() { return { body: JSON.stringify({ name: 'Astro Technology Company', url: 'https://astro.build/' }) } -}
\ No newline at end of file +} diff --git a/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts index 2bcfe50a1..2fa13ac18 100644 --- a/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts +++ b/packages/astro/test/fixtures/static-build/src/pages/data/[slug].json.ts @@ -5,7 +5,7 @@ export async function getStaticPaths() { ] } -export async function get({ params }) { +export async function GET({ params }) { return { body: JSON.stringify({ slug: params.slug, @@ -13,4 +13,4 @@ export async function get({ params }) { url: 'https://astro.build/' }) } -}
\ No newline at end of file +} diff --git a/packages/astro/test/fixtures/static-build/src/pages/posts.json.js b/packages/astro/test/fixtures/static-build/src/pages/posts.json.js index 6463fdbad..aefbbffff 100644 --- a/packages/astro/test/fixtures/static-build/src/pages/posts.json.js +++ b/packages/astro/test/fixtures/static-build/src/pages/posts.json.js @@ -13,7 +13,7 @@ async function fetchPosts() { return posts.sort((a, b) => a.title.localeCompare(b.title)); } -export async function get() { +export async function GET() { const posts = await fetchPosts(); return { diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts index 364c886e3..783031605 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/[slug].json.ts @@ -5,7 +5,7 @@ export async function getStaticPaths() { ]; } -export async function get({ params }) { +export async function GET({ params }) { return { body: JSON.stringify({ slug: params.slug, diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts index 4392c7ee7..707c39fa9 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/data/[slug].json.ts @@ -5,7 +5,7 @@ export async function getStaticPaths() { ]; } -export async function get({ params }) { +export async function GET({ params }) { return { body: JSON.stringify({ slug: params.slug, diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts index 8046af6df..5eaac42f1 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/home.json.ts @@ -1,4 +1,4 @@ -export async function get() { +export async function GET() { return { body: JSON.stringify({ title: 'home' diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts index e728394f4..c6fa88711 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/[image].svg.ts @@ -5,7 +5,7 @@ export async function getStaticPaths() { ]; } -export async function get({ params }) { +export async function GET({ params }) { return { body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200"> <title>${params.image}</title> diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts index 546796fed..84dd6dcaa 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/hex.ts @@ -1,6 +1,6 @@ import { readFileSync } from "node:fs"; -export async function get({ params, request }) { +export async function GET({ params, request }) { const buffer = readFileSync(new URL('../../astro.png', import.meta.url)); return { body: buffer.toString('hex'), diff --git a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts index 727a0eae8..93fcf4022 100644 --- a/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts +++ b/packages/astro/test/fixtures/with-endpoint-routes/src/pages/images/static.svg.ts @@ -1,4 +1,4 @@ -export async function get() { +export async function GET() { return { body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200"> <title>Static SVG</title> diff --git a/packages/astro/test/preview-routing.test.js b/packages/astro/test/preview-routing.test.js index 5c365b07c..4c99881b6 100644 --- a/packages/astro/test/preview-routing.test.js +++ b/packages/astro/test/preview-routing.test.js @@ -157,9 +157,9 @@ describe('Preview Routing', () => { expect(response.status).to.equal(200); }); - it('404 when loading subpath root without trailing slash', async () => { + it('200 when loading subpath root without trailing slash', async () => { const response = await fixture.fetch('/blog'); - expect(response.status).to.equal(404); + expect(response.status).to.equal(200); }); it('200 when loading another page with subpath used', async () => { @@ -345,9 +345,9 @@ describe('Preview Routing', () => { expect(response.status).to.equal(200); }); - it('404 when loading subpath root without trailing slash', async () => { + it('200 when loading subpath root without trailing slash', async () => { const response = await fixture.fetch('/blog'); - expect(response.status).to.equal(404); + expect(response.status).to.equal(200); }); it('200 when loading another page with subpath used', async () => { diff --git a/packages/astro/test/public-base-404.test.js b/packages/astro/test/public-base-404.test.js index c8d58471d..969e2a952 100644 --- a/packages/astro/test/public-base-404.test.js +++ b/packages/astro/test/public-base-404.test.js @@ -44,7 +44,7 @@ describe('Public dev with base', () => { expect(response.status).to.equal(404); const html = await response.text(); $ = cheerio.load(html); - expect($('a').first().text()).to.equal('/blog/'); + expect($('a').first().text()).to.equal('/blog'); }); it('default 404 page when loading /none/', async () => { diff --git a/packages/astro/test/scoped-style-strategy.test.js b/packages/astro/test/scoped-style-strategy.test.js index 022ef3d6f..a59f227ad 100644 --- a/packages/astro/test/scoped-style-strategy.test.js +++ b/packages/astro/test/scoped-style-strategy.test.js @@ -3,7 +3,7 @@ import * as cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; describe('scopedStyleStrategy', () => { - describe('default', () => { + describe('scopedStyleStrategy: "where"', () => { /** @type {import('./test-utils').Fixture} */ let fixture; let stylesheet; @@ -11,6 +11,7 @@ describe('scopedStyleStrategy', () => { before(async () => { fixture = await loadFixture({ root: './fixtures/scoped-style-strategy/', + scopedStyleStrategy: 'where', }); await fixture.build(); @@ -57,4 +58,35 @@ describe('scopedStyleStrategy', () => { expect(stylesheet).to.match(/h1\.astro/); }); }); + + describe('default', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + let stylesheet; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/scoped-style-strategy/', + }); + await fixture.build(); + + const html = await fixture.readFile('/index.html'); + const $ = cheerio.load(html); + const $link = $('link[rel=stylesheet]'); + const href = $link.attr('href'); + stylesheet = await fixture.readFile(href); + }); + + it('does not include :where pseudo-selector', () => { + expect(stylesheet).to.not.match(/:where/); + }); + + it('does not include the class name directly in the selector', () => { + expect(stylesheet).to.not.match(/h1\.astro/); + }); + + it('includes the data attribute hash', () => { + expect(stylesheet).to.include('h1[data-astro-cid-'); + }); + }); }); diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js index 3e2d5c327..899404b1e 100644 --- a/packages/astro/test/ssr-api-route.test.js +++ b/packages/astro/test/ssr-api-route.test.js @@ -1,6 +1,5 @@ import { expect } from 'chai'; import net from 'node:net'; -import { File, FormData } from 'undici'; import testAdapter from './test-adapter.js'; import { loadFixture } from './test-utils.js'; diff --git a/packages/astro/test/ssr-manifest.test.js b/packages/astro/test/ssr-manifest.test.js index 4e5521220..bd9483505 100644 --- a/packages/astro/test/ssr-manifest.test.js +++ b/packages/astro/test/ssr-manifest.test.js @@ -11,7 +11,6 @@ describe('astro:ssr-manifest', () => { fixture = await loadFixture({ root: './fixtures/ssr-manifest/', output: 'server', - compressHTML: true, adapter: testAdapter(), }); await fixture.build(); diff --git a/packages/astro/test/ssr-prerender-get-static-paths.test.js b/packages/astro/test/ssr-prerender-get-static-paths.test.js index 391e7485d..3fe2950cb 100644 --- a/packages/astro/test/ssr-prerender-get-static-paths.test.js +++ b/packages/astro/test/ssr-prerender-get-static-paths.test.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; -import { loadFixture } from './test-utils.js'; import * as cheerio from 'cheerio'; import testAdapter from './test-adapter.js'; +import { loadFixture } from './test-utils.js'; describe('Prerender', () => { /** @type {import('./test-utils').Fixture} */ @@ -102,11 +102,6 @@ describe('Prerender', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/blog/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/blog/2022/post-1'); @@ -234,11 +229,6 @@ describe('Prerender', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/blog/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/blog/2022/post-1'); diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js index d4a687a5d..0552c353f 100644 --- a/packages/astro/test/static-build.test.js +++ b/packages/astro/test/static-build.test.js @@ -175,7 +175,7 @@ describe('Static build', () => { let found = false; for (const log of logs) { if ( - log.type === 'ssg' && + log.label === 'ssg' && /[hH]eaders are not exposed in static \(SSG\) output mode/.test(log.message) ) { found = true; diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 85b4d69c0..67058023d 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -71,6 +71,15 @@ export default function ( name: 'my-ssr-adapter', serverEntrypoint: '@my-ssr', exports: ['manifest', 'createApp'], + supportedFeatures: { + assets: { + supportKind: 'Stable', + isNodeCompatible: true, + }, + serverOutput: 'Stable', + staticOutput: 'Stable', + hybridOutput: 'Stable', + }, ...extendAdapter, }); }, diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index 27f5d83f7..a58d417d7 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -1,4 +1,3 @@ -import { polyfill } from '@astrojs/webapi'; import { execa } from 'execa'; import fastGlob from 'fast-glob'; import fs from 'node:fs'; @@ -17,16 +16,10 @@ import { nodeLogDestination } from '../dist/core/logger/node.js'; import preview from '../dist/core/preview/index.js'; import { sync } from '../dist/core/sync/index.js'; -// polyfill WebAPIs to globalThis for Node v12, Node v14, and Node v16 -polyfill(globalThis, { - exclude: 'window document', -}); - // Disable telemetry when running tests process.env.ASTRO_TELEMETRY_DISABLED = true; /** - * @typedef {import('undici').Response} Response * @typedef {import('../src/core/dev/dev').DedvServer} DevServer * @typedef {import('../src/@types/astro').AstroInlineConfig & { root?: string | URL }} AstroInlineConfig * @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer diff --git a/packages/astro/test/units/cookies/delete.test.js b/packages/astro/test/units/cookies/delete.test.js index 67fa1306b..f4c9fab53 100644 --- a/packages/astro/test/units/cookies/delete.test.js +++ b/packages/astro/test/units/cookies/delete.test.js @@ -30,7 +30,7 @@ describe('astro/src/core/cookies', () => { expect(cookies.get('foo').value).to.equal('bar'); cookies.delete('foo'); - expect(cookies.get('foo').value).to.equal(undefined); + expect(cookies.get('foo')).to.equal(undefined); }); it('calling cookies.has() after returns false', () => { diff --git a/packages/astro/test/units/cookies/get.test.js b/packages/astro/test/units/cookies/get.test.js index f044d715a..216128907 100644 --- a/packages/astro/test/units/cookies/get.test.js +++ b/packages/astro/test/units/cookies/get.test.js @@ -16,6 +16,13 @@ describe('astro/src/core/cookies', () => { expect(cookies.get('foo').value).to.equal('bar'); }); + it("Returns undefined is the value doesn't exist", () => { + const req = new Request('http://example.com/'); + let cookies = new AstroCookies(req); + let cookie = cookies.get('foo'); + expect(cookie).to.equal(undefined); + }); + describe('.json()', () => { it('returns a JavaScript object', () => { const req = new Request('http://example.com/', { @@ -29,13 +36,6 @@ describe('astro/src/core/cookies', () => { expect(json).to.be.an('object'); expect(json.key).to.equal('value'); }); - - it('throws if the value is undefined', () => { - const req = new Request('http://example.com/'); - let cookies = new AstroCookies(req); - let cookie = cookies.get('foo'); - expect(() => cookie.json()).to.throw('Cannot convert undefined to an object.'); - }); }); describe('.number()', () => { diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index 919628da2..a420dd6c9 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -1,5 +1,7 @@ import { expect } from 'chai'; import { runHookBuildSetup } from '../../../dist/integrations/index.js'; +import { validateSupportedFeatures } from '../../../dist/integrations/astroFeaturesValidation.js'; +import { defaultLogging } from '../test-utils.js'; describe('Integration API', () => { it('runHookBuildSetup should work', async () => { @@ -28,3 +30,187 @@ describe('Integration API', () => { expect(updatedViteConfig).to.haveOwnProperty('define'); }); }); + +describe('Astro feature map', function () { + it('should support the feature when stable', () => { + let result = validateSupportedFeatures( + 'test', + { + hybridOutput: 'stable', + }, + { + output: 'hybrid', + }, + defaultLogging + ); + expect(result['hybridOutput']).to.be.true; + }); + + it('should not support the feature when not provided', () => { + let result = validateSupportedFeatures( + 'test', + undefined, + { + output: 'hybrid', + }, + defaultLogging + ); + expect(result['hybridOutput']).to.be.false; + }); + + it('should not support the feature when an empty object is provided', () => { + let result = validateSupportedFeatures( + 'test', + {}, + { + output: 'hybrid', + }, + defaultLogging + ); + expect(result['hybridOutput']).to.be.false; + }); + + describe('static output', function () { + it('should be supported with the correct config', () => { + let result = validateSupportedFeatures( + 'test', + { staticOutput: 'stable' }, + { + output: 'static', + }, + defaultLogging + ); + expect(result['staticOutput']).to.be.true; + }); + + it("should not be valid if the config is correct, but the it's unsupported", () => { + let result = validateSupportedFeatures( + 'test', + { staticOutput: 'unsupported' }, + { + output: 'static', + }, + defaultLogging + ); + expect(result['staticOutput']).to.be.false; + }); + }); + describe('hybrid output', function () { + it('should be supported with the correct config', () => { + let result = validateSupportedFeatures( + 'test', + { hybridOutput: 'stable' }, + { + output: 'hybrid', + }, + defaultLogging + ); + expect(result['hybridOutput']).to.be.true; + }); + + it("should not be valid if the config is correct, but the it's unsupported", () => { + let result = validateSupportedFeatures( + 'test', + { + hybridOutput: 'unsupported', + }, + { + output: 'hybrid', + }, + defaultLogging + ); + expect(result['hybridOutput']).to.be.false; + }); + }); + describe('server output', function () { + it('should be supported with the correct config', () => { + let result = validateSupportedFeatures( + 'test', + { serverOutput: 'stable' }, + { + output: 'server', + }, + defaultLogging + ); + expect(result['serverOutput']).to.be.true; + }); + + it("should not be valid if the config is correct, but the it's unsupported", () => { + let result = validateSupportedFeatures( + 'test', + { + serverOutput: 'unsupported', + }, + { + output: 'server', + }, + defaultLogging + ); + expect(result['serverOutput']).to.be.false; + }); + }); + + describe('assets', function () { + it('should be supported when it is sharp compatible', () => { + let result = validateSupportedFeatures( + 'test', + { + assets: { + supportKind: 'stable', + isSharpCompatible: true, + }, + }, + { + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + }, + }, + }, + defaultLogging + ); + expect(result['assets']).to.be.true; + }); + it('should be supported when it is squoosh compatible', () => { + let result = validateSupportedFeatures( + 'test', + { + assets: { + supportKind: 'stable', + isSquooshCompatible: true, + }, + }, + { + image: { + service: { + entrypoint: 'astro/assets/services/squoosh', + }, + }, + }, + defaultLogging + ); + expect(result['assets']).to.be.true; + }); + + it("should not be valid if the config is correct, but the it's unsupported", () => { + let result = validateSupportedFeatures( + 'test', + { + assets: { + supportKind: 'unsupported', + isNodeCompatible: false, + }, + }, + { + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + }, + }, + }, + defaultLogging + ); + expect(result['assets']).to.be.false; + }); + }); +}); diff --git a/packages/astro/tsconfig.json b/packages/astro/tsconfig.json index 839239eaf..63854a31d 100644 --- a/packages/astro/tsconfig.json +++ b/packages/astro/tsconfig.json @@ -6,7 +6,7 @@ "declarationDir": "./dist", "module": "ES2022", "outDir": "./dist", - "target": "ES2021", + "target": "ES2022", "jsx": "preserve", "types": ["@types/dom-view-transitions", "network-information-types"] } diff --git a/packages/astro/tsconfigs/base.json b/packages/astro/tsconfigs/base.json index aa1f17543..d921ef4be 100644 --- a/packages/astro/tsconfigs/base.json +++ b/packages/astro/tsconfigs/base.json @@ -5,14 +5,19 @@ "target": "ESNext", "module": "ESNext", // Enable node-style module resolution, for things like npm package imports. - "moduleResolution": "node", + "moduleResolution": "Bundler", + // Allow importing TypeScript files using their native extension (.ts(x)). + "allowImportingTsExtensions": true, // Enable JSON imports. "resolveJsonModule": true, - // Enable stricter transpilation for better output. + // Enforce the usage of type-only imports when needed, which helps avoiding bundling issues. + "verbatimModuleSyntax": true, + // Ensure that each file can be transpiled without relying on other imports. + // This is redundant with the previous option, however it ensures that it's on even if someone disable `verbatimModuleSyntax` "isolatedModules": true, // Astro directly run TypeScript code, no transpilation needed. "noEmit": true, - // Report an error when importing a file using a casing different from the casing on disk. + // Report an error when importing a file using a casing different from another import of the same file. "forceConsistentCasingInFileNames": true, // Properly support importing CJS modules in ESM "esModuleInterop": true, @@ -23,9 +28,6 @@ "paths": { "~/assets/*": ["src/assets/*"] }, - // TypeScript 5.0 changed how `isolatedModules` and `importsNotUsedAsValues` works, deprecating the later - // Until the majority of users are on TypeScript 5.0, we'll have to supress those deprecation errors - "ignoreDeprecations": "5.0", // Allow JavaScript files to be imported "allowJs": true } diff --git a/packages/astro/tsconfigs/strict.json b/packages/astro/tsconfigs/strict.json index bc87a68e0..3064440a5 100644 --- a/packages/astro/tsconfigs/strict.json +++ b/packages/astro/tsconfigs/strict.json @@ -2,8 +2,7 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "./base.json", "compilerOptions": { - "strict": true, - // Error when a value import is only used as a type. - "importsNotUsedAsValues": "error" + // Enable strict mode. This enables a few options at a time, see https://www.typescriptlang.org/tsconfig#strict for a list. + "strict": true } } |