{"id":15085962,"url":"https://github.com/jasonjgardner/fresh-images-plugin","last_synced_at":"2025-10-05T15:31:31.882Z","repository":{"id":199778141,"uuid":"703698067","full_name":"jasonjgardner/fresh-images-plugin","owner":"jasonjgardner","description":"Fresh plugin for manipulating images on-the-fly.","archived":true,"fork":false,"pushed_at":"2023-12-03T12:29:22.000Z","size":55,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-19T05:47:18.079Z","etag":null,"topics":["deno","fresh","fresh-plugin"],"latest_commit_sha":null,"homepage":"https://deno.land/x/fresh_images","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jasonjgardner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-10-11T18:22:08.000Z","updated_at":"2023-12-03T12:29:55.000Z","dependencies_parsed_at":"2023-10-12T10:27:21.396Z","dependency_job_id":"b5c8df51-ce6a-4adc-ba65-8963ed5241ec","html_url":"https://github.com/jasonjgardner/fresh-images-plugin","commit_stats":null,"previous_names":["jasonjgardner/fresh-images-plugin"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasonjgardner%2Ffresh-images-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasonjgardner%2Ffresh-images-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasonjgardner%2Ffresh-images-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jasonjgardner%2Ffresh-images-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jasonjgardner","download_url":"https://codeload.github.com/jasonjgardner/fresh-images-plugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235411575,"owners_count":18986065,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","fresh","fresh-plugin"],"created_at":"2024-09-25T07:02:18.466Z","updated_at":"2025-10-05T15:31:31.585Z","avatar_url":"https://github.com/jasonjgardner.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e **This project is no longer maintained.** Switch to [aleph.js](https://github.com/alephjs/aleph.js)\n\n# Fresh Images\n\n**Image manipulation and optimization for\n[Fresh](https://github.com/deno/fresh).**\n\nInspired by [nuxt/image](https://github.com/nuxt/image) and\n[next/image](https://nextjs.org/docs/app/building-your-application/optimizing/images).\n\n## Demo\n\nA working demo is deployed at https://fresh-images.deno.dev.\n\n**([View source](https://github.com/jasonjgardner/fresh-images-demo))**\n\n## Install\n\nModify the import map in your Fresh project to include\n[ImageScript](https://github.com/matmen/ImageScript) and the\n[Fresh images plugin](https://deno.land/x/fresh_images/).\n\n`deno.json`:\n\n```json\n{\n  \"imports\": {\n    \"fresh_images/\": \"https://deno.land/x/fresh_images/\",\n    \"imagescript/\": \"https://deno.land/x/imagescript@1.2.15/\"\n  }\n}\n```\n\nInclude the plugin and desired transformation functions in your\n`fresh.config.ts` file.\n\n```ts\nimport { defineConfig } from \"$fresh/server.ts\";\nimport ImagesPlugin from \"fresh_images/mod.ts\";\nimport { resize, rotate } from \"fresh_images/transformer.ts\";\n\nexport default defineConfig({\n  plugins: [\n    ImagesPlugin({\n      route: \"/image\",\n      realPath: \"./static/images\",\n      transformers: { resize, rotate },\n    }),\n  ],\n});\n```\n\n### Initializing the Plugin\n\n**`realPath`** — Defaults to `./static/image`. This is the static asset\ndirectory which contains images to transform.\n\n**`route`** — Defaults to `/images`. This property defines the alias URL to the\nimage directory. Requests to this route are able to receive transformation\nfunction parameters to manipulate images in the `realPath` directory.\n\n\u003e **Note:** `route` should not be the same name as a subdirectory in `./static`.\n\n**`tranformers`** — An object containing transformation functions, with the\nfunction name (`fn`) as keys and the image transformation function as the value.\n\n**`build`** — Optional function to pass to Fresh's `buildStart` hook. Useful for\nimage preprocessing/pre-rendering.\n\n\u003e **Note:** Build output must be saved in the `./static` directory in order to\n\u003e be served. Use `.gitignore` to exclude the output directory if necessary.\n\n## Creating Transformations\n\nA transformer function accepts an instance of ImageScript's `Image` or `GIF`\nclass to be modified according to the given URL parameters. It must also return\nthe modified instance of `Image` or `GIF`.\n\n### [Example](https://github.com/jasonjgardner/fresh-images-demo/blob/091aa83bbbd45be4e6c5c798c58207aaeaf92dbe/fresh.config.ts#L17)\n\n```ts\nimport ImagesPlugin, {\n  extendKeyMap,\n  getParam,\n  transform,\n} from \"fresh_images/mod.ts\";\nimport { decode, type GIF, type Image } from \"imagescript/mod.ts\";\n\n/**\n * Custom transformer example.\n * Rotate an image hue by a random number of degrees.\n * Optionally accept a query parameter to invert the hue.\n */\nconst myTransformer = async (img: Image | GIF, req: Request) =\u003e {\n  const randomDegrees = Math.floor(Math.random() * 360);\n\n  // Use `extendKeyMap` to lookup custom parameters\n  const invert = getParam(req, \"invert\", extendKeyMap({ invert: \"i\" }));\n\n  // Use the `transform` helper function to apply asynchronous transformations.\n  // This ensures transformations will be applied to every frame of a GIF animation.\n  if (invert) {\n    img = await transform(img, (frame) =\u003e Promise.resolve((frame as Image).invertHue()));\n  }\n\n  return transform(\n    img,\n    (frame) =\u003e Promise.resolve((frame as Image).hueShift(randomDegrees)),\n  );\n};\n\nexport default defineConfig({\n  plugins: [\n    ImagesPlugin({\n      transformers: {\n        cool: myTransformer,\n        withCustomRoute: {\n          // Always applies this `handler` transformation to the route.\n          // Uses same static image directory!\n          path: \"/desaturate\",\n          handler: (img: Image | GIF) =\u003e\n            transform(\n              img,\n              (img) =\u003e Promise.resolve((img as Image).saturation(0, true)),\n            ),\n        },\n      },\n    }),\n});\n```\n\n[More examples](https://github.com/jasonjgardner/fresh-images-demo/tree/main/transformers)\nare available in the demo repository.\n\n#### Tip\n\nPass multiple instances of the plugin to allow transformations in more than one\nimage directory.\n\n```ts\nexport default defineConfig({\n  plugins: [\n    // This creates a route, \"/images\", that will serve images from the \"./static/image\" directory.\n    ImagesPlugin(),\n    // Create a different route to access another directory.\n    ImagesPlugin({\n      route: \"/placeholder\",\n      realPath: \"./static/placeholders\",\n      transformers: {\n        // Only specified transformers will be available on this route.\n      },\n    }),\n  ],\n});\n```\n\n\u003e **Note:** Nested directory routes are currently not supported.\n\n## Usage\n\nPass each transformation to apply as a `fn` parameter. Reference the\n[key map](./src/transformers/_keymap.ts) to view valid URL parameter keys.\n\n### URL Parameters\n\nPass image transformation parameters in URL:\n\n```html\n\u003c!-- Rotate `./static/images/nyan.gif` 45 degrees and resize it to 500×600px --\u003e\n\u003cimg src=\"/img/nyan.gif?fn=rotate\u0026d=45\u0026fn=resize\u0026rw=500\u0026rh=600\" /\u003e\n```\n\n### JSX Component\n\nReturns headless `div.fresh-image`. Contains `img.fresh-image__placeholder`\nand/or `img.fresh-image__image`.\n\n```jsx\nimport FreshImage from \"fresh_images/src/components/FreshImage.tsx\";\n\n// Example using the `transformations` property\n\u003cFreshImage\n  src=\"/image/meow.png\"\n  alt=\"Cropped to 100px by 100px starting at 100px by 100px. Then resized to 200px by 200px.\"\n  transformations={[{\n    fn: \"crop\",\n    cropX: \"100\",\n    cropY: \"100\",\n    cropWidth: \"100\",\n    cropHeight: \"100\",\n  }, {\n    fn: \"resize\",\n    resizeWidth: \"200\",\n    resizeHeight: \"200\",\n  }]}\n/\u003e\n\n// Define a `placeholder` image to display while the `src` image loads.\n\u003cFreshImage\n  src=\"/image/cat.jpg\"\n  placeholder=\"/placeholders/loading.png\"\n  alt=\"Image with custom transformation and resize\"\n  transformations={[{\n    fn: \"cool\",\n  }, {\n    fn: \"resize\",\n    rw: \"400\",\n  }]}\n/\u003e\n\n// Set the `preload` property to inject `\u003clink rel=\"preload\"\u003e` for this image.\n// (Use wisely.)\n\u003cFreshImage\n  src=\"/image/chonk.jpg\"\n  alt=\"Big, hidden image\"\n  preload=\"true\"\n  transformations={{\n    fn: \"resize\",\n    resizeWidth: \"1000\",\n    quality: 100\n  }}\n/\u003e\n```\n\n### Caching\n\nDeno Deploy\n[currently does not support the Cache API](https://docs.deno.com/deploy/api#future-support);\nhowever, Deno KV can be utilized to serve cached images. Set the following\nenvironment variables to enable caching in Deno Deploy, but **use with\ncaution**. Deno KV is not an ideal blob storage solution and comes with certain\n[costs and limitations](https://deno.com/deploy/pricing).\n\n#### Environment Variables\n\n- `FRESH_IMAGES_USE_CACHE` – Enables image caching via\n  [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) (when\n  available) or Deno KV.\n- `FRESH_IMAGES_USE_KV` — When `FRESH_IMAGES_USE_CACHE` is `true`, setting this\n  variable to `true` will cache images using\n  [kv_toolbox](https://deno.land/x/kv_toolbox@0.0.5)\n\nIf `FRESH_IMAGES_USE_CACHE=true` and `FRESH_IMAGES_USE_KV=false`, caching will\nnot be used on Deno Deploy.\n\n## Optional Build Step\n\nAlthough [caching is supported](#caching) for images rendered on-the-fly, it is\nalways faster to load a pre-rendered, static asset when available.\n\nThe Fresh images plugin runs the function provided in the `build` property\nduring\n[ahead-of-time builds](https://fresh.deno.dev/docs/concepts/ahead-of-time-builds).\nThis function receives the same arguments passed to the `ImagesPlugin` instance.\n\n```ts\nimport type { ImagesPluginOptions } from \"fresh_images/src/types.ts\";\nimport ImagesPlugin, { transform } from \"fresh_images/mod.ts\";\nimport { ensureDir, join } from \"$std/fs/mod.ts\";\nimport { defineConfig } from \"$fresh/server.ts\";\nimport { decode, GIF, Image } from \"imagescript/mod.ts\";\n\n/**\n * Pre-optimize images before serving them.\n */\nconst myBuildFunction: ImagesPluginOptions[\"build\"] = async ({\n  realPath,\n}) =\u003e {\n  const targetDir = realPath ?? \"./static\";\n  const files = Deno.readDir(\"./static/image\");\n\n  await ensureDir(targetDir);\n\n  // Resize all images in the directory\n  for await (const file of files) {\n    if (!file.isFile) {\n      continue;\n    }\n\n    const input = await Deno.readFile(`./static/image/${file.name}`);\n\n    const output = await transform(\n      await decode(input),\n      (img) =\u003e Promise.resolve((img as Image).resize(Image.RESIZE_AUTO, 100)),\n    );\n\n    // Encode at lowest quality\n\n    if (output instanceof GIF) {\n      await Deno.writeFile(\n        join(targetDir, file.name),\n        await output.encode(30),\n      );\n      continue;\n    }\n\n    await Deno.writeFile(\n      join(targetDir, file.name),\n      await output.encodeJPEG(1),\n    );\n  }\n};\n\nexport default defineConfig({\n  plugins: [\n    ImagesPlugin({\n      build: myBuildFunction,\n    }),\n  ],\n});\n```\n\n[View full example](https://github.com/jasonjgardner/fresh-images-demo/blob/main/fresh.config.ts#L37)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasonjgardner%2Ffresh-images-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjasonjgardner%2Ffresh-images-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjasonjgardner%2Ffresh-images-plugin/lists"}