diff --git a/.changeset/dull-beers-move.md b/.changeset/dull-beers-move.md new file mode 100644 index 000000000000..5c5679bdc9e2 --- /dev/null +++ b/.changeset/dull-beers-move.md @@ -0,0 +1,23 @@ +--- +"astro": minor +--- + +Adds an option for integration authors to suppress adapter warning/errors in `supportedAstroFeatures`. This is useful when either an warning/error isn't applicable in a specific context or the default one might conflict and confuse users. + +To do so, you can add `suppress: "all"` (to suppress both the default and custom message) or `suppress: "default"` (to only suppress the default one): +```ts +setAdapter({ + name: 'my-astro-integration', + supportedAstroFeatures: { + staticOutput: "stable", + hybridOutput: "stable", + sharpImageService: { + support: "limited", + message: "The sharp image service isn't available in the deploy environment, but will be used by prerendered pages on build.", + suppress: "default", + }, + } +}) +``` + +For more information, see the [Adapter API reference docs](https://docs.astro.build/en/reference/adapter-reference/#astro-features). diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 0edcb7bb9139..058758627311 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -108,6 +108,10 @@ function getSupportMessage(supportKind: AdapterSupport): string | undefined { return typeof supportKind === 'object' ? supportKind.message : undefined; } +function getSupportMessageSuppression(supportKind: AdapterSupport): 'all' | 'default' | undefined { + return typeof supportKind === 'object' ? supportKind.suppress : undefined; +} + function validateSupportKind( supportKind: AdapterSupport, adapterName: string, @@ -117,6 +121,7 @@ function validateSupportKind( ): boolean { const supportValue = unwrapSupportKind(supportKind); const message = getSupportMessage(supportKind); + const suppress = getSupportMessageSuppression(supportKind); if (!supportValue) { return false; @@ -126,7 +131,7 @@ function validateSupportKind( return true; } else if (hasCorrectConfig()) { // If the user has the relevant configuration, but the adapter doesn't support it, warn the user - logFeatureSupport(adapterName, logger, featureName, supportValue, message); + logFeatureSupport(adapterName, logger, featureName, supportValue, message, suppress); } return false; @@ -138,38 +143,41 @@ function logFeatureSupport( featureName: string, supportKind: AdapterSupport, adapterMessage?: string, + suppress?: 'all' | 'default', ) { - switch (supportKind) { - case AdapterFeatureStability.STABLE: - break; - case AdapterFeatureStability.DEPRECATED: - logger.warn( - 'config', - `The adapter ${adapterName} has deprecated its support for "${featureName}", and future compatibility is not guaranteed. The adapter may completely remove support for this feature without warning.`, - ); - break; - case AdapterFeatureStability.EXPERIMENTAL: - logger.warn( - 'config', - `The adapter ${adapterName} provides experimental support for "${featureName}". You may experience issues or breaking changes until this feature is fully supported by the adapter.`, - ); - break; - case AdapterFeatureStability.LIMITED: - logger.warn( - 'config', - `The adapter ${adapterName} has limited support for "${featureName}". Certain features may not work as expected.`, - ); - break; - case AdapterFeatureStability.UNSUPPORTED: - logger.error( - 'config', - `The adapter ${adapterName} does not currently support the feature "${featureName}". Your project may not build correctly.`, - ); - break; + if (!suppress) { + switch (supportKind) { + case AdapterFeatureStability.STABLE: + break; + case AdapterFeatureStability.DEPRECATED: + logger.warn( + 'config', + `The adapter ${adapterName} has deprecated its support for "${featureName}", and future compatibility is not guaranteed. The adapter may completely remove support for this feature without warning.`, + ); + break; + case AdapterFeatureStability.EXPERIMENTAL: + logger.warn( + 'config', + `The adapter ${adapterName} provides experimental support for "${featureName}". You may experience issues or breaking changes until this feature is fully supported by the adapter.`, + ); + break; + case AdapterFeatureStability.LIMITED: + logger.warn( + 'config', + `The adapter ${adapterName} has limited support for "${featureName}". Certain features may not work as expected.`, + ); + break; + case AdapterFeatureStability.UNSUPPORTED: + logger.error( + 'config', + `The adapter ${adapterName} does not currently support the feature "${featureName}". Your project may not build correctly.`, + ); + break; + } } - // If the adapter specified a custom message, log it after the default message - if (adapterMessage) { + // If the adapter specified a custom message, log it after the default message (when not suppressed) + if (adapterMessage && suppress !== 'all') { logger.warn('adapter', adapterMessage); } } diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 2d0b13e96300..6bb437f31509 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -67,6 +67,15 @@ export type AdapterSupportsKind = export type AdapterSupportWithMessage = { support: Exclude; message: string; + /** + * Determines if a feature support warning/error in the adapter should be suppressed: + * - `"default"`: Suppresses the default warning/error message. + * - `"all"`: Suppresses both the custom and the default warning/error message. + * + * This is useful when the warning/error might not be applicable in certain contexts, + * or the default message might cause confusion and conflict with a custom one. + */ + suppress?: 'all' | 'default'; }; export type AdapterSupport = AdapterSupportsKind | AdapterSupportWithMessage; diff --git a/packages/astro/test/fixtures/feature-support-message-suppresion/astro.config.mjs b/packages/astro/test/fixtures/feature-support-message-suppresion/astro.config.mjs new file mode 100644 index 000000000000..a28113763aff --- /dev/null +++ b/packages/astro/test/fixtures/feature-support-message-suppresion/astro.config.mjs @@ -0,0 +1,29 @@ +import { defineConfig } from 'astro/config'; +export default defineConfig({ + integrations: [ + { + name: 'astro-test-feature-support-message-suppression', + hooks: { + 'astro:config:done': ({ setAdapter }) => { + setAdapter({ + name: 'astro-test-feature-support-message-suppression', + supportedAstroFeatures: { + staticOutput: "stable", + hybridOutput: "stable", + serverOutput: { + support: "experimental", + message: "This should be logged.", + suppress: "default", + }, + sharpImageService: { + support: 'limited', + message: 'This shouldn\'t be logged.', + suppress: "all", + }, + } + }) + }, + }, + }, + ], +}); diff --git a/packages/astro/test/fixtures/feature-support-message-suppresion/package.json b/packages/astro/test/fixtures/feature-support-message-suppresion/package.json new file mode 100644 index 000000000000..f34355172460 --- /dev/null +++ b/packages/astro/test/fixtures/feature-support-message-suppresion/package.json @@ -0,0 +1,15 @@ +{ + "name": "@test/feature-support-message-suppresion", + "type": "module", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/feature-support-message-suppresion/src/pages/index.astro b/packages/astro/test/fixtures/feature-support-message-suppresion/src/pages/index.astro new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index d5c2e0be900e..c8854482f4d2 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -25,13 +25,13 @@ import { } from './utils/cloudflare-module-loader.js'; import { createGetEnv } from './utils/env.js'; import { createRoutesFile, getParts } from './utils/generate-routes-json.js'; -import { setImageConfig } from './utils/image-config.js'; +import { type ImageService, setImageConfig } from './utils/image-config.js'; export type { Runtime } from './entrypoints/server.js'; export type Options = { /** Options for handling images. */ - imageService?: 'passthrough' | 'cloudflare' | 'compile' | 'custom'; + imageService?: ImageService; /** Configuration for `_routes.json` generation. A _routes.json file controls when your Function is invoked. This file will include three different properties: * * - version: Defines the version of the schema. Currently there is only one version of the schema (version 1), however, we may add more in the future and aim to be backwards compatible. @@ -148,12 +148,16 @@ export default function createIntegration(args?: Options): AstroIntegration { if (!session?.driver) { const sessionDir = isBuild ? undefined : createCodegenDir(); const bindingName = args?.sessionKVBindingName ?? 'SESSION'; - logger.info( - `Enabling sessions with ${isBuild ? 'Cloudflare KV' : 'filesystem storage'}. Be sure to define a KV binding named "${bindingName}".`, - ); - logger.info( - `If you see the error "Invalid binding \`${bindingName}\`" in your build output, you need to add the binding to your wrangler config file.`, - ); + + if (isBuild) { + logger.info( + `Enabling sessions with Cloudflare KV for production with the "${bindingName}" KV binding.`, + ); + logger.info( + `If you see the error "Invalid binding \`${bindingName}\`" in your build output, you need to add the binding to your wrangler config file.`, + ); + } + session = isBuild ? { ...session, @@ -238,11 +242,16 @@ export default function createIntegration(args?: Options): AstroIntegration { hybridOutput: 'stable', staticOutput: 'unsupported', i18nDomains: 'experimental', - sharpImageService: { - support: 'limited', - message: - 'Cloudflare does not support sharp. You can use the `compile` image service to compile images at build time. It will not work for any on-demand rendered images.', - }, + // For explicitly set image services, this hack is used to suppress the warning about using `sharp` + // by inferring the user is aware of its implications. + // TODO: If an option to supress the warnings for `supportedAstroFeatures` is added, we should use it instead. + sharpImageService: args?.imageService + ? 'stable' + : { + support: 'limited', + message: + 'Cloudflare does not support sharp at runtime. However, you can configure `image service: "compile"` to optimize images with sharp on prerendered pages during build time.', + }, envGetSecret: 'stable', }, }); diff --git a/packages/integrations/cloudflare/src/utils/image-config.ts b/packages/integrations/cloudflare/src/utils/image-config.ts index f9ed1a709fd1..967b1b014390 100644 --- a/packages/integrations/cloudflare/src/utils/image-config.ts +++ b/packages/integrations/cloudflare/src/utils/image-config.ts @@ -1,8 +1,10 @@ import type { AstroConfig, AstroIntegrationLogger, HookParameters } from 'astro'; import { passthroughImageService, sharpImageService } from 'astro/config'; +export type ImageService = 'passthrough' | 'cloudflare' | 'compile' | 'custom'; + export function setImageConfig( - service: string, + service: ImageService, config: AstroConfig['image'], command: HookParameters<'astro:config:setup'>['command'], logger: AstroIntegrationLogger, @@ -35,7 +37,7 @@ export function setImageConfig( default: if (config.service.entrypoint === 'astro/assets/services/sharp') { logger.warn( - `The current configuration does not support image optimization. To allow your project to build with the original, unoptimized images, the image service has been automatically switched to the 'noop' option. See https://docs.astro.build/en/reference/configuration-reference/#imageservice`, + `The current configuration does not support image optimization. To allow your project to build with the original, unoptimized images, the image service has been automatically switched to the 'passthrough' option. See https://docs.astro.build/en/reference/configuration-reference/#imageservice`, ); return { ...config, service: passthroughImageService() }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8c5fd637e8c..7d2f2d98d32b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3089,6 +3089,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/feature-support-message-suppresion: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/fetch: dependencies: '@astrojs/preact':