diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1b1ddcd..9f08a598 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: 'lts/*' + node-version: '18' - name: Install dependencies run: yarn install --frozen-lockfile - name: Test diff --git a/.github/workflows/test-on-pr.yml b/.github/workflows/test-on-pr.yml index 5c88ad4c..97040dc8 100644 --- a/.github/workflows/test-on-pr.yml +++ b/.github/workflows/test-on-pr.yml @@ -4,9 +4,6 @@ jobs: release: name: Release runs-on: ubuntu-latest - strategy: - matrix: - node: ['14', '16', '18'] steps: - name: Checkout uses: actions/checkout@v2 @@ -15,7 +12,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: ${{ matrix.node }} + node-version: '18' - name: Install dependencies run: yarn install --frozen-lockfile - name: Test diff --git a/CHANGELOG.md b/CHANGELOG.md index c51f341c..1e330f66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,54 @@ -# [3.1.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v3.0.1...v3.1.0) (2022-12-20) +# [4.3.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v4.2.0...v4.3.0) (2022-12-21) + +### Features + +- add Gatsby as a peer dependency ([#220](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/220)) ([890193e](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/890193e82dbdafb9054d6bfe861a655777a5226c)), closes [#219](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/219) + +# [4.2.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v4.1.0...v4.2.0) (2022-12-20) + +### Bug Fixes +- gatsbyImageData generates http urls, not https ([#210](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/210)) ([3508cd3](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3508cd336d3075c88d8e1498893cfc4ff2c4f5ae)), closes [#209](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/209) +- peer dependency (gatsby-plugin-image) ([#212](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/212)) ([3f40130](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3f4013082328d57fcbefdea93f813a3a932090f1)) ### Features -* allow invalid source data by making gatsbyImageData nullable ([#218](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/218)) ([acf28f9](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/acf28f932a39ed3f864cf44aeb836e401c865692)), closes [#214](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/214) +- allow invalid source data by making gatsbyImageData nullable ([#218](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/218)) ([acf28f9](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/acf28f932a39ed3f864cf44aeb836e401c865692)), closes [#214](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/214) -## [3.0.1](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v3.0.0...v3.0.1) (2022-11-30) +# [4.1.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v4.0.1...v4.1.0) (2022-10-26) + +### Features + +- validation of plugin options ([#199](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/199)) ([ea27988](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/ea279885a873ab5d212b47e233376ec09a9b27e8)) + +## [4.0.1](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v4.0.0...v4.0.1) (2022-10-10) + +### Performance Improvements +- remove upload code only needed by gatsby-image ([#197](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/197)) ([576f30f](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/576f30f7f04545e8fc614cfeeb50784045d3037b)), closes [#188](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/188) + +# [4.0.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v3.0.0...v4.0.0) (2022-10-10) + +### Features + +- remove support for gatsby-image - deprecated Gatsby plugin ([#195](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/195)) ([d451b8e](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/d451b8e17d4bf271bc1f602fadab75d1d67adb87)) + +### BREAKING CHANGES + +- Removed support for gatsby-image (ie. `fixed` and `fluid`), use gatsby-plugin-image (ie. `gatsbyImageData` instead. + +# [3.1.0](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v3.0.1...v3.1.0) (2022-12-20) + +### Features + +- allow invalid source data by making gatsbyImageData nullable ([#218](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/218)) ([acf28f9](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/acf28f932a39ed3f864cf44aeb836e401c865692)), closes [#214](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/214) + +## [3.0.1](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare/v3.0.0...v3.0.1) (2022-11-30) ### Bug Fixes -* gatsbyImageData generates http urls, not https ([#210](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/210)) ([3508cd3](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3508cd336d3075c88d8e1498893cfc4ff2c4f5ae)), closes [#209](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/209) -* peer dependency (gatsby-plugin-image) ([#212](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/212)) ([3f40130](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3f4013082328d57fcbefdea93f813a3a932090f1)) +- gatsbyImageData generates http urls, not https ([#210](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/210)) ([3508cd3](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3508cd336d3075c88d8e1498893cfc4ff2c4f5ae)), closes [#209](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/209) +- peer dependency (gatsby-plugin-image) ([#212](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/212)) ([3f40130](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/commit/3f4013082328d57fcbefdea93f813a3a932090f1)) # Version 3.0.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..6b4d7da2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,140 @@ +# Contributor Covenant Code of Conduct + +- [Our Pledge](#our-pledge) +- [Our Standards](#our-standards) +- [Enforcement Responsibilities](#enforcement-responsibilities) +- [Scope](#scope) +- [Enforcement](#enforcement) +- [Enforcement Guidelines](#enforcement-guidelines) +- [Attribution](#attribution) + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +community@cloudinary.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][mozilla coc]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..8fb15dec --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,216 @@ +# Contributing to Gatsby Transformer Cloudinary Plugin + +First off, thank you for your interest in helping with the plugin ❀️ + +Our community looks forward to your contributions πŸŽ‰ + +## How can you help? + +You can improve [documentation](#improving-the-documentation), help [fellow users](#i-have-a-question), report [bugs](#reporting-bugs), suggest [enhancements](#suggesting-enhancements) or [contribute code](#your-first-code-contribution). + +## Why do we need your help? + +As a user, you know best how to make the Cloudinary Transformer Plugin better for users like you πŸ‘Š + +## Other ways to support the plugin + +If you like the plugin but don't have time to contribute, there are other ways to support the plugin and show your appreciation: + +⭐️ Star the GitHub repository\ +🐦 Write a tweet and mention the plugin and tag @cloudinary\ +πŸ‘©β€πŸ’» Create content and let @cloudinary know on Twitter\ +✍️ Refer to the plugin in your project's readme\ +πŸ‘©β€πŸ« Mention the plugin when you attend local meetups and tell your friends and colleagues + +  + +## πŸ“– Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) + - [Your First Code Contribution](#your-first-code-contribution) + - [Improving The Documentation](#improving-the-documentation) +- [Release Process](#release-process) +- [Additional Notes](#additional-notes) + +  + +## Code of Conduct + +> To make people feel welcome, we as contributors pledge to not harass anybody for their body size, skill level, experience level, education, socio-economic status, visible or invisible disability, ethnicity, age, gender identity and expression, nationality, the way they look, race, caste, colour, religion, or sexual identity and orientation. +> We pledge to make our community open, welcoming, diverse, inclusive and healthy by the way we interact. + +[Read the complete code of conduct](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/CODE_OF_CONDUCT.md). + +πŸ‘‰ Please report abusive, harassing, or otherwise unacceptable behavior to community@cloudinary.com. + +  + +## I Have a Question + +The guide for submitting a **question**. Following the guidelines helps us (maintainers and the community) to answer your question as fast as possible. + +### Before asking a question + +Read the documentation. Both for the [plugin](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/README.md) and [Cloudinary in general](https://cloudinary.com/documentation). +Look through existing questions both on the plugin's discussion board](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/discussions) and in the [Cloudinary Community](https://community.cloudinary.com/). + +#### How Do I Submit a Good Question? + +- Start a [discussion](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/discussions) in the repo for plugin-specific questions or start a thread in the [Cloudinary Community](https://community.cloudinary.com/) for other Cloudinary questions. +- Use a **clear and descriptive title** to identify your question. +- **Describe your question** including as many details as you can. +- If relevant **include screenshots and/or animated GIFs** to enhance the question. + +  + +## I Want To Contribute + +### Reporting Bugs + +The guide for submitting a **bug report**. Following the guidelines helps us (maintainers and the community) to understand the problem and come up with a fix. + +#### Before Submitting a Bug Report + +- Use the [latest version](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/releases). +- Reread the [readme](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/README.md) and review your plugin configuration to make sure it's correct. +- Check the [bug tracker](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues?q=label%3Abug) and see if somebody has already reported your bug or error. + - If so, commenting on the existing issue is better than opening a new issue + +Jump to ["I Have a Question"](#i-have-a-question) if you need help or support. + +#### How Do I Submit a Good Bug Report? + +We track bug reports as [Github issues](https://github.com/cloudinary-devs/issues). + +- Open an [Issue](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/new). +- **Use a clear and descriptive title** to identify the bug. +- Describe **step-by-step** how to reproduce the bug. Include as many details as you can. Don't just say what you did, but explain how you did it. +- **Describe the current behavior** and **explain the behavior you expected instead** and why. +- If possible **provide an example** to reproduce the bug. Include links to files, GitHub projects, or include copy/pasteable snippets. + - If you provide code snippets, use [Markdown code blocks](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks). +- If relevant **include screenshots and/or animated GIFs** to enhance the bug report. + +⚠️ Do _not_ include your Cloudinary API Secret or other sensitive information. + +### Suggesting Enhancements + +The guide for submitting an enhancement suggestion, both **new feature and minor improvement ideas**. Following the guidelines helps us (maintainers and the community) to understand your suggestion and follow up on it. + +#### Before Submitting an Enhancement + +- Use the [latest version](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/releases). +- Reread the [readme](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/README.md). Maybe your idea is already possible with a different configuration? +- Perform a [search](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues) and see if someone has already suggested your idea. If so, commenting on the existing issue is better than opening a new issue + +#### How Do I Submit a Good Enhancement Suggestion? + +We track enhancement ideas as [Github issues](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues). + +- Open an [Issue](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/new). +- Use a **clear and descriptive title** to identify your idea. +- **Describe your idea** including as many details as you can. +- **Describe the current behavior** and **explain the behavior you would like instead** and why. +- If relevant **include screenshots and/or animated GIFs** to enhance the description. +- **Explain why your idea is useful** to most plugin users. + +### Your Code Contribution + +The guide for submitting your **code contribution**. Following the guidelines increase the chance of getting your code released. + +If you are new to open-source code contributions, start with these links: + +- [Set up Git](https://docs.Github.com/en/get-started/quickstart/set-up-git) +- [Using pull requests when collaborating](https://docs.Github.com/en/Github/collaborating-with-pull-requests) + +Unsure where you can best start contributing code? Look through these ["good first issues"](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues?q=is%3Aissue+is%3Aopen+label%3A"good+first+issue"). + +#### Before You Start Contributing + +- Add a comment to an existing [issue](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues) indicating your interest, +- or create a new [issue](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/new) describing what you'd like to contribute. + +In both cases, outline your action plan and wait for feedback before spending too much time coding your solution in case your action plan is not within the scope/goals of this plugin. + +#### Fork, Clone, and Install + +1. [Fork the repository](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/fork) +2. Clone the fork to your local machine: `git clone /git@Github.com:/gatsby-plugin-starter.git` +3. Move into the repository folder: `cd gatsby-plugin-starter` +4. Install packages: `yarn install` + +##### A note on Yarn Workspaces + +The project uses [Yarn Workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/), hosting the plugin and demo code in one repository. You may manually test your changes while developing because the included demo will use the code in the plugin workspace (not the one published to the NPM registry). + +Install in the correct workspace by including `workspace plugin` or `workspace demo` in the command. Example: `yarn workspace plugin add lodash`. + +> Be careful when publishing packages in a workspace. If you are preparing your next release and you use a new dependency, but forgot to declare it in the package.json file, your tests might still pass locally if another package already downloaded that dependency into the workspace root. However, it will be broken for consumers that pull it from a registry, since the dependency list is now incomplete so they have no way to download the new dependency. Currently, this scenario will not let you throw a warning. +> [Yarn Workspaces Docs](https://classic.yarnpkg.com/lang/en/docs/workspaces/) + +#### Develop + +- Create a new branch and use the issue number when naming your branch: `git checkout -b --`. +- Commit your changes often: `git commit -a -m `. + +✨ Remember to update the demo and add tests when relevant. ✨ + +You'll find the plugin code in the `plugin` folder and the demo code in the `demo` folder. + +- `yarn develop` runs the demo +- `yarn watch` run the tests in watch mode + +Your local repository folder's root is where you run both scripts. + +#### Pull Request + +Before making a Pull Request, make sure you: + +- Run `yarn test` and **no tests fail**. +- Update the **readme** describing your contribution. +- The files have the **right format** run `yarn format`. + +Then you may: + +- Create a [Pull Request](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/compare). + - Select `base repository: cloudinary-devs/gatsby-transformer-cloudinary` > `base: main` + - and `head repository: ` > `compare: ` +- Write your title and description following the **[conventional commit](https://www.conventionalcommits.org/en/v1.0.0/)** standard. + - If unsure, use a clear title, and a maintainer will help you follow the conventional commit standard + +ℹ️ We will squash all your commits into one with the Pull Request title and description as the commit message, so do not worry about every single commit message being perfect. + +A maintainer will review your Pull Request in the style of [Conventional Comments](https://conventionalcomments.org/). + +- If changes are requested, make those changes. + +When a maintainer accepts and merges your Pull Request, a new version of the plugin is [auto-released](#release-process). + +### Improving The Documentation + +There are two main documentation files: + +- The project [README.md](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/README.md) = "How to use the plugin" +- The project [CONTRIBUTING.md](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/CONTRIBUTING.md) = "How to contribute to the plugin" = this text + +Improvements to both are very much encouraged πŸ™ + +You may follow the guide for [Your First Code Contribution](#your-first-code-contribution) or make changes directly from Github. + +- [Edit the README.md](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/README.md) directly on Github +- [Edit the CONTRIBUTING.md](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/edit/main/CONTRIBUTING.md) (this text) directly on Github + +  + +## Release Process + +Our plugin use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) and [semantic versioning](https://semver.org/) to automate releases by using the [semantic-release](https://semantic-release.gitbook.io/). + +  + +## Credit + +This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! \ No newline at end of file diff --git a/LICENSE b/LICENSE index 5169a5e4..b1063d05 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 gatsbyjs +Copyright (c) 2022 Cloudinary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index cf74afb6..2376aec6 100644 --- a/README.md +++ b/README.md @@ -1,319 +1,412 @@ -# gatsby-transformer-cloudinary +# Gatsby Transformer Cloudinary -The gatsby-transformer-cloudinary lets you upload local and remote assets to [Cloudinary](https://cloudinary.com/) from within your Gatsby project. It also lets you add Gatsby Image support to sourced data on existing Cloudinary assets as well as the uploaded ones. +With `gatsby-transformer-cloudinary` you may: -> Looking to simply leverage Cloudinary's storage and optimized delivery, to fetch existing media files from Cloudinary into your Gatsby project? Checkout [gatsby-source-cloudinary](https://www.npmjs.com/package/gatsby-source-cloudinary) plugin. +- πŸ–ΌοΈ Add [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) support to any GraphQL Types describing a Cloudinary assets. +- πŸ“€ Upload local and remote images to [Cloudinary](https://cloudinary.com/) from within your Gatsby project. -## Upload Assets to Cloudinary +πŸ“₯ But if you want to pull data from your Cloudinary account into the Gatsby data layer use our other plugin, [gatsby-source-cloudinary](https://www.github.com/cloudinary-devs/gatsby-source-cloudinary/) -Provides two ways to upload images to Cloudinary: +  -1. Upload images in `File` nodes to Cloudinary -2. Upload remote images by their URL to Cloudinary +## πŸ“– Table of Contents -A `CloudinaryAsset` node is created for each image. +- [πŸ–ΌοΈ Add Gatsby Image Support to Existing Cloudinary Assets](#πŸ–ΌοΈ-add-gatsby-image-support-to-existing-cloudinary-assets) + - [Install Packages](#install-packages) + - [Configure Plugins](#configure-plugins) + - [Example usage](#example-usage) +- [πŸ“€ Upload local images and add Gatsby Image Support](#πŸ“€-upload-local-images-and-add-gatsby-image-support) + - [Install Packages](#install-packages-1) + - [Configure Plugins](#configure-plugins-1) + - [Example usage](#example-usage-1) +- [πŸ“€ Upload remote images and add Gatsby Image Support](#πŸ“€-upload-remote-images-and-add-gatsby-image-support) + - [Install Packages](#install-packages-2) + - [Configure Plugins](#configure-plugins-2) + - [Example Usage](#example-usage-2) +- [πŸ”Œ Pugin Options](#πŸ”Œ-plugin-options) +- [πŸ–ΌοΈ Gatsby Image API](#πŸ–ΌοΈ-gatsby-plugin-image-gatsbyimagedata-api) +- [πŸ“š Other Resources](#πŸ“š-other-resources) +- [πŸ΄β€β˜ οΈ Contribute](#πŸ΄β€β˜ οΈ-contribute) -## Gatsby Image Support +  -Adds support for `gatsby-plugin-image` and `gatsby-image` (deprecated) by adding the resolvers `gatsbyImageData`, `fluid` (deprecated) and `fixed` (deprecated) to the configured GraphQL Types. +## πŸ–ΌοΈ Add Gatsby Image Support to Existing Cloudinary Assets -No new nodes are created, the resolvers are added to the configured GraphQL Types. By default the configurable option `transformTypes` is set to `[CloudinaryAsset]`, but you may add any GraphQL Type describing a Cloudinary image you have sourced from a CMS/Database or [gatsby-source-cloudinary](https://www.gatsbyjs.com/plugins/gatsby-source-cloudinary/). +Use assets hosted by Cloudinary together with Gatsby's Image component: -## Live demo +- The plugin adds the `gatsbyImageData` resolver to each GraphQLType configured. -[Live demo](https://gatsby-transformer-cloudinary.netlify.com/) ([source](https://github.com/jlengstorf/gatsby-transformer-cloudinary)) -> **DISCLAIMER:** If you try running this demo's source code on your own computer, you might face issues as the demo uses assets and [transformations](https://cloudinary.com/documentation/chained_and_named_transformations#named_transformations) from the author’s Cloudinary account. Before running, please remove them or replace them with images and transformations from your own Cloudinary account. +This configuration and example assumes your Gatsby Data Layer has at least one node of type `BlogPost` with a `heroImage` field describing an already uploaded Cloudinary asset. -## Features +πŸ‘‰ More details in [Transform Type Requierments](#transform-type-requierments). -- Upload local project media assets to a secure remote CDN -- Upload remote media assets to a secure remote CDN -- Utilize media assets on Cloudinary in gatsby-image -- Use gatsby-image `fluid` and `fixed` formats on Cloudinary assets - - Deprecated as `gatsby-image` is deprecated -- Use gatsby-plugin-image `gatsbyImageData` on Cloudinary assets -- Retrieve media files in optimized formats with responsive breakpoints -- Utilize all Cloudinary transformations including chained transformations in gatsby's data layer +### Install Packages -## Example usage +```bash +npm install gatsby-transformer-cloudinary gatsby-plugin-image +``` + +or + +```bash +yarn add gatsby-transformer-cloudinary gatsby-plugin-image +``` + +### Configure Plugins + +```js +// File: ./gatsby-config.js + +module.exports = { + plugins: [ + { + resolve: `gatsby-transformer-cloudinary`, + options: { + // Add the `gatsbyImageData` resolver to `BlogPostHeroImage` + transformTypes: [`BlogPostHeroImage`], + // Optional transformation option + defaultTransformations: ['c_fill', 'g_auto', 'q_auto'], + }, + }, + `gatsby-plugin-image`, + ], +}; +``` -Here's the plugin in action to fetch a fixed asset using the `useStaticQuery` API of Gatsby: +### Example Usage ```jsx +// File: ./pages/{BlogPost.slug}.js + import React from 'react'; -import { useStaticQuery, graphql } from 'gatsby'; -// Both gatsby-image and gatsby-plugin-image is supported -// gatsby-image is deprecated, use gatsby-plugin-image for new projects +import { graphql } from 'gatsby'; import { GatsbyImage, getImage } from 'gatsby-plugin-image'; -import Image from 'gatsby-image'; - -const SingleImage = () => { - const data = useStaticQuery(graphql` - query ExampleQuery { - cloudinaryAsset(publicId: { eq: "gatsby-cloudinary/jason" }) { - fixed(width: 300) { - ...CloudinaryAssetFixed - } - gatsbyImageData(width: 300, layout: FIXED) - } - } - `); - - const image = getImage(data.cloudinaryAsset); +const BlogPost = ({ data }) => { + const { blogPost } = data; + const gatsbyImage = getImage(blogPost.heroImage); return ( - <> - - banner - +
+

{blogPost.title}

+
); }; -export default SingleImage; +export const query = graphql` + query BlogPostById($id: String!) { + blogPost(id: { eq: $id }) { + title + heroImage { + gatsbyImageData( + height: 300 + aspectRatio: 2 + placeholder: TRACED_SVG + transformations: ["c_fill", "e_grayscale"] + ) + } + } + } +`; + +export default BlogPost; ``` -## Installation +### Transform Type Requierments + +You may add Gatsby Image support to any GraphQL Type describing a Cloudinary assets with this data shape: + +```js +{ + // Required + cloudName: "my-amazing-blog", + publicId: "blue-blue-blue", + // Optional: Saves a network request for size/format data per image queried if all are added + originalHeight: 360, + originalWidth: 820, + originalFormat: "jpg", + // Optional: Saves a Cloudinary transformation per image queried with `placeholder=BLURRED` as this value will be used instead + defaultBase64: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMMXG/8HwAEwAI0Bj1bnwAAAABJRU5ErkJggg==", + // Optional: Saves a Cloudinary transformation per image queried with `placeholder=TRACED_SVG` as this value will be used instead + defaultTracedSVG: "data:image/svg+xml,%3Csvg%20height%3D%229999%22%20viewBox%3D%220%200%209999%209999%22%20width%3D%229999%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22m0%200h9999v9999h-9999z%22%20fill%3D%22%23f9fafb%22%2F%3E%3C%2Fsvg%3E", +} +``` -This transformer automatically creates childCloudinaryAsset nodes for `File` nodes created by [`gatsby-source-filesystem`](https://www.gatsbyjs.org/packages/gatsby-source-filesystem/). +To find the GraphQL Type describing your Cloudinary assets use the built in [GraphiQL exlorer](https://www.gatsbyjs.com/docs/how-to/querying-data/running-queries-with-graphiql/). Either hover over the field describing the asset, or look in the "Documentation Explorer". -This transformer also allows you to pass URLs directly to Cloudinary to side-step the need to first download files to your development machine. This can be achieved by calling the `createRemoteImageNode` function from an `onCreateNode` function. +`defaultBase64` and `defaultTracedSVG` is the base64 URI of the placeholder image, it must comply with [RFC 2397](https://tools.ietf.org/html/rfc2397). -Install the plugins using either `npm` or `yarn`. +  -```sh -npm install --save gatsby-transformer-cloudinary gatsby-source-filesystem -``` +## πŸ“€ Upload Local Images and Add Gatsby Image Support -```sh -yarn add gatsby-transformer-cloudinary gatsby-source-filesystem -``` +If you upload local images to Cloudinary and skip the gatsby-transformer-sharp you speed up your build process and enjoy Cloudinary's transformations: -## How to use +- The plugin creates a `CloudinaryAsset` node for each image. +- The plugin adds a `gatsbyImageData` resolver to each node by default. -### Set up environment variables +This configuration and example assumes you have your images folder in the root of your project. -Add the data that shouldn’t be committed to Git into `.env.development`: +### Install packages -```sh -# Find these values at https://cloudinary.com/console/ -CLOUDINARY_CLOUD_NAME= -CLOUDINARY_API_KEY= -CLOUDINARY_API_SECRET= +```bash +npm install gatsby-transformer-cloudinary gatsby-source-filesystem gatsby-plugin-image ``` -> **NOTE:** you’ll also need to set these environment variables in your build system (i.e. Netlify). +or -### Configure the plugin +```bash +yarn add gatsby-transformer-cloudinary gatsby-source-filesystem gatsby-plugin-image +``` -In your `gatsby-config.js`, point `gatsby-source-filesystem` to images in your app, then set up `gatsby-transformer-cloudinary` with your credentials. +### Configure plugins ```js -// Load the environment variables. -require('dotenv').config({ - path: `.env.${process.env.NODE_ENV}`, -}); +// File: ./gatsby-config.js module.exports = { plugins: [ { resolve: `gatsby-source-filesystem`, options: { - name: `images`, - path: `${__dirname}/src/images`, + name: `gallery`, + path: `${__dirname}/gallery`, }, }, { resolve: 'gatsby-transformer-cloudinary', options: { + // Required for uploading cloudName: process.env.CLOUDINARY_CLOUD_NAME, apiKey: process.env.CLOUDINARY_API_KEY, apiSecret: process.env.CLOUDINARY_API_SECRET, - uploadFolder: 'gatsby-cloudinary', - uploadSourceInstanceNames: ['images'], + // Optional uploading options + uploadFolder: process.env.CLOUDINARY_UPLOAD_FOLDER, + uploadSourceInstanceNames: ['gallery'], + overwriteExisting: process.env.NODE_ENV === 'production' ? true : false, + // Optional transformation options + transformTypes: ['CloudinaryAsset'], + defaultTransformations: ['c_fill', 'g_auto', 'q_auto'], }, }, ], }; ``` -### Upload remote images +`process.env` ⁉️ Read about [env variables in the Gatsby docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/). -To directly upload images to Cloudinary from remote sources, you can use the `createRemoteImageNode` function: +### Example Usage -```js -// gatsby-node.js -import { createRemoteImageNode } from 'gatsby-transformer-cloudinary'; +Example of the plugin fetching an asset using the `useStaticQuery` API of Gatsby: -// This example assumes "Post" nodes are created in a `sourceNodes` function. -const POST_NODE_TYPE = 'Post'; +```jsx +// File ./components/local-upload.js -export async function onCreateNode({ - node, - actions: { createNode }, - createNodeId, - createContentDigest, - reporter, -}) { - // In this example, "Post" nodes sometimes have a "cover_photo_url" that's a link to an image. - if (node.internal.type === POST_NODE_TYPE && node.coverPhotoUrl) { - const imageNode = await createRemoteImageNode({ - url: node.coverPhotoUrl, - parentNode: node, - createNode, - createNodeId, - createContentDigest, - reporter, - }); +import React from 'react'; +import { graphql, useStaticQuery } from 'gatsby'; +import { GatsbyImage, getImage } from 'gatsby-plugin-image'; - createNodeField({ node: node, name: 'coverPhoto', value: imageNode.id }); - } -} +const LocalUploadExample = () => { + // Using gatsby-transformer-sharp + // commented out for comparison -exports.createSchemaCustomization = (gatsbyUtils) => { - const { actions } = gatsbyUtils; + // const data = useStaticQuery(graphql` + // query { + // file(name: { eq: "sergey-semin-unsplash" }) { + // childImageSharp { + // gatsbyImageData(height: 300, layout: FIXED) + // } + // } + // } + // `); - const PostType = ` - type Post implements Node { - coverPhotoUrl: String! - coverPhoto: CloudinaryAsset @link(from: "fields.coverPhoto" by: "id") + const data = useStaticQuery(graphql` + query { + file(name: { eq: "sergey-semin-unsplash" }) { + childCloudinaryAsset { + gatsbyImageData(height: 300, layout: FIXED) + } } - `; + } + `); + + // const gatsbyImage = getImage(data.file.childImageSharp); + const gatsbyImage = getImage(data.file.childCloudinaryAsset); - actions.createTypes([PostType]); + return ( + + ); }; + +export default LocalUploadExample; ``` -### Use images already on Cloudinary +  -To create GraphQL nodes for images that are already uploaded to Cloudinary, you need to create nodes containing data that describe the asset on Cloudinary. +## πŸ“€ Upload Remote Images and add Gatsby Image Support -For example, you may have sourced existing data from Cloudinary using [gatsby-source-cloudinary](https://www.gatsbyjs.com/plugins/gatsby-source-cloudinary/) and have `CloudinaryMedia` nodes that look like... +Upload remote images referenced in any node to Cloudinary and enjoy Cloudinary's transformations: -```js -{ - cloudName: "my-amazing-blog", - publicId: "blue-blue-blue", - originalHeight: 360, - originalWidth: 820, - originalFormat: "jpg" -} -``` +- The plugin creates a `CloudinaryAsset` node for each image. +- The plugin adds the `gatsbyImageData` resolver to each node by default. -Or you might have a `Post` node with a cover photo already stored on Cloudinary. The data in the Post node should then look something like... +Uploading remote image requires you to write some custom code. We'd like to make it configurable instead, let us know if you'd benefit by [joining the discussion](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/discussions/207). -```js -{ - title: "How to beat the pandemic blues", - publishedAt: "2020-07-26T21:55:13.358Z", - coverPhoto: { - cloudName: "my-amazing-blog", - publicId: "blue-blue-blue", - originalHeight: 360, - originalWidth: 820, - originalFormat: "jpg", - defaultBase64: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMMXG/8HwAEwAI0Bj1bnwAAAABJRU5ErkJggg==", - defaultTracedSVG: "data:image/svg+xml,%3Csvg%20height%3D%229999%22%20viewBox%3D%220%200%209999%209999%22%20width%3D%229999%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22m0%200h9999v9999h-9999z%22%20fill%3D%22%23f9fafb%22%2F%3E%3C%2Fsvg%3E", - } -} -``` +This configuration and example assumes your Gatsby Data Layer has at least one node of type `Project` with a `coverImageUrl` field containg a url pointing to a publically available image file. -To add Gatsby Image support you need to to add the GraphQL Type of `coverPhoto` GraphQL to the `transformTypes` plugin option array. +### Install Packages -For the example above the GraphQL Types are `CloudinaryMedia` and `PostCoverPhoto`. +```bash +npm install gatsby-transformer-cloudinary gatsby-plugin-image +``` + +or -To find the GraphQL type of the data describing the assets on Cloudinary use the GraphiQL explorer and hover over the asset key, in the second example this would be `coverPhoto`. +```bash +yarn add gatsby-transformer-cloudinary gatsby-plugin-image +``` -If you have used the upload functionality of this plugin, the GraphQL type of the nodes describing the uploaded files is `CloudinaryAsset`. +### Configure Plugins ```js +// File: ./gatsby-config.js + module.exports = { plugins: [ { resolve: 'gatsby-transformer-cloudinary', options: { - transformTypes: [ - `CloudinaryAsset`, - `PostCoverPhoto`, - `CloudinaryMedia`, - ], + // Required for uploading + cloudName: process.env.CLOUDINARY_CLOUD_NAME, + apiKey: process.env.CLOUDINARY_API_KEY, + apiSecret: process.env.CLOUDINARY_API_SECRET, + // Optional uploading options + uploadFolder: process.env.CLOUDINARY_UPLOAD_FOLDER, + overwriteExisting: process.env.NODE_ENV === 'production' ? true : false, + // Optional transformation options + transformTypes: ['CloudinaryAsset'], + defaultTransformations: ['c_fill', 'g_auto', 'q_auto'], }, }, ], }; ``` -The property `defaultBase64` in the node above can be used by your CMS/backend API to provide precomputed or cached base64 URIs for your images. The provided string must comply with [RFC 2397](https://tools.ietf.org/html/rfc2397). This base64 image will be used unless `ignoreDefaultBase64: true` is set in your GraphQL query. In cases where you prefer to have an accurate base64 image with the same transformations applied as you full-size image, you should use `ignoreDefaultBase64: true` in your GraphQL query. When a defaultBase64 property is not supplied or `ignoreDefaultBase64` is true, an API call to Cloudinary will be made when resolving your GraphQL queries to fetch the base64 image. +`process.env` ⁉️ Read about [env variables in the Gatsby docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/). + +### Example Usage + +```js +// File: ./gatsby-node.js -When providing `defaultBase64` properties, it's recommended that you set the plugin option `alwaysUseDefaultBase64` to true in development. This may result in your base64 images looking different in development and production, but it will also result in much faster development build times as fewer API calls to Cloudinary will be made. The `alwaysUseDefaultBase64` plugin option overrides the `ignoreDefaultBase64` GraphQL query parameter and forces `gatsby-transformer-cloudinary` to always use `defaultBase64` images when they are provided. +import { createRemoteImageNode } from 'gatsby-transformer-cloudinary'; -No API calls to Cloudinary for base64 images will be made if your GraphQL queries do not request base64 images. +export async function onCreateNode({ + node, + actions: { createNode }, + createNodeId, + createContentDigest, + reporter, +}) { + if (node.internal.type === 'Project' && node.coverImageUrl) { + // Upload the image to Cloudinary + const imageNode = await createRemoteImageNode({ + url: node.coverImageUrl, + parentNode: node, + createNode, + createNodeId, + createContentDigest, + reporter, + }); -The property `defaultTracedSVG` in the node above can be used by your CMS/backend to provide precomputed or cached SVG placeholders for your images. The provided string must comply with [RFC 2397](https://tools.ietf.org/html/rfc2397). It should also be encoded with something like JavaScript's `encodeURIComponent()`. + // Add node field to be used by "createSchemaCustomization" + createNodeField({ node: node, name: 'coverImage', value: imageNode.id }); + } +} -### Plugin options +exports.createSchemaCustomization = (gatsbyUtils) => { + const { actions } = gatsbyUtils; -In `gatsby-config.js` the plugin accepts the following options: + // Connect the node to the CloudinaryAsset using @link + const ProjectType = ` + type Project implements Node { + coverImageUrl: String! + coverImage: CloudinaryAsset @link(from: "fields.coverImage" by: "id") + } + `; -| option | type | required | default value | description | -| --------------------------------------------------- | ---------- | -------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `cloudName` | `String` | false | n/a | Cloud name of your Cloudinary account, can be obtained from your [Cloudinary console](https://cloudinary.com/console/). This should be stored and retrieved as an environment variable. | -| `apiKey` | `String` | false | n/a | API Key of your Cloudinary account, can be obtained from your [Cloudinary console](https://cloudinary.com/console/). This should be stored and retrieved as an environment variable. | -| `apiSecret` | `String` | false | n/a | API Secret of your Cloudinary account, can be obtained from your [Cloudinary console](https://cloudinary.com/console/). This should be stored and retrieved as an environment variable. | -| `uploadFolder` | `String` | false | n/a | An optional folder name where the uploaded assets will be stored on Cloudinary. | -| `uploadSourceInstanceNames` | `[String]` | false | n/a | An optional array limiting uploads to file nodes with a matching sourceInstanceName. | -| `transformTypes` | `[String]` | false | `['CloudinaryAsset']` | An optional array of GraphQL Types needing Gatsby Image support. Adds the resolvers `gatsbyImageData`, `fluid` (deprecated) and `fixed` (deprecated)). | -| `overwriteExisting` | `Boolean` | false | false | Whether to overwrite existing assets with the same public ID. When set to false, return immediately if an asset with the same Public ID was found. It's recommended that this is set to false in development as each image overwrite costs one Cloudinary transformation. | -| `defaultTransformations` (gatsby-plugin-image only) | `[String]` | false | ` ['c_fill', 'g_auto', 'q_auto']` | The default value for the `gatsbyImageData` resolver argument `transformations`. | -| `fluidMaxWidth` (gatsby-image only) | `Int` | false | 1000 | The maximum width needed for an image. If specifying a width bigger than the original image, the width of the original image is used instead. Used when calculating breakpoints. | -| `fluidMinWidth` (gatsby-image only) | `Int` | false | 200 | The minimum width needed for an image. Used when calculating breakpoints. | -| `createDerived` (gatsby-image only) | `Boolean` | false | true | If `true`, create and keep the derived images of the selected breakpoints during the API call. If false, images generated during the analysis process are thrown away. This option is ignored if `useCloudinaryBreakpoints` is `false`. It's recommended that you enable `createDerived` if `useCloudinaryBreakpoints` is true to store the breakpoint images and prevent them from being recalculated on every build. | -| `breakpointsMaxImages` (gatsby-image only) | `Integer` | false | 5 | Set maximum number of responsive breakpoint images generated and returned on image upload. If `useCloudinaryBreakpoints` is false, then exactly `breakpointsMaxImages` breakpoints will be created. | -| `useCloudinaryBreakpoints` (gatsby-image only) | `Boolean` | false | false | If `true`, then Cloudinary will be requested to automatically find the best breakpoints for each image. It's recommended that this option be set to `false` in development because this option uses one Cloudinary transformation for every image uploaded to Cloudinary plus one transformation for every derived image created while calculating breakpoints. | -| `enableDefaultTransformations` (gatsby-image only) | `Boolean` | false | false | `true` will add the `q_auto` and `f_auto` transformations to images for quality and format optimizations. | -| `alwaysUseDefaultBase64` (gatsby-image only) | `Boolean` | false | false | When `alwaysUseDefaultBase64` is true, `gatsby-transformer-cloudinary` will always use `defaultBase64` images when they are provided to the GraphQL layer. It's recommended that you set `alwaysUseDefaultBase64` to true in your development environment and provide `defaultBase64` properties for any images already uploaded to Cloudinary. Doing so will result in faster and cheaper builds because no Cloudinary API calls will need to be made when resolving your GraphQL queries. | - -The options `cloudName`, `apiKey`, and `apiSecret` are required if any images will be uploaded to Cloudinary during the build process. If you're solely using images already uploaded to Cloudinary, then these options can be safely omitted. - -> Note: Each derived image created for a breakpoint will consume one Cloudinary transformation. Enable the `useCloudinaryBreakpoints` option with care. If the `createDerived` option is enabled, transformations will only be consumed when the images are first created. However, created images will consume Cloudinary storage space. If `overwriteExisting` is enabled, each image that you upload will consume one transformation each time your Gatsby cache gets cleared and the image gets re-uploaded. For this reason, it's recommended that you keep `overWriteExisting` disabled and instead set the `overwriteExisting` parameter of `createRemoteImageNode` on a per-image basis when you know that an image has actually been updated. - -## Gatsby Plugin Image API + actions.createTypes([ProjectType]); +}; +``` -The plugin supports [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) by adding a `gatsbyImageData` resolver to the configured GraphQL types. +```jsx +// File: ./pages/{Article.slug}.js -### Arguments for `gatsbyImageData` +import React from 'react'; +import { graphql } from 'gatsby'; +import { GatsbyImage, getImage } from 'gatsby-plugin-image'; -#### `transformations` +const Project = ({ data }) => { + const { project } = data; + const gatsbyImage = getImage(project.coverImage); + return ( +
+

{project.name}

+
+ ); +}; -An array of "raw" cloudinary transformations added to the initial transformation together with the width and height. +export const query = graphql` + query ProjectById($id: String!) { + project(id: { eq: $id }) { + name + coverImage { + gatsbyImageData( + height: 300 + aspectRatio: 2 + placeholder: TRACED_SVG + transformations: ["c_fill", "g_auto:subject", "q_auto"] + ) + } + } + } +`; -**Type:** `[String]` -**Default:**`["c_fill", "g_auto", "q_auto"]` or the configured `defaultTransformations` -**Example:** `["c_crop", "x_300"]` +export default Project; +``` -> **WARNING:** Changing the sizing using transformations will mess with the Gatsby Image Component +  -#### `chained` +## πŸ”Œ Plugin Options -An array of "raw" cloudinary transformations added after the initial transformations above. +In `gatsby-config.js` the plugin accepts the following options: -**Type:** `[String]` -**Default:** `[]` -**Example:** `["e_grayscale","e_pixelate_faces,e_tint:100:663399:0p:white:100p"]` +### `cloudName` (required for upload functionality) -> **WARNING:** Changing the sizing using chained transformations will mess with the Gatsby Image Component +You'll find your Cloudinary account's `cloudName` in your [Cloudinary console](https://cloudinary.com/console/). -#### `placeholder` +**Type:** `String`\ +**Default:** n/a\ +**Note:** Store and retrieve your `cloudName` as [an environment variable](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/). -The style of the temporary image shown while the larger image is loaded. +### `apiKey` (required for upload functionality) -**Type:** `NONE`, `BLURRED` or `TRACED_SVG` -**Default:** `NONE` -**Example:** `BLURRED` +You'll find your Cloudinary API Key in the [Cloudinary console](https://cloudinary.com/console/). -> **NOTE:** `DOMINANT_COLOR` is not supported +**Type:** `String`\ +**Default:** n/a\ +**Note:** Store and retrieve your `apiKey` as [an environment variable](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/). + +### `apiSecret` (required for upload functionality) -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#placeholder) for more information. +You'll find your Cloudinary API Secret in your [Cloudinary console](https://cloudinary.com/console/). #### `secure` @@ -324,186 +417,127 @@ When set to `false` uses `http` instead of `https` for the image urls. #### `height` / `width` -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#widthheight) for information on `height` / `width`. +**Type:** `String`\ +**Default:** n/a\ +**Note:** Store and retrieve your `apiSecret` as [an environment variable](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/). -#### `aspectRatio` +### `uploadFolder` -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#aspectratio) for information on `aspectRatio`. +An optional folder name where the uploaded assets will be stored on Cloudinary. -#### `layout` +**Type:** `String`\ +**Default:** n/a\ -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#layout) for information on `layout`. +### `uploadSourceInstanceNames` -#### `backgroundColor` +An optional array limiting uploads to file nodes with a matching `sourceInstanceName`. -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) for information on `backgroundColor`. +**Type:** `[String]`\ +**Default:** n/a\ -#### `breakpoints` +### `transformTypes` -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) for information on `breakpoints`. +An optional array of GraphQL Types to add the `gatsbyImageData` resolver for Gatsby Image support. -#### `outputPixelDensities` +**Type:** `[String]`\ +**Default:** `['CloudinaryAsset']` -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) for information on `outputPixelDensities`. +### `overwriteExisting` -#### `sizes` +Whether to overwrite existing assets with the same public ID. When set to false, return immediately if an asset with the same Public ID was found. It's recommended that this is set to false in development as each image overwrite costs one Cloudinary transformation. -Go to the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#all-options) for information on `sizes`. +**Type:** `Boolean`\ +**Default:** `false` -## Gatsby Image API (Deprecated) +### `defaultTransformations` -This plugin can support both the `fixed` and `fluid` formats for `gatsby-image`. +The default value for the `gatsbyImageData` resolver argument `transformations`. -Both `fixed` and `fluid` accept arguments. All arguments are optional. +**Type:** `[String]`\ +**Default:** `['c_fill', 'g_auto', 'q_auto']` -> **WARNING:** Support for `gatsby-image` will be removed in the near future, please upgrade to `gatsby-plugin-image`. +  -### Arguments for both `fixed` and `fluid` +## πŸ–ΌοΈ Gatsby Plugin Image (`gatsbyImageData`) API -| argument | type | required | default | description | -| --------------------- | ----------- | -------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `cloudName` | `String` | true | `n/a` | Cloud name of your Cloudinary account, can be obtained from your [Cloudinary console](https://cloudinary.com/console/). This should be stored and retrieved as an environment variable. | -| `public_id` | `String` | true | `n/a` | Public ID of the image to retrieve from Cloudinary. This can be obtained from your Cloudinary account. | -| `transformations` | `[String!]` | false | `[]` | Array of transformations to be applied to the image. | -| `chained` | `[String!]` | false | `[]` | An array of chained transformations to be applied to the image. | -| `defaults` | `[String!]` | false | `["f_auto", "q_auto"]` | Default transformation applied to the image | -| `originalHeight` | `Int` | true | `n/a` | Height of the image fetched. This is required in gatsby-image to calculate the aspect ratio of the image. | -| `originalWidth` | `Int` | true | `n/a` | Desired width of the image. This is required in gatsby-image to calculate the aspect ratio. | -| `base64Width` | `String` | false | `30` | Base64 width of the image. | -| `version` | `Boolean` | false | `false` | Version number of image if applicable, eg. 300124291, 1241983. | -| `ignoreDefaultBase64` | `Boolean` | false | false | If this parameter is set to true, then an API call will be made to Cloudinary when resolving your GraphQL queries to fetch a base64 image regardless of whether a `defaultBase64` image was provided for the image. This parameter can be overridden by the `alwaysUseDefaultBase64` plugin option in environments where build speed and economy of Cloudinary usage is desired. | +The plugin supports [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) by adding a `gatsbyImageData` resolver to the configured GraphQL types. -### Arguments for `fixed` +### Arguments for `gatsbyImageData` -| argument | type | default | description | -| -------- | ----- | ------- | -------------------------------------------- | -| `height` | `Int` | `n/a` | The height that the image should display at. | -| `width` | `Int` | `400` | The width that the image should display at. | +#### `transformations` -### Arguments for `fluid` +An array of "raw" cloudinary transformations added to the initial transformation together with the `width` and `height`. -| argument | type | default | description | -| ---------- | ----- | --------------------------- | ----------------------------------- | -| `maxWidth` | `Int` | Original width of the image | The maximum width for fluid images. | +**Type:** `[String]`\ +**Default:**`["c_fill", "g_auto", "q_auto"]` or the configured `defaultTransformations`\ +**Example:** `["c_crop", "x_300"]` -### A note about aspect ratios +> **WARNING:** Changing the sizing using transformations will mess with the Gatsby Image Component -You’re able to change the aspect ratio of images by supplying the [aspect ratio parameter](https://cloudinary.com/documentation/image_transformation_reference#aspect_ratio_parameter) in the `transformations` argument. +#### `chained` -> **NOTE:** The aspect ratio _must_ be supplied in the `transformations` array. It **will not** be picked up from the `chained` argument. +An array of "raw" cloudinary transformations added after the initial transformations above. -### Gatsby Image Fragments +**Type:** `[String]`\ +**Default:** `[]`\ +**Example:** `["e_grayscale","e_pixelate_faces,e_tint:100:663399:0p:white:100p"]` -The fragments below can be used when querying your Cloudinary assets: +> **WARNING:** Changing the sizing using chained transformations will mess with the Gatsby Image Component -- `CloudinaryAssetFluid` -- `CloudinaryAssetFluid_noBase64` -- `CloudinaryAssetFluid_tracedSVG` -- `CloudinaryAssetFixed` -- `CloudinaryAssetFixed_noBase64` -- `CloudinaryAssetFixed_tracedSVG` +#### `placeholder` -## Avoiding stretched images using the fluid type +The style of the temporary image shown while the larger image is loaded. -As mentioned previously, images using the fluid type are stretched to match the container’s width and height. In the case where the image’s width or height is smaller than the available viewport, the image will stretch to match the container, potentially leading to unwanted problems and worsened image quality. +**Type:** `NONE`, `BLURRED` or `TRACED_SVG`\ +**Default:** `NONE`\ +**Example:** `BLURRED` -The `CloudinaryAssetFluidLimitPresentationSize` fragment can be used to to `gatsby-image` not to stretch an image larger than its maximum dimensions regardless of the size of its container: +> **NOTE:** `DOMINANT_COLOR` is not supported -```graphql -query { - file(name: { eq: "avatar" }) { - childCloudinaryAsset { - fluid { - ...CloudinaryAssetFluid - ...CloudinaryAssetFluidLimitPresentationSize - } - } - } -} -``` +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#placeholder) for more information. -## Manual Usage (gatsby-image only) +#### `height` / `width` -It’s also possible to manually create `gatsby-image`-friendly `fixed` and `fluid` objects by importing helper functions from the transformer. +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#widthheight) on `height` / `width`. -This is an advanced use case β€” if possible, try not to do this when Gatsby’s data layer is an option. This is intended for cases where assets are already on Cloudinary and moving them to the Gatsby project would be too time-intensive to be reasonable. +#### `aspectRatio` -> **NOTE:** This approach is async, which means you’ll end up with content jumping unless you manually account for the image area. You’ve been warned. +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#aspectratio) on `aspectRatio`. -### Manually creating fluid images +#### `layout` -```js -import React from 'react'; -import Image from 'gatsby-image'; -import { getFluidImageObject } from 'gatsby-transformer-cloudinary/api'; - -export default () => { - const [fluid, setFluid] = useState(undefined); - - useEffect(() => { - getFluidImageObject({ - public_id: 'gatsby-cloudinary/jason', - cloudName: 'jlengstorf', - originalHeight: 3024, - originalWidth: 4032, - breakpoints: [200, 400, 600, 800], - transformations: ['ar_16:10', 'c_fill'], - chained: ['e_grayscale,e_tint:100:663399:0p:white:100p', 't_lwj'], - }).then((result) => setFluid(result)); - }, []); - - return fluid ? Jason :

loading...

; -}; -``` +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#layout) on `layout`. -### Manually creating fixed images +#### `backgroundColor` -```js -import React from 'react'; -import Image from 'gatsby-image'; -import { getFixedImageObject } from 'gatsby-transformer-cloudinary/api'; - -export default () => { - const [fixed, setFixed] = useState(undefined); - - useEffect(() => { - getFixedImageObject({ - public_id: 'gatsby-cloudinary/jason', - cloudName: 'jlengstorf', - originalHeight: 3024, - originalWidth: 4032, - }).then((result) => setFixed(result)); - }, []); - - return fixed ? Jason :

loading...

; -}; -``` +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) on `backgroundColor`. -## Running Tests +#### `breakpoints` -Run the tests once: +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) on `breakpoints`. -``` -yarn workspace gatsby-transformer-cloudinary test -``` +#### `outputPixelDensities` -Run the tests in watch mode: +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image#all-options) on `outputPixelDensities`. -``` -yarn workspace gatsby-transformer-cloudinary test:watch -``` +#### `sizes` -## Other Resources +Read the [Gatsby Plugin Image Docs](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/#all-options) on `sizes`. + +  + +## πŸ“š Other Resources - [Cloudinary image transformation reference](https://cloudinary.com/documentation/image_transformation_reference) - [Try the gatsby-source-cloudinary plugin to source media files into Gatsby file nodes](https://www.npmjs.com/package/gatsby-source-cloudinary) - [Using Cloudinary image service for media optimization](https://www.gatsbyjs.org/docs/using-cloudinary-image-service/) -- [Learn how this plugin was built with Jason Lengstorf](https://www.learnwithjason.dev/build-a-gatsby-transformer-plugin-for-cloudinary) +- [Watch Jason Lengstorf build this plugin's first version](https://www.learnwithjason.dev/build-a-gatsby-transformer-plugin-for-cloudinary) + -## Contribute +  -Want to contribute to make this tool even better? Feel free to send in issues and pull requests on feature requests, fixes, bugs, typos, performance lapses or any other challenge faced with using this tool. +## πŸ΄β€β˜ οΈ Contribute -## License +You may improve the documentation, help fellow users, report bugs, suggest enhancements, contribute code and more. -MIT +Get started by reading the [contribution docs](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/blob/main/CONTRIBUTING.md). diff --git a/demo/gatsby-config.js b/demo/gatsby-config.js index dab48a1e..3e29b185 100644 --- a/demo/gatsby-config.js +++ b/demo/gatsby-config.js @@ -5,8 +5,7 @@ require('dotenv').config({ module.exports = { siteMetadata: { title: `Gatsby Cloudinary Image Transformer`, - description: `Demo of using the Gatsby Cloudinary image transformer with gatsby-image.`, - author: `@jlengstorf`, + description: `Demo of using the Gatsby Cloudinary image transformer with gatsby-plugin-image.`, }, plugins: [ { @@ -25,12 +24,12 @@ module.exports = { cloudName: process.env.CLOUDINARY_CLOUD_NAME, apiKey: process.env.CLOUDINARY_API_KEY, apiSecret: process.env.CLOUDINARY_API_SECRET, - uploadFolder: 'gatsby-cloudinary', + uploadFolder: process.env.GATSBY_CLOUDINARY_UPLOAD_FOLDER, uploadSourceInstanceNames: ['images'], transformTypes: [ 'CloudinaryAsset', - 'ExistingDataExampleImage', - 'ExistingDataNestedExampleImage', + 'ArticleFeatureImage', + 'BlogPostHeroImage', 'SomeBadImageData', ], }, diff --git a/demo/gatsby-node.js b/demo/gatsby-node.js index b02f8184..a2a97f6d 100644 --- a/demo/gatsby-node.js +++ b/demo/gatsby-node.js @@ -1,51 +1,23 @@ const { createRemoteImageNode } = require('gatsby-transformer-cloudinary'); const { createRemoteFileNode } = require(`gatsby-source-filesystem`); -exports.createSchemaCustomization = (gatsbyUtils) => { - const { actions } = gatsbyUtils; - - const RemoteExampleType = ` - type RemoteExample implements Node { - remoteImageUrl: String! - remoteImage: CloudinaryAsset @link(from: "fields.remoteImage" by: "id") - } - `; - - actions.createTypes([RemoteExampleType]); -}; - exports.sourceNodes = (gatsbyUtils) => { const { actions, reporter, createNodeId, createContentDigest, getCache } = gatsbyUtils; const { createNode } = actions; - const cloudinaryData1 = { + const cloudinaryData = { cloudName: 'lilly-labs-consulting', publicId: 'sample', }; - createNode({ - id: createNodeId(`ExistingData >>> 1`), - name: 'Existing data 1', - exampleImage: cloudinaryData1, - nested: { - exampleImage: cloudinaryData1, - }, - internal: { - type: 'ExistingData', - contentDigest: createContentDigest(cloudinaryData1), - }, - }); - - reporter.info(`[site] Create ExistingData node # 1`); - createNode({ id: createNodeId(`GoodData >>> 1`), name: 'GoodData', - ...cloudinaryData1, + ...cloudinaryData, internal: { type: 'SomeBadImageData', - contentDigest: createContentDigest('GoodData' + cloudinaryData1), + contentDigest: createContentDigest(cloudinaryData), }, }); @@ -54,62 +26,81 @@ exports.sourceNodes = (gatsbyUtils) => { name: 'BadData', internal: { type: 'SomeBadImageData', - contentDigest: createContentDigest('BadData'), + contentDigest: createContentDigest({}), }, }); - const cloudinaryData2 = { - cloudName: 'jlengstorf', - publicId: 'gatsby-cloudinary/jason', - originalHeight: 3024, - originalWidth: 4032, - originalFormat: 'jpg', + const blogPostData1 = { + title: 'Blog Post Example One', + slug: 'post-1', + heroImage: cloudinaryData, }; createNode({ - id: createNodeId(`ExistingData >>> 2`), - name: 'Existing data 2', - exampleImage: cloudinaryData2, - nested: { - exampleImage: cloudinaryData2, + ...blogPostData1, + id: createNodeId(`BlogPost >>> 1`), + internal: { + type: 'BlogPost', + contentDigest: createContentDigest(blogPostData1), }, + }); + + reporter.info(`[site] Create BlogPost with existing asset # 1`); + + const articleData1 = { + title: 'Article Example One', + slug: 'article-1', + feature: { + image: cloudinaryData, + }, + }; + + createNode({ + ...articleData1, + id: createNodeId(`Article >>> 1`), internal: { - type: 'ExistingData', - contentDigest: createContentDigest(cloudinaryData2), + type: 'Article', + contentDigest: createContentDigest(articleData1), }, }); - reporter.info(`[site] Create ExistingData node # 2`); + reporter.info(`[site] Create Article with existing asset # 1`); - const remoteImageUrl1 = - 'https://images.unsplash.com/photo-1654805540365-f5f7c81dfadf'; + const projectData1 = { + name: 'Project Example One', + slug: 'project-1', + coverImageUrl: + 'https://images.unsplash.com/photo-1527685609591-44b0aef2400b', + }; createNode({ - id: createNodeId(`RemoteExample >>> 1`), - name: 'Remote Example 1', - remoteImageUrl: remoteImageUrl1, + ...projectData1, + id: createNodeId(`Project >>> 1`), internal: { - type: 'RemoteExample', - contentDigest: createContentDigest(remoteImageUrl1), + type: 'Project', + contentDigest: createContentDigest(projectData1), }, }); - reporter.info(`[site] Create RemoteExample node # 1`); + reporter.info(`[site] Create Project with remote image url # 1`); - const remoteImageUrl2 = - 'https://images.unsplash.com/photo-1654795310460-78f58edf26ba'; + const projectData2 = { + name: 'Project Example Two', + slug: 'project-2', + coverImageUrl: + 'https://images.unsplash.com/photo-1631462685412-80a75dd611bc', + }; createNode({ - id: createNodeId(`RemoteExample >>> 2`), - name: 'Remote Example 2', - remoteImageUrl: remoteImageUrl2, + ...projectData2, + id: createNodeId(`Project >>> 2`), internal: { - type: 'RemoteExample', - contentDigest: createContentDigest(remoteImageUrl2), + type: 'Project', + contentDigest: createContentDigest(projectData2), }, }); - reporter.info(`[site] Create RemoteExample node # 2`); + reporter.info(`[site] Create Project with remote image url # 2`); // Should NOT be uploaded to Cloudinary // since uploadSourceImageNames is set to ["images"] @@ -131,9 +122,10 @@ exports.onCreateNode = async (gatsbyUtils) => { reporter, } = gatsbyUtils; - if (node.internal.type === 'RemoteExample') { + if (node.internal.type === 'Project' && node.coverImageUrl) { + // Upload the image to Cloudinary const imageNode = await createRemoteImageNode({ - url: node.remoteImageUrl, + url: node.coverImageUrl, parentNode: node, createNode, createNodeId, @@ -141,6 +133,20 @@ exports.onCreateNode = async (gatsbyUtils) => { reporter, }); - createNodeField({ node: node, name: 'remoteImage', value: imageNode.id }); + // Add node field to be used by "createSchemaCustomization" + createNodeField({ node: node, name: 'coverImage', value: imageNode.id }); } }; + +exports.createSchemaCustomization = (gatsbyUtils) => { + const { actions } = gatsbyUtils; + + const ProjectType = ` + type Project implements Node { + coverImageUrl: String! + coverImage: CloudinaryAsset @link(from: "fields.coverImage" by: "id") + } + `; + + actions.createTypes([ProjectType]); +}; diff --git a/demo/package.json b/demo/package.json index b5864b6d..2a261f8e 100644 --- a/demo/package.json +++ b/demo/package.json @@ -4,13 +4,12 @@ "version": "0.2.0", "private": true, "dependencies": { - "gatsby": "4.16.0", - "gatsby-image": "3.11.0", - "gatsby-plugin-image": "2.21.0", - "gatsby-source-filesystem": "4.16.0", + "gatsby": "5.3.3", + "gatsby-plugin-image": "3.3.2", + "gatsby-source-filesystem": "5.3.1", "gatsby-transformer-cloudinary": "*", - "react": "18.1.0", - "react-dom": "18.1.0" + "react": "18.2.0", + "react-dom": "18.2.0" }, "devDependencies": { "react-test-renderer": "18.2.0" diff --git a/demo/src/components/header.js b/demo/src/components/header.js index ccf06ef8..a03ecd3d 100644 --- a/demo/src/components/header.js +++ b/demo/src/components/header.js @@ -13,17 +13,8 @@ const Header = ({ siteTitle }) => ( {siteTitle}