UNPKG

4.46 kBJavaScriptView Raw
1"use strict";
2
3// This is very hacky, but this should be temporary hack
4// until we make "sharp" a peerDependency and can be removed afterwards.
5// It's not done yet because it would cause too much friction.
6// Image processing is important part of Gatsby ecosystem
7// and there's lot of guides and tutorials that we don't control
8// that would be outdated. Moving "sharp" to be peerDependency
9// is scheduled for gatsby@3.
10// This file is duplicated in multiple location in this repository,
11// So make sure to apply same changes every "safe-sharp" file.
12const childProcess = require(`child_process`);
13
14const path = require(`path`);
15
16const fs = require(`fs`);
17
18const semver = require(`semver`);
19
20const originalConsoleError = console.error;
21
22const restoreConsoleError = () => {
23 console.error = originalConsoleError;
24};
25
26const getDetailedMessage = () => {
27 try {
28 // `npm list` seems to work in yarn installed projects as long
29 // as there is no package-lock.json, so let's bail out
30 // if both lock files exist
31 if (fs.existsSync(path.join(process.cwd(), `package-lock.json`)) && fs.existsSync(path.join(process.cwd(), `yarn.lock`))) {
32 return null;
33 }
34
35 let msg = [];
36 const {
37 dependencies
38 } = JSON.parse(childProcess.execSync(`npm list sharp --json`, {
39 encoding: `utf-8`
40 }));
41
42 const findSharpVersion = dependency => {
43 if (dependency.dependencies.sharp) {
44 return dependency.dependencies.sharp.version;
45 }
46
47 for (let depName of Object.keys(dependency.dependencies)) {
48 const v = findSharpVersion(dependency.dependencies[depName]);
49
50 if (v) {
51 return v;
52 }
53 }
54
55 return null;
56 };
57
58 const {
59 latestVersion,
60 topLevelPackages
61 } = Object.keys(dependencies).reduce((acc, depName) => {
62 const sharpVersion = findSharpVersion(dependencies[depName]);
63
64 if (sharpVersion) {
65 acc.topLevelPackages[depName] = sharpVersion;
66
67 if (!acc.latestVersion || semver.gt(sharpVersion, acc.latestVersion)) {
68 acc.latestVersion = sharpVersion;
69 }
70 }
71
72 return acc;
73 }, {
74 latestVersion: undefined,
75 topLevelPackages: {}
76 });
77 let packagesToUpdate = []; // list top level dependencies
78
79 msg = msg.concat([`List of installed packages that depend on sharp:`, ...Object.keys(topLevelPackages).map(depName => {
80 const sharpVersion = topLevelPackages[depName];
81
82 if (sharpVersion !== latestVersion) {
83 packagesToUpdate.push(depName);
84 }
85
86 return ` - ${depName}${sharpVersion ? ` (${sharpVersion})${sharpVersion !== latestVersion ? ` - needs update` : ``}` : ``}`;
87 })]);
88
89 if (packagesToUpdate.length > 0) {
90 msg = msg.concat([``, `If you are using npm, run:`, ``, `npm install ${packagesToUpdate.join(` `)}`, ``, `If you are using yarn, run:`, ``, `yarn add ${packagesToUpdate.join(` `)}`]);
91 }
92
93 return msg; // eslint-disable-next-line no-empty
94 } catch {
95 return null;
96 }
97};
98
99const handleMessage = msg => {
100 if (msg.includes(`Incompatible library version: sharp.node requires`)) {
101 restoreConsoleError();
102 let msg = [`It looks like there are multiple versions of "sharp" module installed.`, `Please update any packages that depend on "sharp".`, ``];
103 const detailedMessage = getDetailedMessage();
104
105 if (!detailedMessage) {
106 msg = msg.concat([`To get a list of installed packages that depend on "sharp" try running:`, ` - npm list sharp (if you use npm)`, ` - yarn why sharp (if you use yarn)`, ` and update packages that depend on version older than latest listed in output of above command.`]);
107 } else {
108 msg = msg.concat(detailedMessage);
109 }
110
111 msg = msg.concat([``, `If an older version of "sharp" still persists and this error is displayed after updating your packages, open an issue in the package's repository and request them to update the "sharp" dependency.`]);
112 console.error(msg.join(`\n`));
113 }
114};
115
116let sharp;
117
118try {
119 // sharp@0.22.1 uses console.error and then process.exit and doesn't throw
120 // so to capture error and provide meaningful troubleshooting guide
121 // we intercept console.error calls and add special handling.
122 console.error = (msg, ...args) => {
123 originalConsoleError(msg, ...args);
124 handleMessage(msg);
125 };
126
127 sharp = require(`sharp`);
128} catch (e) {
129 handleMessage(e.toString());
130 throw e;
131} finally {
132 restoreConsoleError();
133}
134
135module.exports = sharp;
\No newline at end of file