1 | "use strict";
|
2 |
|
3 | const Promise = require(`bluebird`);
|
4 |
|
5 | const {
|
6 | GraphQLObjectType,
|
7 | GraphQLList,
|
8 | GraphQLBoolean,
|
9 | GraphQLString,
|
10 | GraphQLInt,
|
11 | GraphQLFloat,
|
12 | GraphQLNonNull
|
13 | } = require(`gatsby/graphql`);
|
14 |
|
15 | const {
|
16 | queueImageResizing,
|
17 | base64,
|
18 | fluid,
|
19 | fixed,
|
20 | traceSVG
|
21 | } = require(`gatsby-plugin-sharp`);
|
22 |
|
23 | const sharp = require(`./safe-sharp`);
|
24 |
|
25 | const fs = require(`fs`);
|
26 |
|
27 | const fsExtra = require(`fs-extra`);
|
28 |
|
29 | const imageSize = require(`probe-image-size`);
|
30 |
|
31 | const path = require(`path`);
|
32 |
|
33 | const DEFAULT_PNG_COMPRESSION_SPEED = 4;
|
34 |
|
35 | const {
|
36 | ImageFormatType,
|
37 | ImageCropFocusType,
|
38 | DuotoneGradientType,
|
39 | PotraceTurnPolicyType,
|
40 | PotraceType,
|
41 | ImageFitType
|
42 | } = require(`./types`);
|
43 |
|
44 | function toArray(buf) {
|
45 | const arr = new Array(buf.length);
|
46 |
|
47 | for (let i = 0; i < buf.length; i++) {
|
48 | arr[i] = buf[i];
|
49 | }
|
50 |
|
51 | return arr;
|
52 | }
|
53 |
|
54 | const getTracedSVG = async ({
|
55 | file,
|
56 | image,
|
57 | fieldArgs,
|
58 | cache,
|
59 | reporter
|
60 | }) => traceSVG({
|
61 | file,
|
62 | args: { ...fieldArgs.traceSVG
|
63 | },
|
64 | fileArgs: fieldArgs,
|
65 | cache,
|
66 | reporter
|
67 | });
|
68 |
|
69 | const fixedNodeType = ({
|
70 | pathPrefix,
|
71 | getNodeAndSavePathDependency,
|
72 | reporter,
|
73 | name,
|
74 | cache
|
75 | }) => {
|
76 | return {
|
77 | type: new GraphQLObjectType({
|
78 | name: name,
|
79 | fields: {
|
80 | base64: {
|
81 | type: GraphQLString
|
82 | },
|
83 | tracedSVG: {
|
84 | type: GraphQLString,
|
85 | resolve: parent => getTracedSVG({ ...parent,
|
86 | cache,
|
87 | reporter
|
88 | })
|
89 | },
|
90 | aspectRatio: {
|
91 | type: GraphQLFloat
|
92 | },
|
93 | width: {
|
94 | type: new GraphQLNonNull(GraphQLFloat)
|
95 | },
|
96 | height: {
|
97 | type: new GraphQLNonNull(GraphQLFloat)
|
98 | },
|
99 | src: {
|
100 | type: new GraphQLNonNull(GraphQLString)
|
101 | },
|
102 | srcSet: {
|
103 | type: new GraphQLNonNull(GraphQLString)
|
104 | },
|
105 | srcWebp: {
|
106 | type: GraphQLString,
|
107 | resolve: ({
|
108 | file,
|
109 | image,
|
110 | fieldArgs
|
111 | }) => {
|
112 |
|
113 |
|
114 | if (file.extension === `webp` || fieldArgs.toFormat === `webp`) {
|
115 | return null;
|
116 | }
|
117 |
|
118 | const args = { ...fieldArgs,
|
119 | pathPrefix,
|
120 | toFormat: `webp`
|
121 | };
|
122 | return Promise.resolve(fixed({
|
123 | file,
|
124 | args,
|
125 | reporter,
|
126 | cache
|
127 | })).then(({
|
128 | src
|
129 | }) => src);
|
130 | }
|
131 | },
|
132 | srcSetWebp: {
|
133 | type: GraphQLString,
|
134 | resolve: ({
|
135 | file,
|
136 | image,
|
137 | fieldArgs
|
138 | }) => {
|
139 | if (file.extension === `webp` || fieldArgs.toFormat === `webp`) {
|
140 | return null;
|
141 | }
|
142 |
|
143 | const args = { ...fieldArgs,
|
144 | pathPrefix,
|
145 | toFormat: `webp`
|
146 | };
|
147 | return Promise.resolve(fixed({
|
148 | file,
|
149 | args,
|
150 | reporter,
|
151 | cache
|
152 | })).then(({
|
153 | srcSet
|
154 | }) => srcSet);
|
155 | }
|
156 | },
|
157 | originalName: {
|
158 | type: GraphQLString
|
159 | }
|
160 | }
|
161 | }),
|
162 | args: {
|
163 | width: {
|
164 | type: GraphQLInt
|
165 | },
|
166 | height: {
|
167 | type: GraphQLInt
|
168 | },
|
169 | base64Width: {
|
170 | type: GraphQLInt
|
171 | },
|
172 | jpegProgressive: {
|
173 | type: GraphQLBoolean,
|
174 | defaultValue: true
|
175 | },
|
176 | pngCompressionSpeed: {
|
177 | type: GraphQLInt,
|
178 | defaultValue: DEFAULT_PNG_COMPRESSION_SPEED
|
179 | },
|
180 | grayscale: {
|
181 | type: GraphQLBoolean,
|
182 | defaultValue: false
|
183 | },
|
184 | duotone: {
|
185 | type: DuotoneGradientType,
|
186 | defaultValue: false
|
187 | },
|
188 | traceSVG: {
|
189 | type: PotraceType,
|
190 | defaultValue: false
|
191 | },
|
192 | quality: {
|
193 | type: GraphQLInt
|
194 | },
|
195 | jpegQuality: {
|
196 | type: GraphQLInt
|
197 | },
|
198 | pngQuality: {
|
199 | type: GraphQLInt
|
200 | },
|
201 | webpQuality: {
|
202 | type: GraphQLInt
|
203 | },
|
204 | toFormat: {
|
205 | type: ImageFormatType,
|
206 | defaultValue: ``
|
207 | },
|
208 | toFormatBase64: {
|
209 | type: ImageFormatType,
|
210 | defaultValue: ``
|
211 | },
|
212 | cropFocus: {
|
213 | type: ImageCropFocusType,
|
214 | defaultValue: sharp.strategy.attention
|
215 | },
|
216 | fit: {
|
217 | type: ImageFitType,
|
218 | defaultValue: sharp.fit.cover
|
219 | },
|
220 | background: {
|
221 | type: GraphQLString,
|
222 | defaultValue: `rgba(0,0,0,1)`
|
223 | },
|
224 | rotate: {
|
225 | type: GraphQLInt,
|
226 | defaultValue: 0
|
227 | },
|
228 | trim: {
|
229 | type: GraphQLFloat,
|
230 | defaultValue: false
|
231 | }
|
232 | },
|
233 | resolve: (image, fieldArgs, context) => {
|
234 | const file = getNodeAndSavePathDependency(image.parent, context.path);
|
235 | const args = { ...fieldArgs,
|
236 | pathPrefix
|
237 | };
|
238 | return Promise.resolve(fixed({
|
239 | file,
|
240 | args,
|
241 | reporter,
|
242 | cache
|
243 | })).then(o => Object.assign({}, o, {
|
244 | fieldArgs: args,
|
245 | image,
|
246 | file
|
247 | }));
|
248 | }
|
249 | };
|
250 | };
|
251 |
|
252 | const fluidNodeType = ({
|
253 | pathPrefix,
|
254 | getNodeAndSavePathDependency,
|
255 | reporter,
|
256 | name,
|
257 | cache
|
258 | }) => {
|
259 | return {
|
260 | type: new GraphQLObjectType({
|
261 | name: name,
|
262 | fields: {
|
263 | base64: {
|
264 | type: GraphQLString
|
265 | },
|
266 | tracedSVG: {
|
267 | type: GraphQLString,
|
268 | resolve: parent => getTracedSVG({ ...parent,
|
269 | cache,
|
270 | reporter
|
271 | })
|
272 | },
|
273 | aspectRatio: {
|
274 | type: new GraphQLNonNull(GraphQLFloat)
|
275 | },
|
276 | src: {
|
277 | type: new GraphQLNonNull(GraphQLString)
|
278 | },
|
279 | srcSet: {
|
280 | type: new GraphQLNonNull(GraphQLString)
|
281 | },
|
282 | srcWebp: {
|
283 | type: GraphQLString,
|
284 | resolve: ({
|
285 | file,
|
286 | image,
|
287 | fieldArgs
|
288 | }) => {
|
289 | if (image.extension === `webp` || fieldArgs.toFormat === `webp`) {
|
290 | return null;
|
291 | }
|
292 |
|
293 | const args = { ...fieldArgs,
|
294 | pathPrefix,
|
295 | toFormat: `webp`
|
296 | };
|
297 | return Promise.resolve(fluid({
|
298 | file,
|
299 | args,
|
300 | reporter,
|
301 | cache
|
302 | })).then(({
|
303 | src
|
304 | }) => src);
|
305 | }
|
306 | },
|
307 | srcSetWebp: {
|
308 | type: GraphQLString,
|
309 | resolve: ({
|
310 | file,
|
311 | image,
|
312 | fieldArgs
|
313 | }) => {
|
314 | if (image.extension === `webp` || fieldArgs.toFormat === `webp`) {
|
315 | return null;
|
316 | }
|
317 |
|
318 | const args = { ...fieldArgs,
|
319 | pathPrefix,
|
320 | toFormat: `webp`
|
321 | };
|
322 | return Promise.resolve(fluid({
|
323 | file,
|
324 | args,
|
325 | reporter,
|
326 | cache
|
327 | })).then(({
|
328 | srcSet
|
329 | }) => srcSet);
|
330 | }
|
331 | },
|
332 | sizes: {
|
333 | type: new GraphQLNonNull(GraphQLString)
|
334 | },
|
335 | originalImg: {
|
336 | type: GraphQLString
|
337 | },
|
338 | originalName: {
|
339 | type: GraphQLString
|
340 | },
|
341 | presentationWidth: {
|
342 | type: new GraphQLNonNull(GraphQLInt)
|
343 | },
|
344 | presentationHeight: {
|
345 | type: new GraphQLNonNull(GraphQLInt)
|
346 | }
|
347 | }
|
348 | }),
|
349 | args: {
|
350 | maxWidth: {
|
351 | type: GraphQLInt
|
352 | },
|
353 | maxHeight: {
|
354 | type: GraphQLInt
|
355 | },
|
356 | base64Width: {
|
357 | type: GraphQLInt
|
358 | },
|
359 | grayscale: {
|
360 | type: GraphQLBoolean,
|
361 | defaultValue: false
|
362 | },
|
363 | jpegProgressive: {
|
364 | type: GraphQLBoolean,
|
365 | defaultValue: true
|
366 | },
|
367 | pngCompressionSpeed: {
|
368 | type: GraphQLInt,
|
369 | defaultValue: DEFAULT_PNG_COMPRESSION_SPEED
|
370 | },
|
371 | duotone: {
|
372 | type: DuotoneGradientType,
|
373 | defaultValue: false
|
374 | },
|
375 | traceSVG: {
|
376 | type: PotraceType,
|
377 | defaultValue: false
|
378 | },
|
379 | quality: {
|
380 | type: GraphQLInt
|
381 | },
|
382 | jpegQuality: {
|
383 | type: GraphQLInt
|
384 | },
|
385 | pngQuality: {
|
386 | type: GraphQLInt
|
387 | },
|
388 | webpQuality: {
|
389 | type: GraphQLInt
|
390 | },
|
391 | toFormat: {
|
392 | type: ImageFormatType,
|
393 | defaultValue: ``
|
394 | },
|
395 | toFormatBase64: {
|
396 | type: ImageFormatType,
|
397 | defaultValue: ``
|
398 | },
|
399 | cropFocus: {
|
400 | type: ImageCropFocusType,
|
401 | defaultValue: sharp.strategy.attention
|
402 | },
|
403 | fit: {
|
404 | type: ImageFitType,
|
405 | defaultValue: sharp.fit.cover
|
406 | },
|
407 | background: {
|
408 | type: GraphQLString,
|
409 | defaultValue: `rgba(0,0,0,1)`
|
410 | },
|
411 | rotate: {
|
412 | type: GraphQLInt,
|
413 | defaultValue: 0
|
414 | },
|
415 | trim: {
|
416 | type: GraphQLFloat,
|
417 | defaultValue: false
|
418 | },
|
419 | sizes: {
|
420 | type: GraphQLString,
|
421 | defaultValue: ``
|
422 | },
|
423 | srcSetBreakpoints: {
|
424 | type: GraphQLList(GraphQLInt),
|
425 | defaultValue: [],
|
426 | description: `A list of image widths to be generated. Example: [ 200, 340, 520, 890 ]`
|
427 | }
|
428 | },
|
429 | resolve: (image, fieldArgs, context) => {
|
430 | const file = getNodeAndSavePathDependency(image.parent, context.path);
|
431 | const args = { ...fieldArgs,
|
432 | pathPrefix
|
433 | };
|
434 | return Promise.resolve(fluid({
|
435 | file,
|
436 | args,
|
437 | reporter,
|
438 | cache
|
439 | })).then(o => Object.assign({}, o, {
|
440 | fieldArgs: args,
|
441 | image,
|
442 | file
|
443 | }));
|
444 | }
|
445 | };
|
446 | };
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 | const inProgressCopy = new Set();
|
454 |
|
455 | const createFields = ({
|
456 | pathPrefix,
|
457 | getNodeAndSavePathDependency,
|
458 | reporter,
|
459 | cache
|
460 | }) => {
|
461 | const nodeOptions = {
|
462 | pathPrefix,
|
463 | getNodeAndSavePathDependency,
|
464 | reporter,
|
465 | cache
|
466 | };
|
467 |
|
468 | const fixedNode = fixedNodeType({
|
469 | name: `ImageSharpFixed`,
|
470 | ...nodeOptions
|
471 | });
|
472 | const resolutionsNode = fixedNodeType({
|
473 | name: `ImageSharpResolutions`,
|
474 | ...nodeOptions
|
475 | });
|
476 | resolutionsNode.deprecationReason = `Resolutions was deprecated in Gatsby v2. It's been renamed to "fixed" https://example.com/write-docs-and-fix-this-example-link`;
|
477 | const fluidNode = fluidNodeType({
|
478 | name: `ImageSharpFluid`,
|
479 | ...nodeOptions
|
480 | });
|
481 | const sizesNode = fluidNodeType({
|
482 | name: `ImageSharpSizes`,
|
483 | ...nodeOptions
|
484 | });
|
485 | sizesNode.deprecationReason = `Sizes was deprecated in Gatsby v2. It's been renamed to "fluid" https://example.com/write-docs-and-fix-this-example-link`;
|
486 | return {
|
487 | fixed: fixedNode,
|
488 | resolutions: resolutionsNode,
|
489 | fluid: fluidNode,
|
490 | sizes: sizesNode,
|
491 | original: {
|
492 | type: new GraphQLObjectType({
|
493 | name: `ImageSharpOriginal`,
|
494 | fields: {
|
495 | width: {
|
496 | type: GraphQLFloat
|
497 | },
|
498 | height: {
|
499 | type: GraphQLFloat
|
500 | },
|
501 | src: {
|
502 | type: GraphQLString
|
503 | }
|
504 | }
|
505 | }),
|
506 | args: {},
|
507 |
|
508 | async resolve(image, fieldArgs, context) {
|
509 | const details = getNodeAndSavePathDependency(image.parent, context.path);
|
510 | const dimensions = imageSize.sync(toArray(fs.readFileSync(details.absolutePath)));
|
511 | const imageName = `${details.name}-${image.internal.contentDigest}${details.ext}`;
|
512 | const publicPath = path.join(process.cwd(), `public`, `static`, imageName);
|
513 |
|
514 | if (!fsExtra.existsSync(publicPath) && !inProgressCopy.has(publicPath)) {
|
515 |
|
516 |
|
517 | inProgressCopy.add(publicPath);
|
518 | fsExtra.copy(details.absolutePath, publicPath, err => {
|
519 |
|
520 | inProgressCopy.delete(publicPath);
|
521 |
|
522 | if (err) {
|
523 | console.error(`error copying file from ${details.absolutePath} to ${publicPath}`, err);
|
524 | }
|
525 | });
|
526 | }
|
527 |
|
528 | return {
|
529 | width: dimensions.width,
|
530 | height: dimensions.height,
|
531 | src: `${pathPrefix}/static/${imageName}`
|
532 | };
|
533 | }
|
534 |
|
535 | },
|
536 | resize: {
|
537 | type: new GraphQLObjectType({
|
538 | name: `ImageSharpResize`,
|
539 | fields: {
|
540 | src: {
|
541 | type: GraphQLString
|
542 | },
|
543 | tracedSVG: {
|
544 | type: GraphQLString,
|
545 | resolve: parent => getTracedSVG({ ...parent,
|
546 | cache,
|
547 | reporter
|
548 | })
|
549 | },
|
550 | width: {
|
551 | type: GraphQLInt
|
552 | },
|
553 | height: {
|
554 | type: GraphQLInt
|
555 | },
|
556 | aspectRatio: {
|
557 | type: GraphQLFloat
|
558 | },
|
559 | originalName: {
|
560 | type: GraphQLString
|
561 | }
|
562 | }
|
563 | }),
|
564 | args: {
|
565 | width: {
|
566 | type: GraphQLInt
|
567 | },
|
568 | height: {
|
569 | type: GraphQLInt
|
570 | },
|
571 | quality: {
|
572 | type: GraphQLInt
|
573 | },
|
574 | jpegQuality: {
|
575 | type: GraphQLInt
|
576 | },
|
577 | pngQuality: {
|
578 | type: GraphQLInt
|
579 | },
|
580 | webpQuality: {
|
581 | type: GraphQLInt
|
582 | },
|
583 | jpegProgressive: {
|
584 | type: GraphQLBoolean,
|
585 | defaultValue: true
|
586 | },
|
587 | pngCompressionLevel: {
|
588 | type: GraphQLInt,
|
589 | defaultValue: 9
|
590 | },
|
591 | pngCompressionSpeed: {
|
592 | type: GraphQLInt,
|
593 | defaultValue: DEFAULT_PNG_COMPRESSION_SPEED
|
594 | },
|
595 | grayscale: {
|
596 | type: GraphQLBoolean,
|
597 | defaultValue: false
|
598 | },
|
599 | duotone: {
|
600 | type: DuotoneGradientType,
|
601 | defaultValue: false
|
602 | },
|
603 | base64: {
|
604 | type: GraphQLBoolean,
|
605 | defaultValue: false
|
606 | },
|
607 | traceSVG: {
|
608 | type: PotraceType,
|
609 | defaultValue: false
|
610 | },
|
611 | toFormat: {
|
612 | type: ImageFormatType,
|
613 | defaultValue: ``
|
614 | },
|
615 | cropFocus: {
|
616 | type: ImageCropFocusType,
|
617 | defaultValue: sharp.strategy.attention
|
618 | },
|
619 | fit: {
|
620 | type: ImageFitType,
|
621 | defaultValue: sharp.fit.cover
|
622 | },
|
623 | background: {
|
624 | type: GraphQLString,
|
625 | defaultValue: `rgba(0,0,0,1)`
|
626 | },
|
627 | rotate: {
|
628 | type: GraphQLInt,
|
629 | defaultValue: 0
|
630 | },
|
631 | trim: {
|
632 | type: GraphQLFloat,
|
633 | defaultValue: 0
|
634 | }
|
635 | },
|
636 | resolve: (image, fieldArgs, context) => {
|
637 | const file = getNodeAndSavePathDependency(image.parent, context.path);
|
638 | const args = { ...fieldArgs,
|
639 | pathPrefix
|
640 | };
|
641 | return new Promise(resolve => {
|
642 | if (fieldArgs.base64) {
|
643 | resolve(base64({
|
644 | file,
|
645 | cache
|
646 | }));
|
647 | } else {
|
648 | const o = queueImageResizing({
|
649 | file,
|
650 | args
|
651 | });
|
652 | resolve(Object.assign({}, o, {
|
653 | image,
|
654 | file,
|
655 | fieldArgs: args
|
656 | }));
|
657 | }
|
658 | });
|
659 | }
|
660 | }
|
661 | };
|
662 | };
|
663 |
|
664 | module.exports = ({
|
665 | actions,
|
666 | schema,
|
667 | pathPrefix,
|
668 | getNodeAndSavePathDependency,
|
669 | reporter,
|
670 | cache
|
671 | }) => {
|
672 | const {
|
673 | createTypes
|
674 | } = actions;
|
675 | const imageSharpType = schema.buildObjectType({
|
676 | name: `ImageSharp`,
|
677 | fields: createFields({
|
678 | pathPrefix,
|
679 | getNodeAndSavePathDependency,
|
680 | reporter,
|
681 | cache
|
682 | }),
|
683 | interfaces: [`Node`],
|
684 | extensions: {
|
685 | infer: true,
|
686 | childOf: {
|
687 | types: [`File`]
|
688 | }
|
689 | }
|
690 | });
|
691 |
|
692 | if (createTypes) {
|
693 | createTypes([ImageFormatType, ImageFitType, ImageCropFocusType, DuotoneGradientType, PotraceTurnPolicyType, PotraceType, imageSharpType]);
|
694 | }
|
695 | }; |
\ | No newline at end of file |