|
| 1 | +import type { |
| 2 | + AstroAssetsFeature, |
| 3 | + AstroConfig, |
| 4 | + AstroFeatureMap, |
| 5 | + SupportsKind, |
| 6 | +} from '../@types/astro'; |
| 7 | +import { error, type LogOptions, warn } from '../core/logger/core.js'; |
| 8 | +import { bold } from 'kleur/colors'; |
| 9 | + |
| 10 | +const STABLE = 'stable'; |
| 11 | +const DEPRECATED = 'deprecated'; |
| 12 | +const UNSUPPORTED = 'unsupported'; |
| 13 | +const EXPERIMENTAL = 'experimental'; |
| 14 | + |
| 15 | +const UNSUPPORTED_ASSETS_FEATURE: AstroAssetsFeature = { |
| 16 | + supportKind: UNSUPPORTED, |
| 17 | + isSquooshCompatible: false, |
| 18 | + isSharpCompatible: false, |
| 19 | +}; |
| 20 | + |
| 21 | +// NOTE: remove for Astro 4.0 |
| 22 | +const ALL_UNSUPPORTED: Required<AstroFeatureMap> = { |
| 23 | + serverOutput: UNSUPPORTED, |
| 24 | + staticOutput: UNSUPPORTED, |
| 25 | + hybridOutput: UNSUPPORTED, |
| 26 | + assets: UNSUPPORTED_ASSETS_FEATURE, |
| 27 | +}; |
| 28 | + |
| 29 | +type ValidationResult = { |
| 30 | + [Property in keyof AstroFeatureMap]: boolean; |
| 31 | +}; |
| 32 | + |
| 33 | +/** |
| 34 | + * Checks whether an adapter supports certain features that are enabled via Astro configuration. |
| 35 | + * |
| 36 | + * If a configuration is enabled and "unlocks" a feature, but the adapter doesn't support, the function |
| 37 | + * will throw a runtime error. |
| 38 | + * |
| 39 | + */ |
| 40 | +export function validateSupportedFeatures( |
| 41 | + adapterName: string, |
| 42 | + featureMap: AstroFeatureMap = ALL_UNSUPPORTED, |
| 43 | + config: AstroConfig, |
| 44 | + logging: LogOptions |
| 45 | +): ValidationResult { |
| 46 | + const { |
| 47 | + assets = UNSUPPORTED_ASSETS_FEATURE, |
| 48 | + serverOutput = UNSUPPORTED, |
| 49 | + staticOutput = UNSUPPORTED, |
| 50 | + hybridOutput = UNSUPPORTED, |
| 51 | + } = featureMap; |
| 52 | + const validationResult: ValidationResult = {}; |
| 53 | + |
| 54 | + validationResult.staticOutput = validateSupportKind( |
| 55 | + staticOutput, |
| 56 | + adapterName, |
| 57 | + logging, |
| 58 | + 'staticOutput', |
| 59 | + () => config?.output === 'static' |
| 60 | + ); |
| 61 | + |
| 62 | + validationResult.hybridOutput = validateSupportKind( |
| 63 | + hybridOutput, |
| 64 | + adapterName, |
| 65 | + logging, |
| 66 | + 'hybridOutput', |
| 67 | + () => config?.output === 'hybrid' |
| 68 | + ); |
| 69 | + |
| 70 | + validationResult.serverOutput = validateSupportKind( |
| 71 | + serverOutput, |
| 72 | + adapterName, |
| 73 | + logging, |
| 74 | + 'serverOutput', |
| 75 | + () => config?.output === 'server' |
| 76 | + ); |
| 77 | + validationResult.assets = validateAssetsFeature(assets, adapterName, config, logging); |
| 78 | + |
| 79 | + return validationResult; |
| 80 | +} |
| 81 | + |
| 82 | +function validateSupportKind( |
| 83 | + supportKind: SupportsKind, |
| 84 | + adapterName: string, |
| 85 | + logging: LogOptions, |
| 86 | + featureName: string, |
| 87 | + hasCorrectConfig: () => boolean |
| 88 | +): boolean { |
| 89 | + if (supportKind === STABLE) { |
| 90 | + return true; |
| 91 | + } else if (supportKind === DEPRECATED) { |
| 92 | + featureIsDeprecated(adapterName, logging); |
| 93 | + } else if (supportKind === EXPERIMENTAL) { |
| 94 | + featureIsExperimental(adapterName, logging); |
| 95 | + } |
| 96 | + |
| 97 | + if (hasCorrectConfig() && supportKind === UNSUPPORTED) { |
| 98 | + featureIsUnsupported(adapterName, logging, featureName); |
| 99 | + return false; |
| 100 | + } else { |
| 101 | + return true; |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +function featureIsUnsupported(adapterName: string, logging: LogOptions, featureName: string) { |
| 106 | + error( |
| 107 | + logging, |
| 108 | + `${adapterName}`, |
| 109 | + `The feature ${featureName} is not supported by the adapter ${adapterName}.` |
| 110 | + ); |
| 111 | +} |
| 112 | + |
| 113 | +function featureIsExperimental(adapterName: string, logging: LogOptions) { |
| 114 | + warn(logging, `${adapterName}`, 'The feature is experimental and subject to issues or changes.'); |
| 115 | +} |
| 116 | + |
| 117 | +function featureIsDeprecated(adapterName: string, logging: LogOptions) { |
| 118 | + warn( |
| 119 | + logging, |
| 120 | + `${adapterName}`, |
| 121 | + 'The feature is deprecated and will be moved in the next release.' |
| 122 | + ); |
| 123 | +} |
| 124 | + |
| 125 | +const SHARP_SERVICE = 'astro/assets/services/sharp'; |
| 126 | +const SQUOOSH_SERVICE = 'astro/assets/services/squoosh'; |
| 127 | + |
| 128 | +function validateAssetsFeature( |
| 129 | + assets: AstroAssetsFeature, |
| 130 | + adapterName: string, |
| 131 | + config: AstroConfig, |
| 132 | + logging: LogOptions |
| 133 | +): boolean { |
| 134 | + const { |
| 135 | + supportKind = UNSUPPORTED, |
| 136 | + isSharpCompatible = false, |
| 137 | + isSquooshCompatible = false, |
| 138 | + } = assets; |
| 139 | + if (config?.image?.service?.entrypoint === SHARP_SERVICE && !isSharpCompatible) { |
| 140 | + error( |
| 141 | + logging, |
| 142 | + 'astro', |
| 143 | + `The currently selected adapter \`${adapterName}\` is not compatible with the service "Sharp". ${bold( |
| 144 | + 'Your project will NOT be able to build.' |
| 145 | + )}` |
| 146 | + ); |
| 147 | + return false; |
| 148 | + } |
| 149 | + |
| 150 | + if (config?.image?.service?.entrypoint === SQUOOSH_SERVICE && !isSquooshCompatible) { |
| 151 | + error( |
| 152 | + logging, |
| 153 | + 'astro', |
| 154 | + `The currently selected adapter \`${adapterName}\` is not compatible with the service "Squoosh". ${bold( |
| 155 | + 'Your project will NOT be able to build.' |
| 156 | + )}` |
| 157 | + ); |
| 158 | + return false; |
| 159 | + } |
| 160 | + |
| 161 | + return validateSupportKind(supportKind, adapterName, logging, 'assets', () => true); |
| 162 | +} |
0 commit comments