drupal-image-style-generator 0.2.0 → 1.0.1

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.
Files changed (27) hide show
  1. package/.idea/drupal-image-style-generator.iml +1 -0
  2. package/index.js +165 -16
  3. package/package.json +5 -3
  4. package/test/data/crop/config/sync/core.extension.yml +107 -0
  5. package/test/data/crop/config/sync/crop.type.aspect_165x266.yml +11 -0
  6. package/test/data/crop/config/sync/crop.type.aspect_635x424.yml +11 -0
  7. package/test/data/crop/config/sync/image.style.mc_1150_aspect_165x266.yml +26 -0
  8. package/test/data/crop/config/sync/image.style.mc_1320_aspect_165x266.yml +26 -0
  9. package/test/data/crop/config/sync/image.style.mc_290_aspect_635x424.yml +26 -0
  10. package/test/data/crop/config/sync/image.style.mc_575_aspect_165x266.yml +26 -0
  11. package/test/data/crop/config/sync/image.style.mc_580_aspect_635x424.yml +26 -0
  12. package/test/data/crop/config/sync/image.style.mc_660_aspect_165x266.yml +26 -0
  13. package/test/data/crop/config/sync/responsive_image.styles.frontpage_hero.yml +42 -0
  14. package/test/data/crop/web/themes/crop/crop.breakpoints.yml +35 -0
  15. package/test/data/insufficient_modules/config/sync/core.extension.yml +106 -0
  16. package/test/data/insufficient_modules/config/sync/image.style.mc_1150_aspect_165x266.yml +26 -0
  17. package/test/data/insufficient_modules/config/sync/image.style.mc_1320_aspect_635x424.yml +26 -0
  18. package/test/data/insufficient_modules/config/sync/image.style.mc_575_aspect_165x266.yml +26 -0
  19. package/test/data/insufficient_modules/config/sync/image.style.mc_660_aspect_635x424.yml +26 -0
  20. package/test/data/insufficient_modules/config/sync/image.style.sc_1150x920.yml +17 -0
  21. package/test/data/insufficient_modules/config/sync/image.style.sc_1320x871.yml +17 -0
  22. package/test/data/insufficient_modules/config/sync/image.style.sc_575x460.yml +17 -0
  23. package/test/data/insufficient_modules/config/sync/image.style.sc_660x436.yml +17 -0
  24. package/test/data/insufficient_modules/web/themes/no_breakpoints_yml/.gitkeep +0 -0
  25. package/test/data/insufficient_modules/web/themes/no_crop/no_crop.breakpoints.yml +25 -0
  26. package/test/data/insufficient_modules/web/themes/no_focal/no_focal.breakpoints.yml +24 -0
  27. package/test/test.js +88 -0
@@ -2,6 +2,7 @@
2
2
  <module type="WEB_MODULE" version="4">
3
3
  <component name="NewModuleRootManager">
4
4
  <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
5
6
  <excludeFolder url="file://$MODULE_DIR$/temp" />
6
7
  <excludeFolder url="file://$MODULE_DIR$/.tmp" />
7
8
  <excludeFolder url="file://$MODULE_DIR$/tmp" />
package/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  const REQUIRED_OPTIONS = ['themePath', 'themeName', 'syncFolder'];
2
2
  const helpers = require('./src/helpers');
3
3
  const fs = require("fs");
4
+ const path = require("path");
4
5
  const yaml = require("js-yaml");
5
6
  const log = require("fancy-log");
6
7
  const {v4} = require("uuid");
7
8
 
8
9
  /**
9
10
  * @param {{themePath: string, themeName: string, syncFolder: string, gridSize?: number, convertTo?: string}} options
11
+ * @returns {boolean}
10
12
  */
11
13
  module.exports = function (options) {
12
14
 
@@ -20,15 +22,23 @@ module.exports = function (options) {
20
22
  const themePath = options.themePath;
21
23
  const themeName = options.themeName;
22
24
  const syncFolder = options.syncFolder;
23
- const IMAGE_STYLE_GRID_SIZE = options.gridSize || 0; // this means, every style will be rounded up to the next divisible number of this
25
+ const IMAGE_STYLE_GRID_SIZE = options.gridSize || 1; // this means, every style will be rounded up to the next divisible number of this
24
26
 
25
27
  const { v4 } = require("uuid");
26
28
  const yaml = require('js-yaml');
27
29
  const fs = require('fs');
28
30
  const log = require('fancy-log');
29
- const breakpointsFile = themePath + '/' + themeName + '.breakpoints.yml';
30
- const imageStyles = {};
31
+ const modulesFile = './' + path.normalize(syncFolder + '/core.extension.yml');
32
+ const breakpointsFile = './' + path.normalize(themePath + '/' + themeName + '.breakpoints.yml');
33
+ const imageStyles = {}; // contains the single actual style definitions
34
+ const cropTypes = {}; // contains potentially required crop types
35
+ const usedCropTypes = {};
31
36
  const usedStyleConfigs = {};
37
+ const requiredModules = {}; // contains additional custom modules, that need to be enabled before generation
38
+
39
+ if (!fs.existsSync(modulesFile)) {
40
+ throw new Error(`Could not find ${modulesFile} - is the sync folder configured correctly?`);
41
+ }
32
42
 
33
43
  if (!fs.existsSync(breakpointsFile)) {
34
44
  throw new Error(`Your configured theme has no ${breakpointsFile} theme breakpoints file. Please read the documentation and double check your options.`);
@@ -58,11 +68,18 @@ module.exports = function (options) {
58
68
  imageStyles[styleId].image_style_mappings = [];
59
69
 
60
70
  imageStyles[styleId].widths = {};
71
+ imageStyles[styleId].cropTypes = {};
61
72
  }
62
73
 
63
74
  // adjust the aspect ratio, if a new is set
64
75
  if (bp.imageStyles[styleId].aspectRatio) {
65
76
  imageStyles[styleId].aspectRatio = bp.imageStyles[styleId].aspectRatio;
77
+
78
+ if (imageStyles[styleId].manual_crop) {
79
+ requiredModules['crop'] = true;
80
+ } else {
81
+ requiredModules['focal_point'] = true;
82
+ }
66
83
  }
67
84
 
68
85
  let width = bp.imageStyles[styleId].width || parsedWidth;
@@ -73,16 +90,21 @@ module.exports = function (options) {
73
90
  // set the new width
74
91
  imageStyles[styleId].width = width;
75
92
  imageStyles[styleId].widths[bpName] = imageStyles[styleId].width;
93
+
94
+ if (imageStyles[styleId].manual_crop) {
95
+ imageStyles[styleId].cropTypes[bpName] = 'aspect_' + imageStyles[styleId].aspectRatio.replace(':', 'x');
96
+ }
97
+
76
98
  });
77
99
  }
78
100
 
79
101
  // loop over all the image styles
80
102
  Object.keys(imageStyles).forEach((styleId) => {
81
103
  bp.multipliers.forEach((multiplier) => {
82
-
83
104
  const multiplyNum = parseFloat(multiplier);
84
105
  const styleWidth = (imageStyles[styleId].widths[bpName] || parsedWidth) * multiplyNum;
85
106
  const aspectRatio = imageStyles[styleId].aspectRatio;
107
+ const manualCrop = imageStyles[styleId].manual_crop;
86
108
 
87
109
  const uniqueId = v4();
88
110
 
@@ -92,9 +114,97 @@ module.exports = function (options) {
92
114
  let styleFileName;
93
115
  let styleFilePath;
94
116
  let styleYml;
95
- let hasChanges = false;
117
+ let hasChanges;
118
+
119
+ if (manualCrop) {
120
+ const cropType = imageStyles[styleId].cropTypes[bpName];
121
+ const cropDimensions = cropType.split('_')[1].split('x').map((num) => {
122
+ return parseInt(num, 10);
123
+ });
124
+ const aspectRatio = cropDimensions[1] / cropDimensions[0];
125
+
126
+ if (!cropTypes[cropType]) {
127
+ cropTypes[cropType] = {
128
+ label: `Aspect Ratio ${cropDimensions[0]}:${cropDimensions[1]}`,
129
+ aspect_ratio: `${cropDimensions[0]}:${cropDimensions[1]}`,
130
+ soft_limit_width: styleWidth,
131
+ soft_limit_height: Math.round(styleWidth * aspectRatio)
132
+ }
133
+ } else {
134
+ cropTypes[cropType].soft_limit_width = Math.max(cropTypes[cropType].soft_limit_width, styleWidth);
135
+ cropTypes[cropType].soft_limit_height = Math.round(Math.max(cropTypes[cropType].soft_limit_height, styleWidth * aspectRatio));
136
+ }
137
+
138
+ styleLabel = `Manual Crop and Scale ${styleWidth}`;
139
+ concreteStyleId = `mc_${styleWidth}_${cropType}`;
140
+ styleFileName = `image.style.${concreteStyleId}.yml`;
141
+ styleFilePath = `${syncFolder}/${styleFileName}`;
142
+
143
+ styleYml = fs.existsSync(styleFilePath) ?
144
+ yaml.load(fs.readFileSync(styleFilePath, 'utf8')) :
145
+ {
146
+ uuid: uniqueId,
147
+ langcode: 'de',
148
+ status: true,
149
+ dependencies: {
150
+ config: ['crop.type.' + cropType],
151
+ module: ['crop']
152
+ },
153
+ name: concreteStyleId,
154
+ label: styleLabel,
155
+ effects: {}
156
+ }
157
+ ;
158
+
159
+ const convertEffect = helpers.getEffectByTypeId('image_convert', styleYml.effects);
160
+ const cropEffect = helpers.getEffectByTypeId('crop_crop', styleYml.effects);
161
+ const scaleEffect = helpers.getEffectByTypeId('image_scale', styleYml.effects);
162
+
163
+ hasChanges = !scaleEffect || scaleEffect.data.width !== styleWidth ||
164
+ !cropEffect || cropEffect.data.crop_type !== cropType ||
165
+ (convertTo && (!convertEffect || (convertEffect && convertEffect.data.extension !== convertTo))) || (!convertTo && convertEffect)
166
+ ;
167
+
168
+ if (hasChanges) {
169
+ styleYml.effects = {};
170
+
171
+ if (convertTo) {
172
+ const convertEffectId = v4();
173
+ styleYml.effects[convertEffectId] = {
174
+ uuid: convertEffectId,
175
+ id: 'image_convert',
176
+ weight: -1,
177
+ data: {
178
+ extension: convertTo
179
+ }
180
+ }
181
+ }
182
+
183
+ const effectId = v4();
184
+ styleYml.effects[effectId] = {
185
+ uuid: effectId,
186
+ id: 'crop_crop',
187
+ weight: 1,
188
+ data: {
189
+ crop_type: cropType,
190
+ automatic_crop_provider: null
191
+ }
192
+ };
193
+
194
+ const scaleEffectId = v4();
195
+ styleYml.effects[scaleEffectId] = {
196
+ uuid: scaleEffectId,
197
+ id: 'image_scale',
198
+ weight: 2,
199
+ data: {
200
+ width: styleWidth,
201
+ height: null,
202
+ upscale: false
203
+ }
204
+ };
205
+ }
96
206
 
97
- if (aspectRatio) {
207
+ } else if (aspectRatio) {
98
208
  styleHeight = imageStyles[styleId].height ? imageStyles[styleId].height * multiplyNum : Math.round(styleWidth * aspectRatio);
99
209
 
100
210
  // generate the filename
@@ -103,8 +213,6 @@ module.exports = function (options) {
103
213
  styleFileName = `image.style.${concreteStyleId}.yml`;
104
214
  styleFilePath = `${syncFolder}/${styleFileName}`;
105
215
 
106
- usedStyleConfigs[styleFileName] = true;
107
-
108
216
  styleYml = fs.existsSync(styleFilePath) ?
109
217
  yaml.load(fs.readFileSync(styleFilePath, 'utf8')) :
110
218
  {
@@ -112,7 +220,7 @@ module.exports = function (options) {
112
220
  langcode: 'de',
113
221
  status: true,
114
222
  dependencies: {
115
- module: ['focal_point'] // @todo - parse the module config and check, if focal point is enabled
223
+ module: ['focal_point']
116
224
  },
117
225
  name: concreteStyleId,
118
226
  label: styleLabel,
@@ -123,7 +231,7 @@ module.exports = function (options) {
123
231
  const scaleEffect = helpers.getEffectByTypeId('focal_point_scale_and_crop', styleYml.effects);
124
232
  const convertEffect = helpers.getEffectByTypeId('image_convert', styleYml.effects);
125
233
 
126
- const hasChanges = !scaleEffect || scaleEffect.data.width !== styleWidth || scaleEffect.data.height !== styleHeight ||
234
+ hasChanges = !scaleEffect || scaleEffect.data.width !== styleWidth || scaleEffect.data.height !== styleHeight ||
127
235
  (convertTo && (!convertEffect || (convertEffect && convertEffect.data.extension !== convertTo))) || (!convertTo && convertEffect)
128
236
  ;
129
237
 
@@ -162,8 +270,6 @@ module.exports = function (options) {
162
270
  styleFileName = `image.style.${concreteStyleId}.yml`;
163
271
  styleFilePath = `${syncFolder}/${styleFileName}`;
164
272
 
165
- usedStyleConfigs[styleFileName] = true;
166
-
167
273
  styleYml = fs.existsSync(styleFilePath) ?
168
274
  yaml.load(fs.readFileSync(styleFilePath, 'utf8')) :
169
275
  {
@@ -180,7 +286,7 @@ module.exports = function (options) {
180
286
  const scaleEffect = helpers.getEffectByTypeId('image_scale', styleYml.effects);
181
287
  const convertEffect = helpers.getEffectByTypeId('image_convert', styleYml.effects);
182
288
 
183
- const hasChanges = !scaleEffect || scaleEffect.data.width !== styleWidth ||
289
+ hasChanges = !scaleEffect || scaleEffect.data.width !== styleWidth ||
184
290
  (convertTo && (!convertEffect || (convertEffect && convertEffect.data.extension !== convertTo))) || (!convertTo && convertEffect)
185
291
  ;
186
292
 
@@ -211,9 +317,10 @@ module.exports = function (options) {
211
317
  }
212
318
  };
213
319
  }
214
-
215
320
  }
216
321
 
322
+ usedStyleConfigs[styleFileName] = true;
323
+
217
324
  if (hasChanges) {
218
325
  // write the file
219
326
  fs.writeFileSync(styleFilePath, yaml.dump(styleYml));
@@ -233,6 +340,43 @@ module.exports = function (options) {
233
340
  });
234
341
  });
235
342
 
343
+ // test the required modules
344
+ const modulesConf = yaml.load(fs.readFileSync(modulesFile, 'utf8')).module;
345
+ Object.keys(requiredModules).forEach((moduleName) => {
346
+ if (requiredModules[moduleName] && modulesConf[moduleName] !== 0) {
347
+ throw new Error(`Your drupal installation is missing the ${moduleName} module. Please enable it before generating the image styles.`)
348
+ }
349
+ });
350
+
351
+ // write the actual crop types files
352
+ Object.keys(cropTypes).forEach((cropTypeId) => {
353
+ const type = cropTypes[cropTypeId];
354
+ const cropTypeFileName = `crop.type.${cropTypeId}.yml`;
355
+ const cropTypePath = `${syncFolder}/${cropTypeFileName}`;
356
+ const uniqueId = v4();
357
+ const cropTypeConfig = fs.existsSync(cropTypePath) ?
358
+ yaml.load(fs.readFileSync(cropTypePath, 'utf8')) :
359
+ {
360
+ uuid: uniqueId,
361
+ langcode: 'de',
362
+ status: true,
363
+ dependencies: {},
364
+ label: type.label,
365
+ id: cropTypeId,
366
+ description: type.label,
367
+ soft_limit_width: type.soft_limit_width,
368
+ soft_limit_height: type.soft_limit_height,
369
+ hard_limit_width: null,
370
+ hard_limit_height: null
371
+ }
372
+ ;
373
+
374
+ usedCropTypes[cropTypeFileName] = true;
375
+
376
+ fs.writeFileSync(cropTypePath, yaml.dump(cropTypeConfig));
377
+ log('Created crop type ' + cropTypePath);
378
+ });
379
+
236
380
  // write the actual responsive config files
237
381
  Object.keys(imageStyles).forEach((styleId) => {
238
382
  const responsiveImagePath = `${syncFolder}/responsive_image.styles.${styleId}.yml`;
@@ -271,11 +415,16 @@ module.exports = function (options) {
271
415
  const configFiles = fs.readdirSync(syncFolder);
272
416
 
273
417
  configFiles.forEach(file => {
274
- if ((file.indexOf('image.style.sc_') === 0 || file.indexOf('image.style.s_') === 0) && !usedStyleConfigs[file]) {
418
+ if (
419
+ ((file.indexOf('image.style.sc_') === 0 || file.indexOf('image.style.s_') === 0 || file.indexOf('image.style.mc_') === 0) && !usedStyleConfigs[file]) ||
420
+ (file.indexOf('crop.type.aspect_') === 0 && !usedCropTypes[file])
421
+ ) {
275
422
  fs.rmSync(`${syncFolder}/${file}`);
276
423
  console.log('%o is unused and was removed.', file);
277
424
  }
278
425
  });
279
426
 
280
427
  log('Generated %d image styles for %d responsive sizes', Object.keys(usedStyleConfigs).length, Object.keys(imageStyles).length);
281
- }
428
+
429
+ return true;
430
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "drupal-image-style-generator",
3
- "version": "0.2.0",
3
+ "version": "1.0.1",
4
4
  "description": "A helper tool to automatically geenrate drupal 8+ responsive image styles",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "mocha --reporter spec"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
@@ -24,7 +24,9 @@
24
24
  "grunt-bump": "^0.8.0",
25
25
  "grunt-prompt": "^1.3.3",
26
26
  "grunt-shell": "^4.0.0",
27
- "semver": "^7.3.8"
27
+ "semver": "^7.3.8",
28
+ "chai": "^4.3.7",
29
+ "mocha": "^10.2.0"
28
30
  },
29
31
  "dependencies": {
30
32
  "fancy-log": "^2.0.0",
@@ -0,0 +1,107 @@
1
+ _core:
2
+ default_config_hash: R4IF-ClDHXxblLcG0L7MgsLvfBIMAvi_skumNFQwkDc
3
+ langcode: de
4
+ module:
5
+ admin_toolbar: 0
6
+ admin_toolbar_tools: 0
7
+ automated_cron: 0
8
+ big_pipe: 0
9
+ block: 0
10
+ block_class: 0
11
+ block_content: 0
12
+ breakpoint: 0
13
+ ckeditor5: 0
14
+ classier_paragraphs: 0
15
+ config: 0
16
+ crop: 0
17
+ config_partial_export: 0
18
+ config_translation: 0
19
+ content_sync: 0
20
+ contextual: 0
21
+ cookies: 0
22
+ datetime: 0
23
+ dblog: 0
24
+ dynamic_page_cache: 0
25
+ editor: 0
26
+ editor_advanced_link: 0
27
+ entity_browser: 0
28
+ entity_browser_entity_form: 0
29
+ entity_reference_display: 0
30
+ entity_reference_revisions: 0
31
+ field: 0
32
+ field_ui: 0
33
+ file: 0
34
+ filter: 0
35
+ fpa: 0
36
+ help: 0
37
+ history: 0
38
+ honeypot: 0
39
+ image: 0
40
+ image_widget_crop: 0
41
+ imageapi_optimize: 0
42
+ imageapi_optimize_resmushit: 0
43
+ inline_entity_form: 0
44
+ jquery_ui: 0
45
+ jquery_ui_draggable: 0
46
+ language: 0
47
+ link: 0
48
+ linkit: 0
49
+ linkit_media_library: 0
50
+ locale: 0
51
+ matomo: 0
52
+ media: 0
53
+ media_entity_browser: 0
54
+ media_entity_browser_media_library: 0
55
+ media_library: 0
56
+ menu_link_attributes: 0
57
+ menu_link_content: 0
58
+ menu_ui: 0
59
+ metatag: 0
60
+ metatag_verification: 0
61
+ metatag_views: 0
62
+ mysql: 0
63
+ node: 0
64
+ obfuscate_email: 0
65
+ options: 0
66
+ page_cache: 0
67
+ paragraphs_edit: 0
68
+ path: 0
69
+ path_alias: 0
70
+ quicklink: 0
71
+ rabbit_hole: 0
72
+ redirect: 0
73
+ responsive_image: 0
74
+ rh_media: 0
75
+ rh_node: 0
76
+ rh_taxonomy: 0
77
+ serialization: 0
78
+ shortcut: 0
79
+ site_image: 0
80
+ site_paragraphs: 0
81
+ site_video: 0
82
+ svg_image: 0
83
+ svg_image_responsive: 0
84
+ symfony_mailer: 0
85
+ system: 0
86
+ taxonomy: 0
87
+ text: 0
88
+ token: 0
89
+ toolbar: 0
90
+ transliterate_filenames: 0
91
+ twig_tweak: 0
92
+ update: 0
93
+ user: 0
94
+ viewfield: 0
95
+ views_ui: 0
96
+ webform: 0
97
+ webform_ui: 0
98
+ pathauto: 1
99
+ views: 10
100
+ cookies_matomo: 11
101
+ paragraphs: 11
102
+ tvi: 15
103
+ distro: 1000
104
+ theme:
105
+ claro: 0
106
+ flowy: 0
107
+ profile: distro
@@ -0,0 +1,11 @@
1
+ uuid: bc4efe45-64e9-4a95-8a82-44d5569741dd
2
+ langcode: de
3
+ status: true
4
+ dependencies: {}
5
+ label: Aspect Ratio 165:266
6
+ id: aspect_165x266
7
+ description: Aspect Ratio 165:266
8
+ soft_limit_width: 1320
9
+ soft_limit_height: 2128
10
+ hard_limit_width: null
11
+ hard_limit_height: null
@@ -0,0 +1,11 @@
1
+ uuid: d56db1d9-6540-4bc8-8a4d-212e8551e456
2
+ langcode: de
3
+ status: true
4
+ dependencies: {}
5
+ label: Aspect Ratio 635:424
6
+ id: aspect_635x424
7
+ description: Aspect Ratio 635:424
8
+ soft_limit_width: 580
9
+ soft_limit_height: 387
10
+ hard_limit_width: null
11
+ hard_limit_height: null
@@ -0,0 +1,26 @@
1
+ uuid: 94309bf7-a6c9-491b-9d02-9f5a553abd9e
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_1150_aspect_165x266
10
+ label: Manual Crop and Scale 1150
11
+ effects:
12
+ fd420ed1-c9ee-4353-b84e-732f5ba251ce:
13
+ uuid: fd420ed1-c9ee-4353-b84e-732f5ba251ce
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ 34e84fce-08d8-4c6f-a014-8076b1d5d6bc:
20
+ uuid: 34e84fce-08d8-4c6f-a014-8076b1d5d6bc
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 1150
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: fb4ebd61-4255-49fa-b915-fc60ddff2e41
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_1320_aspect_165x266
10
+ label: Manual Crop and Scale 1320
11
+ effects:
12
+ 9ad2dc73-5ece-412e-9f7e-b7b3e558ae52:
13
+ uuid: 9ad2dc73-5ece-412e-9f7e-b7b3e558ae52
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ 52c07d14-a4ac-497a-b08e-eb15706b2603:
20
+ uuid: 52c07d14-a4ac-497a-b08e-eb15706b2603
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 1320
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: bfe921e0-c819-4b21-b0e2-6d930e465968
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_635x424
7
+ module:
8
+ - crop
9
+ name: mc_290_aspect_635x424
10
+ label: Manual Crop and Scale 290
11
+ effects:
12
+ c2e88340-30e0-417e-b7a1-e038df8964bf:
13
+ uuid: c2e88340-30e0-417e-b7a1-e038df8964bf
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_635x424
18
+ automatic_crop_provider: null
19
+ dd58c164-bd4a-4279-9f2d-c5d15d6caf9a:
20
+ uuid: dd58c164-bd4a-4279-9f2d-c5d15d6caf9a
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 290
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: 5cb45f65-ff19-4258-b01d-700fc3419bb2
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_575_aspect_165x266
10
+ label: Manual Crop and Scale 575
11
+ effects:
12
+ 5b2132b5-ad86-4e85-9de9-493f51077829:
13
+ uuid: 5b2132b5-ad86-4e85-9de9-493f51077829
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ 23efa495-ce42-4a81-bcc5-00b59c5f6432:
20
+ uuid: 23efa495-ce42-4a81-bcc5-00b59c5f6432
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 575
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: 43aca4e3-ca35-4494-947a-b768a20aebe8
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_635x424
7
+ module:
8
+ - crop
9
+ name: mc_580_aspect_635x424
10
+ label: Manual Crop and Scale 580
11
+ effects:
12
+ a3fdf392-5939-47a6-ac3c-69b468c07db8:
13
+ uuid: a3fdf392-5939-47a6-ac3c-69b468c07db8
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_635x424
18
+ automatic_crop_provider: null
19
+ 4db37637-c7e9-47c0-bbf1-3adc22379da3:
20
+ uuid: 4db37637-c7e9-47c0-bbf1-3adc22379da3
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 580
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: 14c92cbb-0118-40fa-b5e4-f81b7e841b46
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_660_aspect_165x266
10
+ label: Manual Crop and Scale 660
11
+ effects:
12
+ 44162a11-4f9a-404d-94ff-31ea41095194:
13
+ uuid: 44162a11-4f9a-404d-94ff-31ea41095194
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ 12ee83a5-2763-47ee-941e-f36916f15bc9:
20
+ uuid: 12ee83a5-2763-47ee-941e-f36916f15bc9
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 660
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,42 @@
1
+ uuid: 94e0beb1-7f8a-4b34-ad42-1c840f937c77
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - image.style.mc_575_aspect_165x266
7
+ - image.style.mc_1150_aspect_165x266
8
+ - image.style.mc_660_aspect_165x266
9
+ - image.style.mc_1320_aspect_165x266
10
+ - image.style.mc_290_aspect_635x424
11
+ - image.style.mc_580_aspect_635x424
12
+ theme:
13
+ - crop
14
+ id: frontpage_hero
15
+ label: Frontage Hero
16
+ image_style_mappings:
17
+ - breakpoint_id: default.xs
18
+ multiplier: 1x
19
+ image_mapping_type: image_style
20
+ image_mapping: mc_575_aspect_165x266
21
+ - breakpoint_id: default.xs
22
+ multiplier: 2x
23
+ image_mapping_type: image_style
24
+ image_mapping: mc_1150_aspect_165x266
25
+ - breakpoint_id: default.md
26
+ multiplier: 1x
27
+ image_mapping_type: image_style
28
+ image_mapping: mc_660_aspect_165x266
29
+ - breakpoint_id: default.md
30
+ multiplier: 2x
31
+ image_mapping_type: image_style
32
+ image_mapping: mc_1320_aspect_165x266
33
+ - breakpoint_id: default.lg
34
+ multiplier: 1x
35
+ image_mapping_type: image_style
36
+ image_mapping: mc_290_aspect_635x424
37
+ - breakpoint_id: default.lg
38
+ multiplier: 2x
39
+ image_mapping_type: image_style
40
+ image_mapping: mc_580_aspect_635x424
41
+ breakpoint_group: crop
42
+ fallback_image_style: mc_575_aspect_165x266
@@ -0,0 +1,35 @@
1
+ default.xs: # mobile phones
2
+ label: XS
3
+ mediaQuery: '(max-width: 575px)'
4
+ weight: 0
5
+ multipliers:
6
+ - 1x
7
+ - 2x
8
+ imageStyles:
9
+ frontpage_hero:
10
+ label: 'Frontage Hero'
11
+ manual_crop: true
12
+ aspectRatio: '165:266'
13
+ width: 575
14
+ default.md: # large tablets
15
+ label: MD
16
+ mediaQuery: '(min-width: 576px) and (max-width: 991px)'
17
+ weight: 1
18
+ multipliers:
19
+ - 1x
20
+ - 2x
21
+ imageStyles:
22
+ # assuming, that the breakpoint is at md
23
+ frontpage_hero:
24
+ width: 660
25
+ default.lg: # desktop
26
+ label: LG
27
+ mediaQuery: '(min-width: 992px) and (max-width: 1199px)'
28
+ weight: 3
29
+ multipliers:
30
+ - 1x
31
+ - 2x
32
+ imageStyles:
33
+ frontpage_hero:
34
+ width: 290
35
+ aspectRatio: '635:424'
@@ -0,0 +1,106 @@
1
+ _core:
2
+ default_config_hash: R4IF-ClDHXxblLcG0L7MgsLvfBIMAvi_skumNFQwkDc
3
+ langcode: de
4
+ module:
5
+ admin_toolbar: 0
6
+ admin_toolbar_tools: 0
7
+ automated_cron: 0
8
+ big_pipe: 0
9
+ block: 0
10
+ block_class: 0
11
+ block_content: 0
12
+ breakpoint: 0
13
+ ckeditor5: 0
14
+ classier_paragraphs: 0
15
+ config: 0
16
+ config_partial_export: 0
17
+ config_translation: 0
18
+ content_sync: 0
19
+ contextual: 0
20
+ cookies: 0
21
+ datetime: 0
22
+ dblog: 0
23
+ dynamic_page_cache: 0
24
+ editor: 0
25
+ editor_advanced_link: 0
26
+ entity_browser: 0
27
+ entity_browser_entity_form: 0
28
+ entity_reference_display: 0
29
+ entity_reference_revisions: 0
30
+ field: 0
31
+ field_ui: 0
32
+ file: 0
33
+ filter: 0
34
+ fpa: 0
35
+ help: 0
36
+ history: 0
37
+ honeypot: 0
38
+ image: 0
39
+ image_widget_crop: 0
40
+ imageapi_optimize: 0
41
+ imageapi_optimize_resmushit: 0
42
+ inline_entity_form: 0
43
+ jquery_ui: 0
44
+ jquery_ui_draggable: 0
45
+ language: 0
46
+ link: 0
47
+ linkit: 0
48
+ linkit_media_library: 0
49
+ locale: 0
50
+ matomo: 0
51
+ media: 0
52
+ media_entity_browser: 0
53
+ media_entity_browser_media_library: 0
54
+ media_library: 0
55
+ menu_link_attributes: 0
56
+ menu_link_content: 0
57
+ menu_ui: 0
58
+ metatag: 0
59
+ metatag_verification: 0
60
+ metatag_views: 0
61
+ mysql: 0
62
+ node: 0
63
+ obfuscate_email: 0
64
+ options: 0
65
+ page_cache: 0
66
+ paragraphs_edit: 0
67
+ path: 0
68
+ path_alias: 0
69
+ quicklink: 0
70
+ rabbit_hole: 0
71
+ redirect: 0
72
+ responsive_image: 0
73
+ rh_media: 0
74
+ rh_node: 0
75
+ rh_taxonomy: 0
76
+ serialization: 0
77
+ shortcut: 0
78
+ site_image: 0
79
+ site_paragraphs: 0
80
+ site_video: 0
81
+ svg_image: 0
82
+ svg_image_responsive: 0
83
+ symfony_mailer: 0
84
+ system: 0
85
+ taxonomy: 0
86
+ text: 0
87
+ token: 0
88
+ toolbar: 0
89
+ transliterate_filenames: 0
90
+ twig_tweak: 0
91
+ update: 0
92
+ user: 0
93
+ viewfield: 0
94
+ views_ui: 0
95
+ webform: 0
96
+ webform_ui: 0
97
+ pathauto: 1
98
+ views: 10
99
+ cookies_matomo: 11
100
+ paragraphs: 11
101
+ tvi: 15
102
+ distro: 1000
103
+ theme:
104
+ claro: 0
105
+ flowy: 0
106
+ profile: distro
@@ -0,0 +1,26 @@
1
+ uuid: 2095964a-91b2-4f6d-9168-47d176a6fd78
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_1150_aspect_165x266
10
+ label: Manual Crop and Scale 1150
11
+ effects:
12
+ dedf03d7-5674-4c45-8fca-5978ef50f388:
13
+ uuid: dedf03d7-5674-4c45-8fca-5978ef50f388
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ c01f0572-ea6f-4cbd-83a9-af79b4d6ef34:
20
+ uuid: c01f0572-ea6f-4cbd-83a9-af79b4d6ef34
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 1150
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: 94a5ae41-fd00-4cdd-9e41-c55cf708f80c
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_635x424
7
+ module:
8
+ - crop
9
+ name: mc_1320_aspect_635x424
10
+ label: Manual Crop and Scale 1320
11
+ effects:
12
+ 68e5495a-7f52-41a2-b1cf-2bbd7d77da99:
13
+ uuid: 68e5495a-7f52-41a2-b1cf-2bbd7d77da99
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_635x424
18
+ automatic_crop_provider: null
19
+ 63592331-ee1b-4834-b569-8f6920ea7b8a:
20
+ uuid: 63592331-ee1b-4834-b569-8f6920ea7b8a
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 1320
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: ecf507cb-a68b-4ae6-8d13-e87ce4207f17
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_165x266
7
+ module:
8
+ - crop
9
+ name: mc_575_aspect_165x266
10
+ label: Manual Crop and Scale 575
11
+ effects:
12
+ 2dff9f5a-15fc-4163-8394-73cdbff3c553:
13
+ uuid: 2dff9f5a-15fc-4163-8394-73cdbff3c553
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_165x266
18
+ automatic_crop_provider: null
19
+ 07e8f6a8-693c-4d62-b457-d6942290a631:
20
+ uuid: 07e8f6a8-693c-4d62-b457-d6942290a631
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 575
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,26 @@
1
+ uuid: 5ad64293-8f2d-464c-b399-fe757564bba7
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ config:
6
+ - crop.type.aspect_635x424
7
+ module:
8
+ - crop
9
+ name: mc_660_aspect_635x424
10
+ label: Manual Crop and Scale 660
11
+ effects:
12
+ e0ec792c-5016-42a3-b45b-da3be91c1e8b:
13
+ uuid: e0ec792c-5016-42a3-b45b-da3be91c1e8b
14
+ id: crop_crop
15
+ weight: 1
16
+ data:
17
+ crop_type: aspect_635x424
18
+ automatic_crop_provider: null
19
+ a6cb3395-75ba-4cde-be2a-246aa76db45d:
20
+ uuid: a6cb3395-75ba-4cde-be2a-246aa76db45d
21
+ id: image_scale
22
+ weight: 2
23
+ data:
24
+ width: 660
25
+ height: null
26
+ upscale: false
@@ -0,0 +1,17 @@
1
+ uuid: d0c5b014-4002-4e45-bae8-18b5087e3c69
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ module:
6
+ - focal_point
7
+ name: sc_1150x920
8
+ label: Scale and Crop 1150 x 920
9
+ effects:
10
+ 9b83e6ab-740a-4224-95ba-06365f2f109b:
11
+ uuid: 9b83e6ab-740a-4224-95ba-06365f2f109b
12
+ id: focal_point_scale_and_crop
13
+ weight: 1
14
+ data:
15
+ width: 1150
16
+ height: 920
17
+ crop_type: focal_point
@@ -0,0 +1,17 @@
1
+ uuid: e311003b-d51e-4df2-87e6-03e71b0d2f17
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ module:
6
+ - focal_point
7
+ name: sc_1320x871
8
+ label: Scale and Crop 1320 x 871
9
+ effects:
10
+ 1dcbe0b1-d773-4b62-a1d0-a2bde29334e7:
11
+ uuid: 1dcbe0b1-d773-4b62-a1d0-a2bde29334e7
12
+ id: focal_point_scale_and_crop
13
+ weight: 1
14
+ data:
15
+ width: 1320
16
+ height: 871
17
+ crop_type: focal_point
@@ -0,0 +1,17 @@
1
+ uuid: 5f841024-e41b-43c5-a089-86e438319de5
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ module:
6
+ - focal_point
7
+ name: sc_575x460
8
+ label: Scale and Crop 575 x 460
9
+ effects:
10
+ c951eb35-2112-4032-9367-a722e1ab2f03:
11
+ uuid: c951eb35-2112-4032-9367-a722e1ab2f03
12
+ id: focal_point_scale_and_crop
13
+ weight: 1
14
+ data:
15
+ width: 575
16
+ height: 460
17
+ crop_type: focal_point
@@ -0,0 +1,17 @@
1
+ uuid: 60492e17-be04-4806-889f-041d1020a1d8
2
+ langcode: de
3
+ status: true
4
+ dependencies:
5
+ module:
6
+ - focal_point
7
+ name: sc_660x436
8
+ label: Scale and Crop 660 x 436
9
+ effects:
10
+ 87885a34-47ca-44b4-aa89-ba63f0bca648:
11
+ uuid: 87885a34-47ca-44b4-aa89-ba63f0bca648
12
+ id: focal_point_scale_and_crop
13
+ weight: 1
14
+ data:
15
+ width: 660
16
+ height: 436
17
+ crop_type: focal_point
@@ -0,0 +1,25 @@
1
+ default.xs: # mobile phones
2
+ label: XS
3
+ mediaQuery: '(max-width: 575px)'
4
+ weight: 0
5
+ multipliers:
6
+ - 1x
7
+ - 2x
8
+ imageStyles:
9
+ frontpage_hero:
10
+ label: 'Frontage Hero'
11
+ manual_crop: true
12
+ aspectRatio: '165:266'
13
+ width: 575
14
+ default.md: # large tablets
15
+ label: MD
16
+ mediaQuery: '(min-width: 576px) and (max-width: 991px)'
17
+ weight: 1
18
+ multipliers:
19
+ - 1x
20
+ - 2x
21
+ imageStyles:
22
+ # assuming, that the breakpoint is at md
23
+ frontpage_hero:
24
+ width: 660
25
+ aspectRatio: '635:424'
@@ -0,0 +1,24 @@
1
+ default.xs: # mobile phones
2
+ label: XS
3
+ mediaQuery: '(max-width: 575px)'
4
+ weight: 0
5
+ multipliers:
6
+ - 1x
7
+ - 2x
8
+ imageStyles:
9
+ frontpage_hero:
10
+ label: 'Frontage Hero'
11
+ aspectRatio: 0.8
12
+ width: 575
13
+ default.md: # large tablets
14
+ label: MD
15
+ mediaQuery: '(min-width: 576px) and (max-width: 991px)'
16
+ weight: 1
17
+ multipliers:
18
+ - 1x
19
+ - 2x
20
+ imageStyles:
21
+ # assuming, that the breakpoint is at md
22
+ frontpage_hero:
23
+ width: 660
24
+ aspectRatio: 0.66
package/test/test.js ADDED
@@ -0,0 +1,88 @@
1
+ const expect = require('chai').expect;
2
+ const gen = require('../index');
3
+ const fs = require("fs");
4
+
5
+ describe('Running image generator tests', function () {
6
+
7
+ it('should check for the existence of the breakpoints.yml file', () => {
8
+ const themeName = 'no_breakpoints_yml';
9
+ const themePath = './test/data/insufficient_modules/web/themes/' + themeName;
10
+
11
+ try {
12
+ const res = gen({
13
+ themePath: themePath,
14
+ themeName: themeName,
15
+ syncFolder: './test/data/insufficient_modules/config/sync/'
16
+ })
17
+
18
+ // just in case no exception is thrown
19
+ expect(res).to.be.false;
20
+ } catch (e) {
21
+ expect(e.message).to.equal(`Your configured theme has no ${themePath}/${themeName}.breakpoints.yml theme breakpoints file. Please read the documentation and double check your options.`);
22
+ }
23
+ });
24
+
25
+ it('should test for the existence of the focal_point module', () => {
26
+ const themeName = 'no_focal';
27
+ const themePath = './test/data/insufficient_modules/web/themes/' + themeName;
28
+
29
+ try {
30
+ const res = gen({
31
+ themePath: themePath,
32
+ themeName: themeName,
33
+ syncFolder: './test/data/insufficient_modules/config/sync/'
34
+ })
35
+
36
+ // just in case no exception is thrown
37
+ expect(res).to.be.false;
38
+ } catch (e) {
39
+ expect(e.message).to.equal(`Your drupal installation is missing the focal_point module. Please enable it before generating the image styles.`);
40
+ }
41
+ });
42
+
43
+ it('should test for the existence of the crop module', () => {
44
+ const themeName = 'no_crop';
45
+ const themePath = './test/data/insufficient_modules/web/themes/' + themeName;
46
+
47
+ try {
48
+ const res = gen({
49
+ themePath: themePath,
50
+ themeName: themeName,
51
+ syncFolder: './test/data/insufficient_modules/config/sync/'
52
+ })
53
+
54
+ // just in case no exception is thrown
55
+ expect(res).to.be.false;
56
+ } catch (e) {
57
+ expect(e.message).to.equal(`Your drupal installation is missing the crop module. Please enable it before generating the image styles.`);
58
+ }
59
+ });
60
+
61
+ it('should generate the correct crop styles', () => {
62
+ const themeName = 'crop';
63
+ const themePath = './test/data/crop/web/themes/' + themeName;
64
+ const syncFolder = './test/data/crop/config/sync/';
65
+
66
+ const res = gen({
67
+ themePath: themePath,
68
+ themeName: themeName,
69
+ syncFolder: syncFolder
70
+ })
71
+
72
+
73
+ expect(res).to.be.true;
74
+ expect(fs.readdirSync(syncFolder)).deep.equal([
75
+ 'core.extension.yml',
76
+ 'crop.type.aspect_165x266.yml',
77
+ 'crop.type.aspect_635x424.yml',
78
+ 'image.style.mc_1150_aspect_165x266.yml',
79
+ 'image.style.mc_1320_aspect_165x266.yml',
80
+ 'image.style.mc_290_aspect_635x424.yml',
81
+ 'image.style.mc_575_aspect_165x266.yml',
82
+ 'image.style.mc_580_aspect_635x424.yml',
83
+ 'image.style.mc_660_aspect_165x266.yml',
84
+ 'responsive_image.styles.frontpage_hero.yml'
85
+ ]);
86
+ });
87
+
88
+ });