New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

gatsby-transformer-cloudinary

Package Overview
Dependencies
Maintainers
2
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gatsby-transformer-cloudinary - npm Package Compare versions

Comparing version 1.1.3 to 2.1.0

get-image-objects/get-aspect-ratio.js

41

CHANGELOG.md

@@ -0,1 +1,40 @@

# Version 2.1.0
Additions:
- Added the ability to use both width and height parameters simultaneously for fixed queries.
- Added the ability to use precomputed base64 images. When precomputed base64 images are used, build times should improve and Cloudinary usage should decrease.
Fixes:
- Deeply nested asset data is now transformed into CloudinaryAsset nodes.
# Version 2.0.0
Changes:
- This is a major version bump to call attention to the change in default behavior introduced in version 1.1.1. (`f_auto` and `q_auto` are no longer added to image URLs by default.)
Fixes:
- Images uploaded using the `createRemoteImageNode` function respect the `overwriteExisting` argument when provided and fall back to using the `overwriteExisting` plugin option.
# Version 1.1.3
Fixes:
- Typo fix.
# Version 1.1.2
Fixes:
- Local images uploaded to Cloudinary now respect the `overwriteExisting` plugin option.
# Version 1.1.1
Changes:
- Added `enableDefaultTransformations` plugin option. When set to true, `f_auto` and `q_auto` are added to all source URLs automatically. Previously, this was on by default. This behavior is now opt-in.
# Version 1.1.0

@@ -14,3 +53,3 @@

- Changed the public_id to be the relative path of files without the extension instead of just the file's name. This fixes an [issue with childrenCloudinaryAsset nodes](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/42) being created instead of childCloudinaryAsset nodes.
- Changed the public_id to be the relative path of files without the extension instead of just the file's name. This fixes an [issue with childrenCloudinaryAsset nodes](https://github.com/cloudinary-devs/gatsby-transformer-cloudinary/issues/42) being created instead of childCloudinaryAsset nodes.

@@ -17,0 +56,0 @@ # Version 1.0.1

@@ -38,2 +38,3 @@ const stringify = require('fast-json-stable-stringify');

cloudName,
defaultBase64,
}) => {

@@ -70,2 +71,3 @@ let breakpoints = getDefaultBreakpoints(width);

breakpoints,
defaultBase64,

@@ -72,0 +74,0 @@ // Add the required internal Gatsby node fields.

37

create-image-node.test.js

@@ -23,3 +23,3 @@ const { createImageNode } = require('./create-image-node');

test('calculates breakpoints when they are not provided', async () => {
it('calculates breakpoints when they are not provided', async () => {
const options = getDefaultOptions({

@@ -40,3 +40,3 @@ breakpointsMaxImages: 6,

test('calculates breakpoints when they are not provided and the image is small', async () => {
it('calculates breakpoints when they are not provided and the image is small', async () => {
const options = getDefaultOptions({

@@ -57,3 +57,3 @@ breakpointsMaxImages: 6,

test('calculates breakpoints when they are not provided and the image is really small', async () => {
it('calculates breakpoints when they are not provided and the image is really small', async () => {
const options = getDefaultOptions({

@@ -74,3 +74,3 @@ breakpointsMaxImages: 6,

test('uses breakpoints when they are provided', async () => {
it('uses breakpoints when they are provided', async () => {
const options = getDefaultOptions();

@@ -92,3 +92,3 @@ getPluginOptions.mockReturnValue(options);

test('sets the cloud name', async () => {
it('sets the cloud name', async () => {
const options = getDefaultOptions({ cloudName: 'cloudName' });

@@ -104,3 +104,3 @@ getPluginOptions.mockReturnValue(options);

test('sets the public ID', async () => {
it('sets the public ID', async () => {
const options = getDefaultOptions();

@@ -118,3 +118,3 @@ getPluginOptions.mockReturnValue(options);

test('sets the version', async () => {
it('sets the version', async () => {
const options = getDefaultOptions();

@@ -132,3 +132,3 @@ getPluginOptions.mockReturnValue(options);

test('sets the original height', async () => {
it('sets the original height', async () => {
const options = getDefaultOptions();

@@ -146,3 +146,3 @@ getPluginOptions.mockReturnValue(options);

test('sets the original width', async () => {
it('sets the original width', async () => {
const options = getDefaultOptions();

@@ -160,6 +160,19 @@ getPluginOptions.mockReturnValue(options);

test('creates a node ID', async () => {
it('sets the defaultBase64 image', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const args = getDefaultArgs({
defaultBase64: 'defaultBase64',
});
const actual = createImageNode(args);
const expected = { defaultBase64: 'defaultBase64' };
expect(actual).toEqual(expect.objectContaining(expected));
});
it('creates a node ID', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const createNodeId = jest.fn(createNodeIdArg => {

@@ -190,3 +203,3 @@ expect(createNodeIdArg).toEqual(

test('sets the parent', async () => {
it('sets the parent', async () => {
const options = getDefaultOptions();

@@ -204,3 +217,3 @@ getPluginOptions.mockReturnValue(options);

test('creates the content digest', async () => {
it('creates the content digest', async () => {
const options = getDefaultOptions();

@@ -207,0 +220,0 @@ getPluginOptions.mockReturnValue(options);

@@ -41,2 +41,3 @@ const fs = require('fs-extra');

width: Int
ignoreDefaultBase64: Boolean
): CloudinaryAssetFixed!

@@ -50,2 +51,3 @@

transformations: [String!]
ignoreDefaultBase64: Boolean
): CloudinaryAssetFluid!

@@ -81,6 +83,14 @@ }

resolve: (
{ public_id, version, cloudName, originalHeight, originalWidth },
{
public_id,
version,
cloudName,
originalHeight,
originalWidth,
defaultBase64,
},
{
base64Width,
base64Transformations,
ignoreDefaultBase64,
height,

@@ -102,2 +112,4 @@ width,

base64Transformations,
defaultBase64,
ignoreDefaultBase64,
transformations,

@@ -104,0 +116,0 @@ chained,

@@ -0,1 +1,5 @@

const flatMap = require('lodash/flatMap');
const get = require('lodash/get');
const set = require('lodash/set');
const unset = require('lodash/unset');
const { createImageNode } = require('../create-image-node');

@@ -9,7 +13,10 @@

}) => {
const assetDataKeys = getAssetDataKeys(node);
assetDataKeys.forEach(assetDataKey => {
const assetData = { ...node[assetDataKey] };
delete node[assetDataKey];
const assetDataPaths = getAssetDataPaths({ node });
assetDataPaths.forEach(assetDataPath => {
const assetData = {
...get(node, assetDataPath),
};
unset(node, assetDataPath);
const assetDataPathParts = assetDataPath.split('.');
const relationshipName = assetDataPathParts[assetDataPathParts.length - 1];
if (verifyAssetData(assetData)) {

@@ -22,3 +29,4 @@ createCloudinaryAssetNode({

parentNode: node,
relationshipName: assetDataKey,
relationshipName,
assetDataPath,
});

@@ -46,4 +54,43 @@ }

function getAssetDataPaths({ node, basePath = '' }) {
const currentNode = basePath === '' ? node : get(node, basePath);
const directAssetDataPaths = Object.keys(currentNode)
.filter(key => {
return currentNode[key] && currentNode[key].cloudinaryAssetData === true;
})
.map(subPath => {
return basePath === '' ? subPath : `${basePath}.${subPath}`;
});
const objectPaths = Object.keys(currentNode)
.filter(key => {
return isObject(currentNode[key]);
})
.map(subPath => {
return basePath === '' ? subPath : `${basePath}.${subPath}`;
});
const indirectAssetDataPaths = flatMap(objectPaths, objectPath => {
return getAssetDataPaths({ node, basePath: objectPath });
});
const assetDataPaths = [...directAssetDataPaths, ...indirectAssetDataPaths];
return assetDataPaths;
}
function isObject(thing) {
return typeof thing === 'object' && thing != null;
}
function createCloudinaryAssetNode({
assetData: { cloudName, originalHeight, originalWidth, publicId, version },
assetData: {
cloudName,
defaultBase64,
originalHeight,
originalWidth,
publicId,
version,
},
assetDataPath = null,
createContentDigest,

@@ -68,2 +115,3 @@ createNode,

parentNode,
defaultBase64,
});

@@ -75,4 +123,4 @@

// Tell Gatsby to add `${relationshipName}` to the parent node.
const relationshipKey = `${relationshipName}___NODE`;
parentNode[relationshipKey] = imageNode.id;
const relationshipKey = `${assetDataPath || relationshipName}___NODE`;
set(parentNode, relationshipKey, imageNode.id);
}

@@ -24,2 +24,3 @@ const { createAssetNodesFromData } = require('./create-asset-nodes-from-data');

originalWidth: 1920,
defaultBase64: 'defaultBase64',
},

@@ -34,3 +35,55 @@ },

test('passes the right data to createImageNode', () => {
function getDefaultNestedArgs(args) {
return {
node: {
blog: {
author: {
photo: {
cloudinaryAssetData: true,
cloudName: 'cloudName',
publicId: 'publicId',
originalHeight: 1080,
originalWidth: 1920,
},
},
},
},
actions: { createNode: jest.fn() },
createNodeId: jest.fn(),
createContentDigest: jest.fn(),
...args,
};
}
it('passes deeply nested asset data to createImageNode', () => {
const args = getDefaultNestedArgs();
const assetData = { ...args.node.blog.author.photo };
createAssetNodesFromData(args);
expect(createImageNode).toHaveBeenCalledWith(
expect.objectContaining({
cloudinaryUploadResult: {
height: assetData.originalHeight,
width: assetData.originalWidth,
public_id: assetData.publicId,
},
cloudName: assetData.cloudName,
createContentDigest: args.createContentDigest,
createNodeId: args.createNodeId,
parentNode: args.node,
}),
);
});
it('adds a relationship to the parent node for nested data', () => {
const createImageNodeResult = { id: 'created-image-node-id' };
createImageNode.mockReturnValue(createImageNodeResult);
const args = getDefaultNestedArgs();
createAssetNodesFromData(args);
expect(args.node).toEqual({
blog: { author: { photo___NODE: createImageNodeResult.id } },
});
});
it('passes the right data to createImageNode', () => {
const args = getDefaultArgs();

@@ -50,2 +103,3 @@ const assetData = { ...args.node.authorPhoto };

parentNode: args.node,
defaultBase64: assetData.defaultBase64,
}),

@@ -55,3 +109,3 @@ );

test('passes the result of createImageNode to createNode', () => {
it('passes the result of createImageNode to createNode', () => {
const createImageNodeResult = 'create-image-node-result';

@@ -68,3 +122,3 @@ createImageNode.mockReturnValue(createImageNodeResult);

test('adds a relationship to the parent node', () => {
it('adds a relationship to the parent node', () => {
const createImageNodeResult = { id: 'created-image-node-id' };

@@ -78,3 +132,3 @@ createImageNode.mockReturnValue(createImageNodeResult);

test('does not call createImageNode if cloudinaryAssetData !== true', () => {
it('does not call createImageNode if cloudinaryAssetData !== true', () => {
const args = getDefaultArgs();

@@ -86,3 +140,3 @@ args.node.authorPhoto.cloudinaryAssetData = 'true';

test('does not call createImageNode if cloudName is missing', () => {
it('does not call createImageNode if cloudName is missing', () => {
const args = getDefaultArgs();

@@ -94,3 +148,3 @@ args.node.authorPhoto.cloudName = null;

test('does not call createImageNode if publicId is missing', () => {
it('does not call createImageNode if publicId is missing', () => {
const args = getDefaultArgs();

@@ -102,3 +156,3 @@ args.node.authorPhoto.publicId = null;

test('does not call createImageNode if originalHeight is missing', () => {
it('does not call createImageNode if originalHeight is missing', () => {
const args = getDefaultArgs();

@@ -110,3 +164,3 @@ args.node.authorPhoto.originalHeight = null;

test('does not call createImageNode if originalWidth is missing', () => {
it('does not call createImageNode if originalWidth is missing', () => {
const args = getDefaultArgs();

@@ -118,3 +172,3 @@ args.node.authorPhoto.originalWidth = null;

test('deletes nodes with .cloudinaryAssetData === true', () => {
it('deletes nodes with .cloudinaryAssetData === true', () => {
const args = getDefaultArgs({

@@ -121,0 +175,0 @@ node: { authorPhoto: { cloudinaryAssetData: true } },

@@ -1,100 +0,13 @@

const axios = require('axios');
const { getPluginOptions } = require('./options');
// todo: for fluid images when original width and height is not set, use width and height of image as is and make full width
// todo: investigate graphQL query overrides for width and height and abstract control to user through gql query
const {
getAspectRatio,
getBase64,
getImageURL,
} = require('./get-image-objects/get-shared-image-data');
const {
getDisplayDimensions,
} = require('./get-image-objects/get-display-dimensions');
// Define default width values for fluid, fixed and base64 images
const DEFAULT_BASE64_WIDTH = 30;
const DEFAULT_FIXED_WIDTH = 400;
const base64Cache = {};
// Create Cloudinary image URL with transformations.
const getImageURL = ({
public_id,
cloudName,
transformations = [],
chained = [],
defaults,
version = false,
}) => {
const { defaultTransformations } = getPluginOptions();
defaults = defaultTransformations || [];
const baseURL = 'https://res.cloudinary.com/';
const allTransformations = [transformations.concat(defaults).join()]
.concat(chained)
.join('/');
const imagePath = [
cloudName,
'/image/upload/',
allTransformations,
version ? `/v${version}/` : '/',
public_id,
]
.join('')
.replace('//', '/');
return baseURL + imagePath;
};
// Fetch and return Base64 image
const getBase64 = async url => {
if (!base64Cache[url]) {
const result = await axios.get(url, { responseType: 'arraybuffer' });
const data = Buffer.from(result.data).toString('base64');
base64Cache[url] = `data:image/jpeg;base64,${data}`;
}
return base64Cache[url];
};
// retrieve aspect ratio if in transformation else create aspect ratio values
const getAspectRatio = (transformations, originalAspectRatio) => {
const arTransform = transformations.find(t => t.startsWith('ar_'));
if (!arTransform) {
return originalAspectRatio;
}
const newAspectRatio = arTransform.replace('ar_', '');
if (newAspectRatio.indexOf(':') === -1) {
return Number(newAspectRatio);
}
const [w, h] = newAspectRatio.split(':').map(Number);
return w / h;
};
// Create shared image data for both fixed and fluid. Returns src and Base64
const getSharedImageData = async ({
public_id,
version,
cloudName,
base64Transformations,
transformations,
base64Width,
chained,
}) => {
const b64Transformations = base64Transformations || transformations;
const base64URL = getImageURL({
transformations: b64Transformations.concat(`w_${base64Width}`),
public_id,
version,
cloudName,
chained,
});
const base64 = await getBase64(base64URL);
const src = getImageURL({
public_id,
version,
cloudName,
transformations,
chained,
});
return { base64, src };
};
exports.getFixedImageObject = async ({

@@ -110,6 +23,10 @@ public_id,

base64Transformations = [],
defaultBase64,
ignoreDefaultBase64 = false,
transformations = [],
chained = [],
}) => {
const { base64, src } = await getSharedImageData({
const base64 = await getBase64({
defaultBase64,
ignoreDefaultBase64,
public_id,

@@ -124,2 +41,10 @@ version,

const src = getImageURL({
public_id,
version,
cloudName,
transformations,
chained,
});
const aspectRatio = getAspectRatio(

@@ -130,10 +55,9 @@ transformations,

let displayWidth;
if (!!width) {
displayWidth = Math.min(width, originalWidth);
} else if (!!height) {
displayWidth = Math.min(height * aspectRatio, originalWidth);
} else if (!height && !width) {
displayWidth = Math.min(DEFAULT_FIXED_WIDTH, originalWidth);
}
const { displayWidth, displayHeight } = getDisplayDimensions({
aspectRatio,
width,
height,
originalWidth,
originalHeight,
});

@@ -143,2 +67,3 @@ const sizes = [1, 1.5, 2, 3].map(size => ({

width: Math.round(displayWidth * size),
height: Math.round(displayHeight * size),
}));

@@ -149,6 +74,19 @@

.map(size => {
const finalTransformations = [...transformations];
if (!width && !height) {
finalTransformations.push(`w_${size.width}`);
} else if (!!width && !height) {
finalTransformations.push(`w_${size.width}`);
} else if (!!height && !width) {
finalTransformations.push(`h_${size.height}`);
} else if (!!height && !!width) {
finalTransformations.push(`w_${size.width}`);
finalTransformations.push(`h_${size.height}`);
} else {
throw Error('This should never happen.');
}
// Get URL for each image including user-defined transformations.
const url = getImageURL({
// Add the size at the end to override width for srcSet support.
transformations: transformations.concat(`w_${size.width}`),
transformations: finalTransformations,
chained,

@@ -166,3 +104,3 @@ public_id,

base64,
height: Math.round(displayWidth / aspectRatio),
height: Math.round(displayHeight),
src,

@@ -174,6 +112,2 @@ srcSet,

function onlyUnique(element, index, array) {
return array.indexOf(element) === index;
}
exports.getFluidImageObject = async ({

@@ -189,2 +123,4 @@ public_id,

base64Transformations = [],
defaultBase64,
ignoreDefaultBase64 = false,
transformations = [],

@@ -200,12 +136,20 @@ chained = [],

const sizes = `(max-width: ${max}px) 100vw, ${max}px`;
const { base64, src } = await getSharedImageData({
const base64 = await getBase64({
defaultBase64,
ignoreDefaultBase64,
public_id,
version,
cloudName,
breakpoints,
base64Transformations,
base64Width,
transformations,
base64Width,
chained,
});
const src = getImageURL({
public_id,
version,
cloudName,
transformations,
chained,
});

@@ -249,1 +193,5 @@ const breakpointWidths = breakpoints

};
function onlyUnique(element, index, array) {
return array.indexOf(element) === index;
}

@@ -160,2 +160,12 @@ const {

it('allows the user to set the width and height simultaneously', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const args = getDefaultArgs({ width: 100, height: 100 });
expect(await getFixedImageObject(args)).toEqual(
expect.objectContaining({ width: 100, height: 100 }),
);
});
it('creates a srcset with multiple images based on the provided width', async () => {

@@ -172,6 +182,4 @@ const options = getDefaultOptions();

];
expect(await getFixedImageObject(args)).toEqual(
expect.objectContaining({
srcSet: expectedSrcSet.join(','),
}),
expect((await getFixedImageObject(args)).srcSet).toEqual(
expectedSrcSet.join(','),
);

@@ -183,17 +191,31 @@ });

getPluginOptions.mockReturnValue(options);
const args = getDefaultArgs({ height: 100 });
const args = getDefaultArgs({ height: 97 });
const expectedSrcSet = [
'https://res.cloudinary.com/cloudName/image/upload/w_178/public_id 1x',
'https://res.cloudinary.com/cloudName/image/upload/w_267/public_id 1.5x',
'https://res.cloudinary.com/cloudName/image/upload/w_356/public_id 2x',
'https://res.cloudinary.com/cloudName/image/upload/w_533/public_id 3x',
'https://res.cloudinary.com/cloudName/image/upload/h_97/public_id 1x',
'https://res.cloudinary.com/cloudName/image/upload/h_146/public_id 1.5x',
'https://res.cloudinary.com/cloudName/image/upload/h_194/public_id 2x',
'https://res.cloudinary.com/cloudName/image/upload/h_291/public_id 3x',
];
expect(await getFixedImageObject(args)).toEqual(
expect.objectContaining({
srcSet: expectedSrcSet.join(','),
}),
expect((await getFixedImageObject(args)).srcSet).toEqual(
expectedSrcSet.join(','),
);
});
it('creates a srcset with multiple images based on the provided width and height', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const args = getDefaultArgs({ width: 89, height: 97 });
const expectedSrcSet = [
'https://res.cloudinary.com/cloudName/image/upload/w_89,h_97/public_id 1x',
'https://res.cloudinary.com/cloudName/image/upload/w_134,h_146/public_id 1.5x',
'https://res.cloudinary.com/cloudName/image/upload/w_178,h_194/public_id 2x',
'https://res.cloudinary.com/cloudName/image/upload/w_267,h_291/public_id 3x',
];
expect((await getFixedImageObject(args)).srcSet).toEqual(
expectedSrcSet.join(','),
);
});
it('returns a base64 image', async () => {

@@ -222,2 +244,50 @@ const options = getDefaultOptions();

});
it('uses defaultBase64 images when provided', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const defaultBase64 = 'defaultBase64';
const args = getDefaultArgs({ defaultBase64 });
expect(await getFluidImageObject(args)).toEqual(
expect.objectContaining({ base64: defaultBase64 }),
);
});
it('ignores defaultBase64 images when ignoreDefaultBase64 is true', async () => {
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const defaultBase64 = 'defaultBase64';
const args = getDefaultArgs({ defaultBase64, ignoreDefaultBase64: true });
expect(await getFluidImageObject(args)).toEqual(
expect.objectContaining({ base64: 'data:image/jpeg;base64,AQID' }),
);
});
it('uses defaultBase64 images when ignoreDefaultBase64 is true and alwaysUseDefaultBase64 is true', async () => {
const options = getDefaultOptions({ alwaysUseDefaultBase64: true });
getPluginOptions.mockReturnValue(options);
const defaultBase64 = 'defaultBase64';
const args = getDefaultArgs({ defaultBase64, ignoreDefaultBase64: true });
expect(await getFluidImageObject(args)).toEqual(
expect.objectContaining({ base64: defaultBase64 }),
);
});
it('uses defaultBase64 images when ignoreDefaultBase64 is false and alwaysUseDefaultBase64 is true', async () => {
const options = getDefaultOptions({ alwaysUseDefaultBase64: true });
getPluginOptions.mockReturnValue(options);
const defaultBase64 = 'defaultBase64';
const args = getDefaultArgs({ defaultBase64, ignoreDefaultBase64: true });
expect(await getFluidImageObject(args)).toEqual(
expect.objectContaining({ base64: defaultBase64 }),
);
});
});

@@ -10,2 +10,4 @@ let options = null;

overwriteExisting: false,
enableDefaultTransformations: false,
alwaysUseDefaultBase64: false,
};

@@ -33,10 +35,2 @@

// Set default transformations based on plugin option sent by the user
const defaultTransformations = ['f_auto', 'q_auto'];
if(pluginOptions.enableDefaultTransformations && pluginOptions.enableDefaultTransformations === true){
pluginOptions.defaultTransformations = defaultTransformations;
}else{
pluginOptions.defaultTransformations = [];
}
options = {

@@ -43,0 +37,0 @@ ...defaultOptions,

{
"name": "gatsby-transformer-cloudinary",
"version": "1.1.3",
"version": "2.1.0",
"description": "Transform local files into Cloudinary-managed assets for Gatsby sites.",

@@ -24,3 +24,4 @@ "main": "index.js",

"fast-json-stable-stringify": "^2.1.0",
"fs-extra": "^9.0.1"
"fs-extra": "^9.0.1",
"lodash": "^4.17.20"
},

@@ -27,0 +28,0 @@ "devDependencies": {

@@ -163,2 +163,3 @@ # gatsby-transformer-cloudinary

originalWidth: 820,
defaultBase64: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMMXG/8HwAEwAI0Bj1bnwAAAABJRU5ErkJggg==",
}

@@ -170,2 +171,6 @@ }

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.
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.
### Plugin options

@@ -175,16 +180,16 @@

| option | type | required | default value | 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 | 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` | 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. |
| `uploadFolder` | `String` | false | n/a | An optional folder name where the uploaded assets will be stored on Cloudinary. |
| `fluidMaxWidth` | `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` | `Int` | false | 200 | The minimum width needed for an image. Used when calculating breakpoints. |
| `createDerived` | `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` | `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` | `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. |
| `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. |
| `enableDefaultTransformations`| `Boolean` | false | n/a | `true` will add the `q_auto` and `f_auto` transformations to images for quality and format optimizations. |
|
| option | type | required | default value | 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 | 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` | 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. |
| `uploadFolder` | `String` | false | n/a | An optional folder name where the uploaded assets will be stored on Cloudinary. |
| `fluidMaxWidth` | `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` | `Int` | false | 200 | The minimum width needed for an image. Used when calculating breakpoints. |
| `createDerived` | `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` | `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` | `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. |
| `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. |
| `enableDefaultTransformations` | `Boolean` | false | false | `true` will add the `q_auto` and `f_auto` transformations to images for quality and format optimizations. |
| `alwaysUseDefaultBase64` | `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. |

@@ -303,20 +308,21 @@ > 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.

| 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. |
| 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. |
### Arguments for `fixed`
| argument | type | default | description |
| -------- | ----- | ------- | ---------------------------------------------------------------------------------------------- |
| `height` | `Int` | `n/a` | The height that the image should display at. If `width` is provided, then `height` is ignored. |
| `width` | `Int` | `400` | The width that the image should display at. |
| 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. |

@@ -335,2 +341,16 @@ ### Arguments for `fluid`

## Running Tests
Run the tests once:
```
yarn workspace gatsby-transformer-cloudinary test
```
Run the tests in watch mode:
```
yarn workspace gatsby-transformer-cloudinary test:watch
```
## Other Resources

@@ -337,0 +357,0 @@

@@ -12,2 +12,3 @@ const cloudinary = require('cloudinary').v2;

publicId,
overwrite,
reporter,

@@ -24,3 +25,2 @@ }) => {

uploadFolder,
overwriteExisting,
useCloudinaryBreakpoints,

@@ -36,3 +36,3 @@ } = getPluginOptions();

folder: uploadFolder,
overwrite: overwriteExisting,
overwrite,
public_id: publicId,

@@ -99,7 +99,12 @@ resource_type: 'auto',

const url = node.absolutePath;
const relativePathWithoutExtension = node.relativePath.replace(/\.[^.]*$/, "");
const relativePathWithoutExtension = node.relativePath.replace(
/\.[^.]*$/,
'',
);
const publicId = relativePathWithoutExtension;
const overwrite = getPluginOptions().overwriteExisting;
const result = await exports.uploadImageToCloudinary({
url,
publicId,
overwrite,
reporter,

@@ -106,0 +111,0 @@ });

@@ -16,3 +16,3 @@ const {

url: 'url',
// overwrite: 'overwrite',
overwrite: 'overwrite',
publicId: 'publicId',

@@ -34,3 +34,2 @@ reporter: {

uploadFolder: 'uploadFolder',
overwriteExisting: false,
createDerived: false,

@@ -44,3 +43,3 @@ breakpointsMaxImages: 234,

test('configures cloudinary with the appropriate plugin options', async () => {
it('configures cloudinary with the appropriate plugin options', async () => {
const cloudinaryConfig = jest.fn();

@@ -63,3 +62,3 @@ cloudinary.config = cloudinaryConfig;

test('does not ask for responsive breakpoints when useCloudinaryBreakpoints is false', async () => {
it('does not ask for responsive breakpoints when useCloudinaryBreakpoints is false', async () => {
const cloudinaryUpload = jest.fn();

@@ -77,3 +76,3 @@ cloudinary.uploader.upload = cloudinaryUpload;

folder: options.uploadFolder,
overwrite: options.overwriteExisting,
overwrite: args.overwrite,
public_id: args.publicId,

@@ -86,10 +85,10 @@ resource_type: 'auto',

test('overwrite only when overwriteExisting is set to true', async () => {
it('overwrites when passed overwrite:true', async () => {
const cloudinaryUpload = jest.fn();
cloudinary.uploader.upload = cloudinaryUpload;
const options = getDefaultOptions({ overwriteExisting: true });
const options = getDefaultOptions();
getPluginOptions.mockReturnValue(options);
const args = getDefaultArgs();
const args = getDefaultArgs({ overwrite: true });
await uploadImageToCloudinary(args);

@@ -108,3 +107,3 @@

test('asks for responsive breakpoints when useCloudinaryBreakpoints is true', async () => {
it('asks for responsive breakpoints when useCloudinaryBreakpoints is true', async () => {
const cloudinaryUpload = jest.fn();

@@ -125,3 +124,3 @@ cloudinary.uploader.upload = cloudinaryUpload;

folder: options.uploadFolder,
overwrite: options.overwriteExisting,
overwrite: args.overwrite,
public_id: args.publicId,

@@ -143,3 +142,3 @@ resource_type: 'auto',

test('returns the result returned from the Cloudinary uploader', async () => {
it('returns the result returned from the Cloudinary uploader', async () => {
const cloudinaryUpload = jest.fn();

@@ -177,2 +176,23 @@ const cloudinaryUploadResult = 'cloudinaryUploadResult';

});
it('passes the overwrite setting from the plugin options', async () => {
const cloudinaryUpload = jest.fn();
cloudinary.uploader.upload = cloudinaryUpload;
const reporter = { info: jest.fn() };
const node = {
relativePath: 'relativePath.jpg',
};
const overwriteExisting = 'overwriteExistingDouble';
getPluginOptions.mockReturnValue({ overwriteExisting });
await uploadImageNodeToCloudinary({ node, reporter });
expect(cloudinaryUpload).toHaveBeenCalledWith(
undefined,
expect.objectContaining({
overwrite: overwriteExisting,
}),
);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc