gatsby-source-cloudinary
Advanced tools
Comparing version
@@ -1,59 +0,103 @@ | ||
const {newCloudinary, getResourceOptions} = require('./utils'); | ||
const type = `CloudinaryMedia`; | ||
const { newCloudinary, getResourceOptions } = require('./utils'); | ||
const getNodeData = (gatsby, media) => { | ||
const REPORTER_PREFIX = `gatsby-source-cloudinary`; | ||
const NODE_TYPE = `CloudinaryMedia`; | ||
const getNodeData = (gatsbyUtils, media, cloudName) => { | ||
const { createNodeId, createContentDigest } = gatsbyUtils; | ||
return { | ||
...media, | ||
id: gatsby.createNodeId(`cloudinary-media-${media.public_id}`), | ||
parent: null, | ||
id: createNodeId(`cloudinary-media-${media.public_id}`), | ||
originalHeight: media.height, | ||
originalWidth: media.width, | ||
originalFormat: media.format, | ||
cloudName: cloudName, | ||
publicId: media.public_id, | ||
cloudinaryData: media, | ||
internal: { | ||
type, | ||
type: NODE_TYPE, | ||
content: JSON.stringify(media), | ||
contentDigest: gatsby.createContentDigest(media) | ||
} | ||
contentDigest: createContentDigest(media), | ||
}, | ||
}; | ||
}; | ||
const addTransformations = (resource, transformation, secure)=>{ | ||
const splitURL = secure ? resource.secure_url.split('/') : resource.url.split('/'); | ||
splitURL.splice( 6, 0, transformation); | ||
const addTransformations = (resource, transformation, secure) => { | ||
const splitURL = secure | ||
? resource.secure_url.split('/') | ||
: resource.url.split('/'); | ||
splitURL.splice(6, 0, transformation); | ||
const transformedURL = splitURL.join('/'); | ||
return transformedURL; | ||
} | ||
}; | ||
const createCloudinaryNodes = (gatsby, cloudinary, options) => { | ||
return cloudinary.api.resources(options, (error, result) => { | ||
const hasResources = (result && result.resources && result.resources.length); | ||
const createCloudinaryNodes = async ( | ||
gatsbyUtils, | ||
cloudinary, | ||
resourceOptions, | ||
cloudName, | ||
) => { | ||
const { actions, reporter } = gatsbyUtils; | ||
const { max_results, results_per_page } = resourceOptions; | ||
if (error) { | ||
console.error(error); | ||
return; | ||
} | ||
let nextCursor = null; | ||
let limit = max_results; | ||
let resultsPerPage = results_per_page; | ||
if (!hasResources) { | ||
console.warn('\n ~Yikes! No nodes created because no Cloudinary resources found. Try a different query?'); | ||
return; | ||
} | ||
do { | ||
try { | ||
const result = await cloudinary.api.resources({ | ||
...resourceOptions, | ||
max_results: limit < resultsPerPage ? limit : resultsPerPage, | ||
next_cursor: nextCursor, | ||
}); | ||
result.resources.forEach(resource => { | ||
const transformations = "q_auto,f_auto" // Default CL transformations, todo: fetch base transformations from config maybe. | ||
resource.url = addTransformations(resource, transformations); | ||
resource.secure_url = addTransformations(resource, transformations, true); | ||
result.resources.forEach((resource) => { | ||
const transformations = 'q_auto,f_auto'; // Default CL transformations, todo: fetch base transformations from config maybe. | ||
const nodeData = getNodeData(gatsby, resource); | ||
gatsby.actions.createNode(nodeData); | ||
}); | ||
resource.url = addTransformations(resource, transformations); | ||
resource.secure_url = addTransformations( | ||
resource, | ||
transformations, | ||
true, | ||
); | ||
console.info(`Added ${hasResources} CloudinaryMedia ${hasResources > 1 ? 'nodes' : 'node'}`); | ||
}); | ||
const nodeData = getNodeData(gatsbyUtils, resource, cloudName); | ||
actions.createNode(nodeData); | ||
}); | ||
if (result.resources.length === 0) { | ||
reporter.warn( | ||
`${REPORTER_PREFIX}: No Cloudinary resources found. Try a different query?`, | ||
); | ||
} else { | ||
reporter.info( | ||
`${REPORTER_PREFIX}: Added ${result.resources.length} ${NODE_TYPE} nodes(s)`, | ||
); | ||
} | ||
nextCursor = result.next_cursor; | ||
limit = limit - result.resources.length; | ||
} catch (error) { | ||
reporter.error( | ||
`${REPORTER_PREFIX}: Fetching Cloudinary resources failed.`, | ||
error.error || error, | ||
); | ||
} | ||
} while (nextCursor && limit > 0); | ||
}; | ||
exports.sourceNodes = (gatsby, options) => { | ||
const cloudinary = newCloudinary(options); | ||
const resourceOptions = getResourceOptions(options); | ||
exports.sourceNodes = async (gatsbyUtils, pluginOptions) => { | ||
const { cloudName } = pluginOptions; | ||
const cloudinary = newCloudinary(pluginOptions); | ||
const resourceOptions = getResourceOptions(pluginOptions); | ||
return createCloudinaryNodes(gatsby, cloudinary, resourceOptions); | ||
await createCloudinaryNodes( | ||
gatsbyUtils, | ||
cloudinary, | ||
resourceOptions, | ||
cloudName, | ||
); | ||
}; |
{ | ||
"name": "gatsby-source-cloudinary", | ||
"version": "0.1.14", | ||
"version": "0.2.0", | ||
"description": "Gatsby source plugin to fetch files from Cloudinary into Gatsby.", | ||
@@ -19,5 +19,5 @@ "main": "gatsby-node.js", | ||
"dependencies": { | ||
"cloudinary": "^1.11.0", | ||
"lodash.snakecase": "^4.1.1" | ||
"cloudinary": "^1.30.0", | ||
"lodash": "^4.17.21" | ||
} | ||
} |
200
README.md
# Gatsby-Source-Cloudinary | ||
This source plugin queries media files from a Cloudinary account into `cloudinaryMedia` nodes in your Gatsby project. | ||
This source plugin queries media files from a Cloudinary account into `CloudinaryMedia` nodes in your Gatsby project. | ||
@@ -9,5 +9,8 @@ [See a live demo here](https://gsc-sample.netlify.com/) | ||
If support for the [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) is needed add and configure the [gatsby-transformer-cloudinary](https://www.gatsbyjs.com/plugins/gatsby-transformer-cloudinary/) plugin. | ||
## Motivation | ||
Gatsby offers the ability to develop high performance web pages with a rich developer experience and declarative data fetching Layer with GraphQL. | ||
Cloudinary provides a robust solution to manage media assets, from storage, optimized delivery, to media transformations. Extending the powers of Gatsby with the use of gatsby-source-cloudinary affords the best of both worlds, to allow users store media assets on Cloudinary, | ||
Gatsby offers the ability to develop high-performance web pages with a rich developer experience and declarative data fetching Layer with GraphQL. | ||
Cloudinary provides a robust solution to manage media assets, from storage, and optimized delivery, to media transformations. Extending the powers of Gatsby with the use of gatsby-source-cloudinary affords the best of both worlds, to allow users to store media assets on Cloudinary, | ||
leveraging Cloudinary's powerful optimization and transformation capabilities in fast sites built with Gatsby. | ||
@@ -18,2 +21,3 @@ | ||
## Features | ||
- Store media files on Cloudinary and deliver through a secure CDN to your Gatsby site | ||
@@ -23,37 +27,47 @@ - Average of over 60% image optimizations using `f_auto` and `q_auto` applied by default. | ||
- Utilize Cloudinary's robust transformation suite in media files on a Gatsby site. | ||
- Manage media assets of an application completely on Cloudinary rather than directly in the codebase. | ||
- Manage media assets of an application entirely on Cloudinary rather than directly in the codebase. | ||
Looking to use the features of Gatsby-Image with Cloudinary optimized storage, transformations and delivery? Checkout the [gatsby-transformer-cloudinary](https://www.npmjs.com/package/gatsby-transformer-cloudinary) plugin. | ||
Looking to use the features of Gatsby-Image with Cloudinary optimized storage, transformations, and delivery? Check out the [gatsby-transformer-cloudinary](https://www.npmjs.com/package/gatsby-transformer-cloudinary) plugin. | ||
## Example usage | ||
Here's a sample usage of the source plugin to create an image gallery from images stored on Cloudinary: | ||
```jsx harmony | ||
import React from 'react' | ||
import {useStaticQuery, graphql} from 'gatsby' | ||
Example showing use with and without [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) + [ [gatsby-transformer-cloudinary](https://www.gatsbyjs.com/plugins/gatsby-transformer-cloudinary/). The latter will add the `gatsbyImageData` resolver used below. | ||
```js | ||
import React from 'react'; | ||
import { useStaticQuery, graphql } from 'gatsby'; | ||
// Optional usage of gatsby-plugin-image | ||
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; | ||
const SingleImage = () => { | ||
const data = useStaticQuery(graphql` | ||
query CloudinaryImage { | ||
cloudinaryMedia(public_id: {eq: "gatsby-source-cloudinary/11"}) { | ||
secure_url | ||
} | ||
} | ||
` | ||
); | ||
const clImage = data.cloudinaryMedia.secure_url; | ||
const data = useStaticQuery(graphql` | ||
query CloudinaryImage { | ||
cloudinaryMedia { | ||
secure_url | ||
gatsbyImageData( | ||
width: 300 | ||
aspectRatio: 1 | ||
transformations: ["e_grayscale", "c_fill"] | ||
) | ||
} | ||
} | ||
`); | ||
return ( | ||
<div> | ||
<div> | ||
<img src={clImage} alt={"no alt :("} /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
``` | ||
const imageSrc = data.cloudinaryMedia.secure_url; | ||
const image = getImage(data.cloudinaryMedia); | ||
return ( | ||
<> | ||
<img width="300" src={imageSrc} alt={'no alt :('} /> | ||
<GatsbyImage image={image} alt="no alt :(" /> | ||
); | ||
}; | ||
export default SingleImage; | ||
``` | ||
## Installation | ||
Install the source plugin using either `npm` or `yarm`: | ||
Install the source plugin using either `npm` or `yarn`: | ||
```bash | ||
@@ -63,9 +77,29 @@ npm install --save gatsby-source-cloudinary | ||
```bash | ||
yarn add --save gatsby-source-cloudinary | ||
``` | ||
### Gatsby Plugin Image | ||
To use with [gatsby-plugin-image](https://www.gatsbyjs.com/plugins/gatsby-plugin-image/) you'll need to install it along with [gatsby-transformer-cloudinary](https://www.gatsbyjs.com/plugins/gatsby-transformer-cloudinary/). | ||
> **NOTE:** Currently in beta, may be used with both Gatsby v3 and Gatsby v4 | ||
```bash | ||
npm install --save gatsby-transformer-cloudinary@beta-v4 gatsby-plugin-image | ||
``` | ||
```bash | ||
yarn add --save gatsby-transformer-cloudinary@beta-v4 gatsby-plugin-image | ||
``` | ||
### Cloudinary Credentials | ||
Cloudinary offers a generous free tier which is more than enough to bootstrap projects. | ||
Obtain your cloudname, key and secret from your cloudinary console when you signup at [Cloudinary.com](https://cloudinary.com). | ||
Obtain your cloudname, key, and secret from your cloudinary console when you signup at [Cloudinary.com](https://cloudinary.com). | ||
### Environment configuration | ||
Store your `cloudName`, `apiKey` and `apiSecret` as environment variables for security. | ||
To do this, create a file in the root of the project named `.env`. Add your environment variables in it with: | ||
To do this, create a file in the project's root named `.env`. Add your environment variables in it with: | ||
@@ -90,3 +124,3 @@ ``` | ||
There are several options to configuring `dotenv` to use different env files either in development or production. You can find that [here](https://www.npmjs.com/package/dotenv). | ||
There are several options to configure `dotenv` to use different env files either in development or production. You can find that [here](https://www.npmjs.com/package/dotenv). | ||
@@ -98,29 +132,40 @@ Add the `.env` file to `.gitignore` so it's not committed. | ||
### Plugin setup | ||
In your `gatsby-config.js` file, include the plugin like this: | ||
```js | ||
{ | ||
resolve:`gatsby-source-cloudinary`, | ||
options: { | ||
cloudName: process.env.CLOUDINARY_CLOUD_NAME, | ||
apiKey: process.env.CLOUDINARY_API_KEY, | ||
apiSecret: process.env.CLOUDINARY_API_SECRET, | ||
resourceType: `image`, | ||
type: `type Value`, | ||
prefix: `abc-xyz/` | ||
} | ||
} | ||
module.exports = { | ||
plugins: [ | ||
{ | ||
resolve: `gatsby-source-cloudinary`, | ||
options: { | ||
cloudName: process.env.CLOUDINARY_CLOUD_NAME, | ||
apiKey: process.env.CLOUDINARY_API_KEY, | ||
apiSecret: process.env.CLOUDINARY_API_SECRET, | ||
resourceType: `image`, | ||
maxResults: 22, | ||
}, | ||
}, | ||
// Optional usage of gatsby-plugin-image | ||
{ | ||
resolve: `gatsby-transformer-cloudinary`, | ||
options: { | ||
transformTypes: [`CloudinaryMedia`], | ||
}, | ||
}, | ||
`gatsby-plugin-image`, | ||
], | ||
}; | ||
``` | ||
### Plugin options | ||
### Plugin options | ||
You can find the plugin options in the table below. | ||
| option | 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. | | ||
| `apiKey` | string | true | na/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 | true | 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. | | ||
| `resourceType` | string | false | image | This is the type of file. Possible values: image, raw, video. Note: Use the video resource type for all video resources as well as for audio files, such as .mp3. | | ||
| `resourceType` | string | false | image | This is the file type. Possible values: image, raw, video. Note: Use the video resource type for all video resources as well as for audio files, such as .mp3. | | ||
| `type` | string | false | all | This is the storage type: upload, private, authenticated, facebook, twitter, gplus, instagram_name, gravatar, youtube, hulu, vimeo, animoto, worldstarhiphop or dailymotion. | | ||
@@ -138,5 +183,6 @@ | `maxResults` | integer | false | 10 | Max number of resources to return | | ||
> All media files sourced from Cloudinary are done when Gatsby creates an optimized build, hence you will need to trigger a new production build whenever new media files are added directly on Cloudinary. | ||
> All media files sourced from Cloudinary are done when Gatsby creates an optimized build; hence you will need to trigger a new production build whenever new media files are added directly on Cloudinary. | ||
## How to use | ||
Once a development server is started using `gatsby develop`, all media assets configured in the plugin are available as `cloudinaryMedia` and `allCloudinaryMedia` in graphQL. | ||
@@ -146,37 +192,35 @@ These can run in a Page Query or StaticQuery. | ||
```jsx harmony | ||
import React from 'react' | ||
import {useStaticQuery, graphql} from 'gatsby' | ||
import React from 'react'; | ||
import { useStaticQuery, graphql } from 'gatsby'; | ||
const Images = () => { | ||
const data = useStaticQuery(graphql` | ||
query CloudinaryImages { | ||
allCloudinaryMedia { | ||
edges { | ||
node { | ||
secure_url | ||
} | ||
} | ||
} | ||
const data = useStaticQuery(graphql` | ||
query CloudinaryImages { | ||
allCloudinaryMedia { | ||
edges { | ||
node { | ||
secure_url | ||
} | ||
` | ||
); | ||
const clImages = data.allCloudinaryMedia.edges; | ||
} | ||
} | ||
} | ||
`); | ||
const clImages = data.allCloudinaryMedia.edges; | ||
return ( | ||
<div> | ||
<div> | ||
{clImages.map((image, index) => ( | ||
<div key={`${index}-cl`}> | ||
<img src={image.node.secure_url} /> | ||
</div> | ||
)) | ||
} | ||
return ( | ||
<div> | ||
<div> | ||
{clImages.map((image, index) => ( | ||
<div key={`${index}-cl`}> | ||
<img src={image.node.secure_url} /> | ||
</div> | ||
</div> | ||
) | ||
}; | ||
``` | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
``` | ||
## Other Resources | ||
- [Cloudinary image transformation reference](https://cloudinary.com/documentation/image_transformation_reference) | ||
@@ -187,5 +231,7 @@ - [Try the gatsby-transformer-cloudinary plugin to utilize the power of gatsby-image and cloudinary](https://www.npmjs.com/package/gatsby-transformer-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. | ||
Want to contribute to making 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. | ||
## License | ||
MIT | ||
MIT |
30
utils.js
@@ -1,6 +0,14 @@ | ||
const cloudinary = require("cloudinary").v2; | ||
const snakeCase = require("lodash.snakecase"); | ||
const cloudinary = require('cloudinary').v2; | ||
const { snakeCase } = require('lodash'); | ||
const DEFAULT_KEYS = ["resourceType", "prefix", "tags", "maxResults", "type", "context"]; | ||
const DEFAULT_TYPE = "upload"; | ||
const DEFAULT_KEYS = [ | ||
'resourceType', | ||
'prefix', | ||
'tags', | ||
'maxResults', | ||
'type', | ||
'context', | ||
'resultsPerPage', | ||
]; | ||
const DEFAULT_TYPE = 'upload'; | ||
@@ -14,3 +22,3 @@ const newCloudinary = (options) => { | ||
return cloudinary | ||
return cloudinary; | ||
}; | ||
@@ -21,5 +29,5 @@ | ||
DEFAULT_KEYS.forEach(key => { | ||
if (typeof options[key] !== "undefined") { | ||
result[snakeCase(key)] = options[key] | ||
DEFAULT_KEYS.forEach((key) => { | ||
if (typeof options[key] !== 'undefined') { | ||
result[snakeCase(key)] = options[key]; | ||
} | ||
@@ -29,4 +37,6 @@ }); | ||
result.type = result.type || DEFAULT_TYPE; | ||
return result | ||
if (!result.results_per_page) { | ||
result.results_per_page = result.max_results; | ||
} | ||
return result; | ||
}; | ||
@@ -33,0 +43,0 @@ |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
14672
21.13%5
25%125
73.61%229
25.14%1
Infinity%+ Added
- Removed
- Removed
Updated