-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cloudflare):
imageService
adapter config (#34)
Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Arsh <69170106+lilnasy@users.noreply.github.com> Co-authored-by: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Co-authored-by: Sarah Rainsberger <5098874+sarah11918@users.noreply.github.com>
- Loading branch information
1 parent
3540bd2
commit 4e1060b
Showing
44 changed files
with
643 additions
and
570 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@astrojs/cloudflare': minor | ||
--- | ||
|
||
Adds an `imageService` adapter option to configure which image service is used. Read more in the [Cloudflare adapter docs](https://docs.astro.build/en/guides/integrations-guide/cloudflare/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@astrojs/cloudflare': minor | ||
--- | ||
|
||
Adds support for using Cloudflare's Image Resizing service as an external image service in Astro. See [Cloudflare's image docs](https://developers.cloudflare.com/images/image-resizing/) for more information about pricing and features. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import type { ExternalImageService } from 'astro'; | ||
|
||
import { baseService } from 'astro/assets'; | ||
import { isESMImportedImage, isRemoteAllowed, joinPaths } from '../utils/assets.js'; | ||
|
||
const service: ExternalImageService = { | ||
...baseService, | ||
getURL: (options, imageConfig) => { | ||
const resizingParams = []; | ||
if (options.width) resizingParams.push(`width=${options.width}`); | ||
if (options.height) resizingParams.push(`height=${options.height}`); | ||
if (options.quality) resizingParams.push(`quality=${options.quality}`); | ||
if (options.fit) resizingParams.push(`fit=${options.fit}`); | ||
if (options.format) resizingParams.push(`format=${options.format}`); | ||
|
||
let imageSource = ''; | ||
if (isESMImportedImage(options.src)) { | ||
imageSource = options.src.src; | ||
} else if (isRemoteAllowed(options.src, imageConfig)) { | ||
imageSource = options.src; | ||
} else { | ||
// If it's not an imported image, nor is it allowed using the current domains or remote patterns, we'll just return the original URL | ||
return options.src; | ||
} | ||
|
||
const imageEndpoint = joinPaths( | ||
// @ts-expect-error - Property 'env' does not exist on type 'ImportMeta'.ts(2339) | ||
import.meta.env.BASE_URL, | ||
'/cdn-cgi/image', | ||
resizingParams.join(','), | ||
imageSource | ||
); | ||
|
||
return imageEndpoint; | ||
}, | ||
}; | ||
|
||
export default service; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import type { AstroConfig, ImageMetadata, RemotePattern } from 'astro'; | ||
|
||
export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata { | ||
return typeof src === 'object'; | ||
} | ||
export function isRemotePath(src: string) { | ||
return /^(http|ftp|https|ws):?\/\//.test(src) || src.startsWith('data:'); | ||
} | ||
export function matchHostname(url: URL, hostname?: string, allowWildcard?: boolean) { | ||
if (!hostname) { | ||
return true; | ||
} else if (!allowWildcard || !hostname.startsWith('*')) { | ||
return hostname === url.hostname; | ||
} else if (hostname.startsWith('**.')) { | ||
const slicedHostname = hostname.slice(2); // ** length | ||
return slicedHostname !== url.hostname && url.hostname.endsWith(slicedHostname); | ||
} else if (hostname.startsWith('*.')) { | ||
const slicedHostname = hostname.slice(1); // * length | ||
const additionalSubdomains = url.hostname | ||
.replace(slicedHostname, '') | ||
.split('.') | ||
.filter(Boolean); | ||
return additionalSubdomains.length === 1; | ||
} | ||
|
||
return false; | ||
} | ||
export function matchPort(url: URL, port?: string) { | ||
return !port || port === url.port; | ||
} | ||
export function matchProtocol(url: URL, protocol?: string) { | ||
return !protocol || protocol === url.protocol.slice(0, -1); | ||
} | ||
export function matchPathname(url: URL, pathname?: string, allowWildcard?: boolean) { | ||
if (!pathname) { | ||
return true; | ||
} else if (!allowWildcard || !pathname.endsWith('*')) { | ||
return pathname === url.pathname; | ||
} else if (pathname.endsWith('/**')) { | ||
const slicedPathname = pathname.slice(0, -2); // ** length | ||
return slicedPathname !== url.pathname && url.pathname.startsWith(slicedPathname); | ||
} else if (pathname.endsWith('/*')) { | ||
const slicedPathname = pathname.slice(0, -1); // * length | ||
const additionalPathChunks = url.pathname | ||
.replace(slicedPathname, '') | ||
.split('/') | ||
.filter(Boolean); | ||
return additionalPathChunks.length === 1; | ||
} | ||
|
||
return false; | ||
} | ||
export function matchPattern(url: URL, remotePattern: RemotePattern) { | ||
return ( | ||
matchProtocol(url, remotePattern.protocol) && | ||
matchHostname(url, remotePattern.hostname, true) && | ||
matchPort(url, remotePattern.port) && | ||
matchPathname(url, remotePattern.pathname, true) | ||
); | ||
} | ||
export function isRemoteAllowed( | ||
src: string, | ||
{ | ||
domains = [], | ||
remotePatterns = [], | ||
}: Partial<Pick<AstroConfig['image'], 'domains' | 'remotePatterns'>> | ||
): boolean { | ||
if (!isRemotePath(src)) return false; | ||
|
||
const url = new URL(src); | ||
return ( | ||
domains.some((domain) => matchHostname(url, domain)) || | ||
remotePatterns.some((remotePattern) => matchPattern(url, remotePattern)) | ||
); | ||
} | ||
export function isString(path: unknown): path is string { | ||
return typeof path === 'string' || path instanceof String; | ||
} | ||
export function removeTrailingForwardSlash(path: string) { | ||
return path.endsWith('/') ? path.slice(0, path.length - 1) : path; | ||
} | ||
export function removeLeadingForwardSlash(path: string) { | ||
return path.startsWith('/') ? path.substring(1) : path; | ||
} | ||
export function trimSlashes(path: string) { | ||
return path.replace(/^\/|\/$/g, ''); | ||
} | ||
export function joinPaths(...paths: (string | undefined)[]) { | ||
return paths | ||
.filter(isString) | ||
.map((path, i) => { | ||
if (i === 0) { | ||
return removeTrailingForwardSlash(path); | ||
} else if (i === paths.length - 1) { | ||
return removeLeadingForwardSlash(path); | ||
} else { | ||
return trimSlashes(path); | ||
} | ||
}) | ||
.join('/'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import type { AstroConfig, AstroIntegrationLogger } from 'astro'; | ||
import { passthroughImageService, sharpImageService } from 'astro/config'; | ||
|
||
export function prepareImageConfig( | ||
service: string, | ||
config: AstroConfig['image'], | ||
command: 'dev' | 'build' | 'preview', | ||
logger: AstroIntegrationLogger | ||
) { | ||
switch (service) { | ||
case 'passthrough': | ||
return { ...config, service: passthroughImageService() }; | ||
|
||
case 'cloudflare': | ||
return { | ||
...config, | ||
service: | ||
command === 'dev' | ||
? sharpImageService() | ||
: { entrypoint: '@astrojs/cloudflare/image-service' }, | ||
}; | ||
|
||
default: | ||
if ( | ||
config.service.entrypoint === 'astro/assets/services/sharp' || | ||
config.service.entrypoint === 'astro/assets/services/squoosh' | ||
) { | ||
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` | ||
); | ||
return { ...config, service: passthroughImageService() }; | ||
} | ||
return { ...config }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { expect } from 'chai'; | ||
import { readFileSync } from 'node:fs'; | ||
import { fileURLToPath } from 'node:url'; | ||
import { astroCli } from './_test-utils.js'; | ||
|
||
const root = new URL('./fixtures/external-image-service/', import.meta.url); | ||
|
||
describe('ExternalImageService', () => { | ||
it('has correct image service', async () => { | ||
await astroCli(fileURLToPath(root), 'build'); | ||
const outFileToCheck = readFileSync(fileURLToPath(new URL('dist/_worker.js', root)), 'utf-8'); | ||
expect(outFileToCheck).to.include('cdn-cgi/image'); | ||
}); | ||
}); |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,6 @@ | |
"private": true, | ||
"dependencies": { | ||
"@astrojs/cloudflare": "workspace:*", | ||
"astro": "^3.2.3" | ||
"astro": "^3.4.3" | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
packages/cloudflare/test/fixtures/external-image-service/astro.config.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { defineConfig } from 'astro/config'; | ||
import cloudflare from '@astrojs/cloudflare'; | ||
|
||
export default defineConfig({ | ||
adapter: cloudflare({ | ||
imageService: 'cloudflare', | ||
}), | ||
output: 'server', | ||
}); |
9 changes: 9 additions & 0 deletions
9
packages/cloudflare/test/fixtures/external-image-service/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"name": "@test/astro-cloudflare-external-image-service", | ||
"version": "0.0.0", | ||
"private": true, | ||
"dependencies": { | ||
"@astrojs/cloudflare": "workspace:*", | ||
"astro": "^3.4.3" | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
packages/cloudflare/test/fixtures/external-image-service/src/env.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="astro/client" /> |
Oops, something went wrong.