libvips.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2013 Lovell Fuller and others.
  2. // SPDX-License-Identifier: Apache-2.0
  3. 'use strict';
  4. const { spawnSync } = require('node:child_process');
  5. const { createHash } = require('node:crypto');
  6. const semverCoerce = require('semver/functions/coerce');
  7. const semverGreaterThanOrEqualTo = require('semver/functions/gte');
  8. const semverSatisfies = require('semver/functions/satisfies');
  9. const detectLibc = require('detect-libc');
  10. const { config, engines, optionalDependencies } = require('../package.json');
  11. const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
  12. config.libvips;
  13. const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
  14. const prebuiltPlatforms = [
  15. 'darwin-arm64', 'darwin-x64',
  16. 'linux-arm', 'linux-arm64', 'linux-s390x', 'linux-x64',
  17. 'linuxmusl-arm64', 'linuxmusl-x64',
  18. 'win32-ia32', 'win32-x64'
  19. ];
  20. const spawnSyncOptions = {
  21. encoding: 'utf8',
  22. shell: true
  23. };
  24. const log = (item) => {
  25. if (item instanceof Error) {
  26. console.error(`sharp: Installation error: ${item.message}`);
  27. } else {
  28. console.log(`sharp: ${item}`);
  29. }
  30. };
  31. /* istanbul ignore next */
  32. const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
  33. const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
  34. /* istanbul ignore next */
  35. const buildPlatformArch = () => {
  36. if (isEmscripten()) {
  37. return 'wasm32';
  38. }
  39. /* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
  40. const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
  41. const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
  42. return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
  43. };
  44. const buildSharpLibvipsIncludeDir = () => {
  45. try {
  46. return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`);
  47. } catch {
  48. try {
  49. return require('@img/sharp-libvips-dev/include');
  50. } catch {}
  51. }
  52. /* istanbul ignore next */
  53. return '';
  54. };
  55. const buildSharpLibvipsCPlusPlusDir = () => {
  56. try {
  57. return require('@img/sharp-libvips-dev/cplusplus');
  58. } catch {}
  59. /* istanbul ignore next */
  60. return '';
  61. };
  62. const buildSharpLibvipsLibDir = () => {
  63. try {
  64. return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`);
  65. } catch {
  66. try {
  67. return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
  68. } catch {}
  69. }
  70. /* istanbul ignore next */
  71. return '';
  72. };
  73. const isUnsupportedNodeRuntime = () => {
  74. /* istanbul ignore next */
  75. if (process.release?.name === 'node' && process.versions) {
  76. if (!semverSatisfies(process.versions.node, engines.node)) {
  77. return { found: process.versions.node, expected: engines.node };
  78. }
  79. }
  80. };
  81. /* istanbul ignore next */
  82. const isEmscripten = () => {
  83. const { CC } = process.env;
  84. return Boolean(CC && CC.endsWith('/emcc'));
  85. };
  86. const isRosetta = () => {
  87. /* istanbul ignore next */
  88. if (process.platform === 'darwin' && process.arch === 'x64') {
  89. const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
  90. return (translated || '').trim() === 'sysctl.proc_translated: 1';
  91. }
  92. return false;
  93. };
  94. const sha512 = (s) => createHash('sha512').update(s).digest('hex');
  95. const yarnLocator = () => {
  96. try {
  97. const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
  98. const npmVersion = semverCoerce(optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`]).version;
  99. return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
  100. } catch {}
  101. return '';
  102. };
  103. /* istanbul ignore next */
  104. const spawnRebuild = () =>
  105. spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
  106. ...spawnSyncOptions,
  107. stdio: 'inherit'
  108. }).status;
  109. const globalLibvipsVersion = () => {
  110. if (process.platform !== 'win32') {
  111. const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
  112. ...spawnSyncOptions,
  113. env: {
  114. ...process.env,
  115. PKG_CONFIG_PATH: pkgConfigPath()
  116. }
  117. }).stdout;
  118. /* istanbul ignore next */
  119. return (globalLibvipsVersion || '').trim();
  120. } else {
  121. return '';
  122. }
  123. };
  124. /* istanbul ignore next */
  125. const pkgConfigPath = () => {
  126. if (process.platform !== 'win32') {
  127. const brewPkgConfigPath = spawnSync(
  128. 'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
  129. spawnSyncOptions
  130. ).stdout || '';
  131. return [
  132. brewPkgConfigPath.trim(),
  133. process.env.PKG_CONFIG_PATH,
  134. '/usr/local/lib/pkgconfig',
  135. '/usr/lib/pkgconfig',
  136. '/usr/local/libdata/pkgconfig',
  137. '/usr/libdata/pkgconfig'
  138. ].filter(Boolean).join(':');
  139. } else {
  140. return '';
  141. }
  142. };
  143. const skipSearch = (status, reason, logger) => {
  144. if (logger) {
  145. logger(`Detected ${reason}, skipping search for globally-installed libvips`);
  146. }
  147. return status;
  148. };
  149. const useGlobalLibvips = (logger) => {
  150. if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
  151. return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS', logger);
  152. }
  153. if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
  154. return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
  155. }
  156. /* istanbul ignore next */
  157. if (isRosetta()) {
  158. return skipSearch(false, 'Rosetta', logger);
  159. }
  160. const globalVipsVersion = globalLibvipsVersion();
  161. return !!globalVipsVersion && /* istanbul ignore next */
  162. semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
  163. };
  164. module.exports = {
  165. minimumLibvipsVersion,
  166. prebuiltPlatforms,
  167. buildPlatformArch,
  168. buildSharpLibvipsIncludeDir,
  169. buildSharpLibvipsCPlusPlusDir,
  170. buildSharpLibvipsLibDir,
  171. isUnsupportedNodeRuntime,
  172. runtimePlatformArch,
  173. log,
  174. yarnLocator,
  175. spawnRebuild,
  176. globalLibvipsVersion,
  177. pkgConfigPath,
  178. useGlobalLibvips
  179. };