├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── astro.config.mjs ├── index.ts ├── package-lock.json ├── package.json ├── public ├── NotFound.svg └── favicon.svg ├── src ├── Cloudinary.astro ├── CloudinaryProps.ts ├── env.d.ts ├── index.ts └── pages │ └── index.astro └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "astro-build.astro-vscode", 4 | "esbenp.prettier-vscode", 5 | "usernamehw.errorlens", 6 | "wix.vscode-import-cost", 7 | "dbaeumer.vscode-eslint", 8 | "bradlc.vscode-tailwindcss", 9 | "streetsidesoftware.code-spell-checker", 10 | "ms-playwright.playwright" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.validate": [ 3 | "javascript", 4 | "javascriptreact", 5 | "astro", // Enable .astro 6 | "typescript", // Enable .ts 7 | "typescriptreact" // Enable .tsx 8 | ], 9 | "editor.codeActionsOnSave": { 10 | "source.fixAll.eslint": true 11 | }, 12 | "javascript.format.insertSpaceBeforeFunctionParenthesis": true, 13 | "editor.insertSpaces": false, 14 | "editor.detectIndentation": false, 15 | "cSpell.enableFiletypes": ["javascript"], 16 | "cSpell.language": "es,es-ES,en,en-US,en-GB", 17 | "prettier.documentSelectors": ["**/*.astro"] 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Eric Risco de la Torre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![astrocloudinary](https://user-images.githubusercontent.com/20164590/221385959-78078d28-79c3-40ec-b2c1-c4dfa01c03ad.png) 2 | 3 | # 🚀 Astro Cloudinary Image 4 | 5 | This [Astro](https://astro.build/) component makes it easy to wrap the Cloudinary URL Generator library to dynamically generate and optimize images from the Cloudinary service. 6 | 7 | Pull requests and/or feature requests are very welcome! 8 | 9 | ## 🔥 Motivation 10 | 11 | This project aims to participate in [@midudev](https://www.github.com/midudev) hackathon: [Cloudinary CloudCreate](https://cloudinary.com/blog/cloudinary-cloudcreate-tech-products-hackathon?utm_source=twitter&utm_medium=social-company&utm_campaign=1307&utm_term=194&utm_content=blog_cloudcreate_hackathon), in collaboration with [Cloudinary](https://cloudinary.com/). 12 | 13 | ## 💻 Features 14 | 15 | Below is a short list of features that **Astro Cloudinary Image** offers. 16 | 17 | - ✅ **Crops the image to fit the specified dimensions.** 18 | - ✅ **Scales the image to fill the specified dimensions.** 19 | - ✅ **Scales the image to the specified dimensions.** 20 | - ✅ **Scales the image to fit within the specified dimensions.** 21 | - ✅ **Generates a thumbnail of the image.** 22 | - ✅ **Centers the image around detected faces.** 23 | - ✅ **Automatically detects the subject of the image and centers it.** 24 | - ✅ **Add corner radius from 1px to max (round image)** 25 | - ✅ **Rotate the image** 26 | - ✅ **Applies various effects to the image.** 27 | 28 | ## 🛠️ Installation 29 | 30 | To install Astro Cloudinary Image, run the following command in your terminal: 31 | 32 | ```bash 33 | npm install astro-cloudinary-image 34 | ``` 35 | 36 | or if you use yarn: 37 | 38 | ```bash 39 | yarn add astro-cloudinary-image 40 | ``` 41 | 42 | or if you use pnpm: 43 | 44 | ```bash 45 | pnpm add astro-cloudinary-image 46 | ``` 47 | 48 | ## 💻 How To Use 49 | 50 | 1. In any of your Astro projects, you need to create a `.env` file and add your cloud name on Cloudinary, like: 51 | 52 | ```bash 53 | CLOUDINARY_CLOUD_NAME=XXXXXXX 54 | ``` 55 | 56 | Where `XXXXXXX` is your cloud name. Tip: You can find your Cloudinary Cloud Name right on the main Dashboard of your account. 57 | 58 | 2. In any of your Astro pages, import `Cloudinary` and then use the component like: 59 | 60 | ```astro 61 | --- 62 | import { Cloudinary } from 'astro-cloudinary-image'; 63 | --- 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Astro 72 | 73 | 74 | 86 | 87 | 88 | ``` 89 | ## 🗺️ Mandatory Props 90 | 91 | 1. `src: string` - The URL of the image to be displayed. This `src` can be a full image url or the Cloudinary identifier on your media library 92 | 93 | 2. `alt: string` - The alt text for the image. 94 | 95 | 3. `errorImage: string` - A URL for a fallback image to display if the src image fails to load. 96 | 97 | ## 🗺️ Optional Props 98 | 99 | 1. `width?: string;` - Sets the width of the image. It accepts a string value that can be a number in pixels (e.g. "100", "50"). 100 | 101 | 2. `height?: string;` - Sets the height of the image. It accepts a string value that can be a number in pixels (e.g. "100", "50"). 102 | 103 | 3. `resize?:` - Specifies the resizing behavior of the image. It can have one of the following values: 104 | 105 | Resize | Option Description 106 | ------------ | ------------- 107 | "crop" | Crops the image to fit the specified dimensions. 108 | "fill" | Scales the image to fill the specified dimensions. 109 | "scale" | Scales the image to the specified dimensions. 110 | "minimumPad" | Scales the image to fit within the specified dimensions, adding padding if necessary. 111 | "fit" | Scales the image to fit within the specified dimensions. 112 | "limitFit" | Scales the image down to fit within the specified dimensions, but doesn't scale it up. 113 | "thumbnail" | Generates a thumbnail of the image. 114 | "limitFill" | Scales the image up or down to fill the specified dimensions, but doesn't crop it. 115 | "minimumFit" | Scales the image up or down to fit within the specified dimensions, but doesn't scale it beyond its original size. 116 | "limitPad" | Scales the image down to fit within the specified dimensions, adding padding if necessary, but doesn't scale it up. 117 | "fillPad" | Scales the image up or down to fill the specified dimensions, adding padding if necessary. 118 | 119 | 4. `Gravity?:` - Specifies the gravity or position of the image. It can have one of the following values: 120 | 121 | Gravity | Option Description 122 | ------------ | ------------- 123 | "face" | centers the image around detected faces. 124 | "auto" | automatically detects the subject of the image and centers it. 125 | 126 | 5. `cornerRadius?:` number | "max"; - Specifies the radius of the corners of the image. It accepts a number value that represents the radius in pixels or the string value "max" that sets the maximum possible radius. 127 | 128 | 6. `rotate?:` number; - Rotates the image by a specified angle in degrees. 129 | 130 | 7. `effects?:` - Applies various effects to the image. It can have one or more of the following values: 131 | 132 | Effect | Option Description 133 | ------------ | ------------- 134 | "blur" | blurs the image. 135 | "grayscale" | converts the image to grayscale. 136 | "sepia" | adds a sepia tone to the image. 137 | "shadow" | adds a drop shadow to the image. 138 | "colorize" | applies a color overlay to the image. 139 | "oilPaint" | applies an oil painting effect to the image. 140 | "cartoonify" | applies a cartoon effect to the image. 141 | "outline" | adds an outline to the image. 142 | "blackwhite" | converts the image to black and white. 143 | "makeTransparent" | makes the background of the image transparent. 144 | "vectorize" | converts the image to a vector format. 145 | "gradientFade" | applies a gradient fade effect to the image. 146 | "assistColorBlind" | simulates the experience of color blindness. 147 | "backgroundRemoval" | removes the background of the image. 148 | "dropShadow" | adds a drop shadow to the image. 149 | 150 | __**In order to use `backgroundRemoval` you must activate `Cloudinary AI Background Removal` on Addons of your Cloudinary account. The `backgroundRemoval` must be allways the first effect**__ 151 | 152 | # ✅ Live Example 153 | 154 | If you want to view live examples of the component, clone this repo and `npm run dev` it. 155 | 156 | ## 157 | 158 | If you want to report any issues or have found a missing feature, please report it on [GitHub](https://github.com/ericrisco/astro-cloudinary-image/issues)! 159 | 160 | Good luck out there, Astronaut. 🚀 161 | 162 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | // https://astro.build/config 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src"; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-cloudinary-image", 3 | "version": "0.0.6", 4 | "description": "Adds a Cloudinary image component to Astro", 5 | "homepage": "https://github.com/ericrisco/astro-cloudinary-image#readme", 6 | "bugs": { 7 | "url": "https://github.com/ericrisco/astro-cloudinary-image/issues" 8 | }, 9 | "type": "module", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/ericrisco/astro-cloudinary-image" 13 | }, 14 | "exports": { 15 | ".": "./index.ts" 16 | }, 17 | "files": [ 18 | "src", 19 | "index.ts" 20 | ], 21 | "keywords": [ 22 | "astro", 23 | "astro-component", 24 | "cloudinary", 25 | "image", 26 | "images", 27 | "optimization", 28 | "responsive-image", 29 | "astropub" 30 | ], 31 | "author": "Eric Risco de la Torre", 32 | "license": "MIT", 33 | "scripts": { 34 | "dev": "astro dev", 35 | "build": "astro build", 36 | "start": "astro preview", 37 | "format": "prettier -w ./src", 38 | "check:format": "prettier -c ./src", 39 | "release": "standard-version", 40 | "check": "astro check" 41 | }, 42 | "devDependencies": { 43 | "astro": "^2.0.14", 44 | "prettier": "^2.8.4", 45 | "prettier-plugin-astro": "^0.8.0", 46 | "standard-version": "^9.5.0" 47 | }, 48 | "dependencies": { 49 | "@cloudinary/url-gen": "^1.9.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/NotFound.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/Cloudinary.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Cloudinary } from '@cloudinary/url-gen'; 3 | import { imaggaScale, imaggaCrop, crop, fill, scale, minimumPad, fit, pad, limitFit, thumbnail, limitFill, minimumFit, limitPad, fillPad } from "@cloudinary/url-gen/actions/resize"; 4 | import { autoGravity, focusOn } from "@cloudinary/url-gen/qualifiers/gravity"; 5 | import { FocusOn } from "@cloudinary/url-gen/qualifiers/focusOn"; 6 | import { ResizeFillAction } from "@cloudinary/url-gen/actions/resize/ResizeFillAction"; 7 | import { byRadius, max } from "@cloudinary/url-gen/actions/roundCorners"; 8 | import { byAngle } from "@cloudinary/url-gen/actions/rotate" 9 | import { blur, grayscale, sepia, shadow, colorize, oilPaint, cartoonify, outline, blackwhite, makeTransparent, vectorize, gradientFade, assistColorBlind, backgroundRemoval, dropShadow } from "@cloudinary/url-gen/actions/effect"; 10 | 11 | import { type CloudinaryProps } from './CloudinaryProps.js'; 12 | 13 | const cloudName = import.meta.env.CLOUDINARY_CLOUD_NAME; 14 | 15 | if(cloudName === undefined || cloudName === null || cloudName === '') { 16 | throw new Error('Cloudinary cloud name is not defined'); 17 | } 18 | 19 | const cloudinaryMedia = new Cloudinary({ 20 | cloud: { 21 | cloudName: cloudName 22 | } 23 | }); 24 | 25 | interface Props extends CloudinaryProps { 26 | src: string; 27 | alt: string; 28 | errorImage: string; 29 | } 30 | 31 | const { ...props } = Astro.props; 32 | 33 | const imageSource = props.src.startsWith('https://') 34 | ? cloudinaryMedia.image(props.src).setDeliveryType('fetch') 35 | : cloudinaryMedia.image(props.src); 36 | 37 | if(props.effects !== undefined && props.effects !== null) { 38 | for(var i=0; i -------------------------------------------------------------------------------- /src/CloudinaryProps.ts: -------------------------------------------------------------------------------- 1 | export interface CloudinaryProps { 2 | 3 | width?: string; 4 | 5 | height?: string; 6 | 7 | resize?: 8 | | "crop" 9 | | "fill" 10 | | "scale" 11 | | "minimumPad" 12 | | "fit" 13 | | "limitFit" 14 | | "thumbnail" 15 | | "limitFill" 16 | | "minimumFit" 17 | | "limitPad" 18 | | "fillPad"; 19 | 20 | gravity?: "face" | "auto"; 21 | 22 | cornerRadius?: number | "max"; 23 | 24 | rotate?: number; 25 | 26 | effects?: [ 27 | | "blur" 28 | | "grayscale" 29 | | "sepia" 30 | | "shadow" 31 | | "colorize" 32 | | "oilPaint" 33 | | "cartoonify" 34 | | "outline" 35 | | "blackwhite" 36 | | "makeTransparent" 37 | | "vectorize" 38 | | "gradientFade" 39 | | "assistColorBlind" 40 | | "removeBackground" 41 | | "backgroundRemoval" 42 | | "dropShadow" 43 | ] 44 | 45 | } -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Cloudinary } from "./Cloudinary.astro"; 2 | export type { CloudinaryProps } from "./CloudinaryProps"; 3 | export * from "./Cloudinary.astro"; -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Cloudinary from "../Cloudinary.astro"; 3 | --- 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Astro 12 | 13 | 14 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/base" 3 | } 4 | --------------------------------------------------------------------------------