next-cloudinary 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  ######
14
14
 
15
15
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
16
- [![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors-)
16
+ [![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors-)
17
17
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
18
18
 
19
19
  # Cloudinary Next.js Component
@@ -292,6 +292,8 @@ See the examples above under Overlays to learn more about the available configur
292
292
  <tr>
293
293
  <td align="center"><a href="http://joanleon.dev"><img src="https://avatars.githubusercontent.com/u/1307927?v=4?s=100" width="100px;" alt="Joan León"/><br /><sub><b>Joan León</b></sub></a><br /><a href="https://github.com/colbyfayock/next-cloudinary/commits?author=nucliweb" title="Documentation">📖</a></td>
294
294
  <td align="center"><a href="http://www.timbenniks.dev"><img src="https://avatars.githubusercontent.com/u/121096?v=4?s=100" width="100px;" alt="Tim Benniks"/><br /><sub><b>Tim Benniks</b></sub></a><br /><a href="https://github.com/colbyfayock/next-cloudinary/commits?author=timbenniks" title="Code">💻</a> <a href="https://github.com/colbyfayock/next-cloudinary/commits?author=timbenniks" title="Documentation">📖</a></td>
295
+ <td align="center"><a href="https://github.com/csgochan"><img src="https://avatars.githubusercontent.com/u/116420257?v=4?s=100" width="100px;" alt="csgochan"/><br /><sub><b>csgochan</b></sub></a><br /><a href="https://github.com/colbyfayock/next-cloudinary/commits?author=csgochan" title="Documentation">📖</a></td>
296
+ <td align="center"><a href="https://github.com/codingis4noobs2"><img src="https://avatars.githubusercontent.com/u/87560178?v=4?s=100" width="100px;" alt="codingis4noobs2"/><br /><sub><b>codingis4noobs2</b></sub></a><br /><a href="https://github.com/colbyfayock/next-cloudinary/commits?author=codingis4noobs2" title="Code">💻</a> <a href="https://github.com/colbyfayock/next-cloudinary/commits?author=codingis4noobs2" title="Tests">⚠️</a></td>
295
297
  </tr>
296
298
  </tbody>
297
299
  </table>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-cloudinary",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "main": "dist/next-cloudinary.umd.js",
5
5
  "module": "dist/next-cloudinary.module.js",
6
6
  "umd:main": "dist/next-cloudinary.umd.js",
@@ -13,20 +13,20 @@
13
13
  "test": "jest"
14
14
  },
15
15
  "dependencies": {
16
- "@cloudinary/url-gen": "^1.8.5"
16
+ "@cloudinary/url-gen": "^1.8.6"
17
17
  },
18
18
  "devDependencies": {
19
- "@babel/core": "^7.19.3",
20
- "@babel/preset-env": "^7.19.3",
21
- "@types/jest": "^29.1.1",
22
- "babel-jest": "^29.1.2",
19
+ "@babel/core": "^7.19.6",
20
+ "@babel/preset-env": "^7.19.4",
21
+ "@types/jest": "^29.2.0",
22
+ "babel-jest": "^29.2.2",
23
23
  "dotenv": "^16.0.3",
24
- "jest": "^29.1.2",
25
- "jest-environment-jsdom": "^29.1.2",
24
+ "jest": "^29.2.2",
25
+ "jest-environment-jsdom": "^29.2.2",
26
26
  "microbundle": "^0.15.1"
27
27
  },
28
28
  "peerDependencies": {
29
- "next": "^12.2.5",
29
+ "next": "^12.0.0 || ^13.0.0",
30
30
  "react": "^18.2.0"
31
31
  }
32
32
  }
@@ -1,4 +1,4 @@
1
- import { getPublicId } from "../../src/lib/cloudinary"
1
+ import { constructCloudinaryUrl, getPublicId, createPlaceholderUrl } from '../../src/lib/cloudinary';
2
2
 
3
3
  // Mock console.warn() so we can see when it's called
4
4
  global.console = {
@@ -10,19 +10,120 @@ describe('Cloudinary', () => {
10
10
  afterEach(() => {
11
11
  // Clears the state of console.warn, in case multiple tests want to monitor it
12
12
  jest.restoreAllMocks()
13
- })
13
+ });
14
+
15
+ describe('constructCloudinaryUrl', () => {
16
+ it('should create a Cloudinary URL', () => {
17
+ const cloudName = 'customtestcloud';
18
+ const url = constructCloudinaryUrl({
19
+ options: {
20
+ src: 'turtle',
21
+ width: 100,
22
+ height: 100
23
+ },
24
+ config: {
25
+ cloud: {
26
+ cloudName
27
+ }
28
+ }
29
+ });
30
+ expect(url).toBe(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/f_auto/q_auto/turtle`);
31
+ });
32
+
33
+ it('should create a Cloudinary URL with custom quality and format options', () => {
34
+ const format = 'png';
35
+ const quality = 75;
36
+ const cloudName = 'customtestcloud';
37
+ const url = constructCloudinaryUrl({
38
+ options: {
39
+ src: 'turtle',
40
+ width: 100,
41
+ height: 100,
42
+ format,
43
+ quality
44
+ },
45
+ config: {
46
+ cloud: {
47
+ cloudName
48
+ }
49
+ }
50
+ });
51
+ expect(url).toBe(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/f_${format}/q_${quality}/turtle`);
52
+ });
53
+ });
54
+
14
55
  describe('getPublicId', () => {
15
56
  it('Just returns a non-Cloudinary url', () => {
16
- const src = 'https://google.com'
57
+ const src = 'https://google.com';
17
58
  expect(getPublicId(src)).toBe(src)
18
- })
59
+ });
60
+
61
+ it('Just returns the public ID of a Cloudinary URL 1 folder deep', () => {
62
+ const publicId = 'turtle';
63
+ const src = `https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_960/f_auto/q_auto/v1/${publicId}`;
64
+ expect(getPublicId(src)).toBe(publicId);
65
+ });
66
+
67
+ it('Just returns the public ID of a Cloudinary URL 1 folder deep', () => {
68
+ const publicId = 'images/turtle';
69
+ const src = `https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_960/f_auto/q_auto/v1/${publicId}`;
70
+ expect(getPublicId(src)).toBe(publicId);
71
+ });
72
+
73
+ it('Just returns the public ID of a Cloudinary URL 2 folders deep', () => {
74
+ const publicId = 'app/images/turtle';
75
+ const src = `https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_960/f_auto/q_auto/v1/${publicId}`;
76
+ expect(getPublicId(src)).toBe(publicId);
77
+ });
78
+
19
79
  it('Returns invalid Cloudinary url with a warning', () => {
20
- const src = 'https://res.cloudinary.com'
80
+ const src = 'https://res.cloudinary.com';
21
81
  expect(getPublicId(src)).toBe(src)
22
82
  expect(console.warn)
23
83
  .toBeCalledWith(`Not possible to retrieve the publicUrl from ${src}, make sure it's a valid cloudinary image url.`)
24
- })
25
- it.todo('Returns ID from url with transformations')
26
- it.todo('Returns ID from url without transformations')
27
- })
84
+ });
85
+ });
86
+
87
+ describe('createPlaceholderUrl', () => {
88
+ it('should create a placeholder URL with default settings', () => {
89
+ const cloudName = 'customtestcloud';
90
+ const url = createPlaceholderUrl({
91
+ src: 'turtle',
92
+ config: {
93
+ cloud: {
94
+ cloudName
95
+ }
96
+ }
97
+ });
98
+ expect(url).toBe(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/f_auto/q_1/turtle`);
99
+ });
100
+
101
+ it('should create a placeholder URL in grayscale', () => {
102
+ const cloudName = 'customtestcloud';
103
+ const url = createPlaceholderUrl({
104
+ src: 'turtle',
105
+ placeholder: 'grayscale',
106
+ config: {
107
+ cloud: {
108
+ cloudName
109
+ }
110
+ }
111
+ });
112
+ expect(url).toBe(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/e_grayscale/f_auto/q_1/turtle`);
113
+ });
114
+
115
+ it('should create a placeholder URL with a color', () => {
116
+ const cloudName = 'customtestcloud';
117
+ const url = createPlaceholderUrl({
118
+ src: 'turtle',
119
+ placeholder: 'color:blueviolet',
120
+ config: {
121
+ cloud: {
122
+ cloudName
123
+ }
124
+ }
125
+ });
126
+ expect(url).toBe(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/e_grayscale/e_colorize:60,co_blueviolet/f_auto/q_1/turtle`);
127
+ });
128
+ });
28
129
  })
@@ -48,18 +48,18 @@ describe('Cloudinary Loader', () => {
48
48
  gravity: 'faces',
49
49
  tint: '40:253f8c',
50
50
  underlays: [
51
- {
52
- publicId: 'images/city-skyline',
53
- width: 987,
54
- height: 987,
55
- crop: 'fill'
56
- }
51
+ {
52
+ publicId: 'images/city-skyline',
53
+ width: 987,
54
+ height: 987,
55
+ crop: 'fill'
56
+ }
57
57
  ]
58
58
  };
59
59
 
60
60
  const result = cloudinaryLoader({ ...imageProps, options }, cldOptions);
61
61
 
62
- expect(result).toBe('https://res.cloudinary.com/test-cloud/image/upload/e_background_removal/c_thumb,w_987,h_1481,g_faces/e_tint:40:253f8c/u_images:city-skyline,w_987,h_987,c_fill/fl_layer_apply/f_auto/q_auto/v1/images/woman-headphones')
62
+ expect(result).toBe('https://res.cloudinary.com/test-cloud/image/upload/e_background_removal/c_thumb,w_987,h_1481,g_faces/e_tint:40:253f8c/u_images:city-skyline,w_987,h_987,c_fill/fl_layer_apply,fl_no_overflow/f_auto/q_auto/v1/images/woman-headphones')
63
63
  });
64
64
 
65
65
  it('should return a Cloudinary URL with fetch features', async () => {
@@ -0,0 +1,38 @@
1
+ import { Cloudinary } from '@cloudinary/url-gen';
2
+
3
+ import * as croppingPlugin from '../../src/plugins/cropping';
4
+
5
+ const { plugin } = croppingPlugin
6
+
7
+ const cld = new Cloudinary({
8
+ cloud: {
9
+ cloudName: 'test-cloud-name'
10
+ }
11
+ });
12
+
13
+ const TEST_PUBLIC_ID = 'test-public-id';
14
+
15
+ describe('Cropping plugin', () => {
16
+ it('should apply a crop and gravity to a URL', () => {
17
+ const cldImage = cld.image(TEST_PUBLIC_ID);
18
+ const options = {
19
+ width: 100,
20
+ height: 100,
21
+ crop: 'crop',
22
+ gravity: 'auto'
23
+ };
24
+ plugin({ cldImage, options });
25
+ expect(cldImage.toURL()).toContain(`c_${options.crop},w_${options.width},h_${options.height},g_${options.gravity}/${TEST_PUBLIC_ID}`);
26
+ });
27
+
28
+ it('should apply a gravity of auto by default if not set explicitly', () => {
29
+ const cldImage = cld.image(TEST_PUBLIC_ID);
30
+ const options = {
31
+ width: 100,
32
+ height: 100,
33
+ crop: 'fill'
34
+ };
35
+ plugin({ cldImage, options });
36
+ expect(cldImage.toURL()).toContain(`c_${options.crop},w_${options.width},h_${options.height},g_auto/${TEST_PUBLIC_ID}`);
37
+ });
38
+ });
@@ -0,0 +1,112 @@
1
+ import { Cloudinary } from '@cloudinary/url-gen';
2
+
3
+ import * as overlaysPlugin from '../../src/plugins/overlays';
4
+
5
+ const { plugin, DEFAULT_TEXT_OPTIONS } = overlaysPlugin
6
+
7
+ const cld = new Cloudinary({
8
+ cloud: {
9
+ cloudName: 'test-cloud-name'
10
+ }
11
+ });
12
+
13
+ const TEST_PUBLIC_ID = 'test-public-id';
14
+
15
+ describe('Plugins', () => {
16
+ describe('Text Overlays', () => {
17
+ it('should add a text overlay configured by overlay object', () => {
18
+ const cldImage = cld.image(TEST_PUBLIC_ID);
19
+
20
+ const color = 'white';
21
+ const fontFamily = 'Source Sans Pro';
22
+ const fontSize = 200;
23
+ const fontWeight = 'bold';
24
+ const text = 'Next Cloudinary';
25
+
26
+ const options = {
27
+ overlays: [{
28
+ text: {
29
+ color,
30
+ fontFamily,
31
+ fontSize,
32
+ fontWeight,
33
+ text
34
+ }
35
+ }]
36
+ }
37
+
38
+ plugin({
39
+ cldImage,
40
+ options
41
+ });
42
+
43
+
44
+ expect(cldImage.toURL()).toContain(`l_text:${encodeURIComponent(fontFamily)}_${fontSize}_${fontWeight}:${encodeURIComponent(text)},co_${color}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
45
+ });
46
+
47
+ it('should add a text overlay by overlay object text string', () => {
48
+ const cldImage = cld.image(TEST_PUBLIC_ID);
49
+
50
+ const { color, fontFamily, fontSize, fontWeight } = DEFAULT_TEXT_OPTIONS;
51
+ const text = 'Next Cloudinary';
52
+
53
+ const options = {
54
+ overlays: [{
55
+ text
56
+ }]
57
+ }
58
+
59
+ plugin({
60
+ cldImage,
61
+ options
62
+ });
63
+
64
+ expect(cldImage.toURL()).toContain(`l_text:${encodeURIComponent(fontFamily)}_${fontSize}_${fontWeight}:${encodeURIComponent(text)},co_${color}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
65
+ });
66
+
67
+ it('should add a text overlay by text string', () => {
68
+ const cldImage = cld.image(TEST_PUBLIC_ID);
69
+
70
+ const { color, fontFamily, fontSize, fontWeight } = DEFAULT_TEXT_OPTIONS;
71
+ const text = 'Next Cloudinary';
72
+
73
+ const options = {
74
+ text
75
+ }
76
+
77
+ plugin({
78
+ cldImage,
79
+ options
80
+ });
81
+
82
+ expect(cldImage.toURL()).toContain(`l_text:${encodeURIComponent(fontFamily)}_${fontSize}_${fontWeight}:${encodeURIComponent(text)},co_${color}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
83
+ });
84
+
85
+ it('should add a text overlay by text object', () => {
86
+ const cldImage = cld.image(TEST_PUBLIC_ID);
87
+
88
+ const color = 'white';
89
+ const fontFamily = 'Source Sans Pro';
90
+ const fontSize = 200;
91
+ const fontWeight = 'bold';
92
+ const text = 'Next Cloudinary';
93
+
94
+ const options = {
95
+ text: {
96
+ color,
97
+ fontFamily,
98
+ fontSize,
99
+ fontWeight,
100
+ text
101
+ }
102
+ }
103
+
104
+ plugin({
105
+ cldImage,
106
+ options
107
+ });
108
+
109
+ expect(cldImage.toURL()).toContain(`l_text:${encodeURIComponent(fontFamily)}_${fontSize}_${fontWeight}:${encodeURIComponent(text)},co_${color}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
110
+ });
111
+ });
112
+ });
@@ -18,7 +18,7 @@ describe('Plugins', () => {
18
18
 
19
19
  const cldImage = cld.image(TEST_PUBLIC_ID);
20
20
 
21
- const cldOptions = {
21
+ const options = {
22
22
  rawTransformations: [
23
23
  'e_blur:2000'
24
24
  ]
@@ -26,17 +26,17 @@ describe('Plugins', () => {
26
26
 
27
27
  plugin({
28
28
  cldImage,
29
- cldOptions
29
+ options
30
30
  });
31
31
 
32
- expect(cldImage.toURL()).toContain(`${cldOptions.rawTransformations.join('/')}/${TEST_PUBLIC_ID}`);
32
+ expect(cldImage.toURL()).toContain(`${options.rawTransformations.join('/')}/${TEST_PUBLIC_ID}`);
33
33
  });
34
34
 
35
35
  it('should apply an array of raw transformations to the end of a Cloudinary URL', () => {
36
36
 
37
37
  const cldImage = cld.image(TEST_PUBLIC_ID);
38
38
 
39
- const cldOptions = {
39
+ const options = {
40
40
  rawTransformations: [
41
41
  'e_blur:2000',
42
42
  'e_tint:100:0000FF:0p:FF1493:100p'
@@ -45,10 +45,10 @@ describe('Plugins', () => {
45
45
 
46
46
  plugin({
47
47
  cldImage,
48
- cldOptions
48
+ options
49
49
  });
50
50
 
51
- expect(cldImage.toURL()).toContain(`${cldOptions.rawTransformations.join('/')}/${TEST_PUBLIC_ID}`);
51
+ expect(cldImage.toURL()).toContain(`${options.rawTransformations.join('/')}/${TEST_PUBLIC_ID}`);
52
52
  })
53
53
  });
54
54
  });
@@ -0,0 +1,62 @@
1
+ import { Cloudinary } from '@cloudinary/url-gen';
2
+
3
+ import * as underlaysPlugin from '../../src/plugins/underlays';
4
+
5
+ const { plugin, DEFAULT_TEXT_OPTIONS } = underlaysPlugin
6
+
7
+ const cld = new Cloudinary({
8
+ cloud: {
9
+ cloudName: 'test-cloud-name'
10
+ }
11
+ });
12
+
13
+ const TEST_PUBLIC_ID = 'test-public-id';
14
+
15
+ describe('Plugins', () => {
16
+ describe('Underlays', () => {
17
+ it('should add an underlay configured by object', () => {
18
+ const cldImage = cld.image(TEST_PUBLIC_ID);
19
+
20
+ const publicId = 'images/galaxy';
21
+ const width = 1920;
22
+ const height = 1200;
23
+ const crop = 'fill';
24
+
25
+ const options = {
26
+ underlays: [{
27
+ publicId,
28
+ width,
29
+ height,
30
+ crop,
31
+ }]
32
+ }
33
+
34
+ plugin({
35
+ cldImage,
36
+ options
37
+ });
38
+
39
+ expect(cldImage.toURL()).toContain(`u_${publicId.replace(/\//g, ':')},w_${width},h_${height},c_${crop}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
40
+ });
41
+
42
+ it('should add an underlay by string', () => {
43
+ const cldImage = cld.image(TEST_PUBLIC_ID);
44
+
45
+ const publicId = 'images/galaxy';
46
+ const width = 1920;
47
+ const height = 1200;
48
+ const crop = 'fill';
49
+
50
+ const options = {
51
+ underlay: publicId
52
+ }
53
+
54
+ plugin({
55
+ cldImage,
56
+ options
57
+ });
58
+
59
+ expect(cldImage.toURL()).toContain(`u_${publicId.replace(/\//g, ':')},c_${crop}/fl_layer_apply,fl_no_overflow/${TEST_PUBLIC_ID}`);
60
+ });
61
+ });
62
+ });