Distribute prebuilt binaries via the npm registry #3750

- Remove all custom download logic for prebuilt binaries
- Add scripts to populate package contents
- Specify minimum versions of common package managers
- Remove sharp.vendor runtime API as no-longer relevant
- Update installation docs and issue templates
This commit is contained in:
Lovell Fuller 2023-09-25 16:50:41 +01:00
parent 0f8bb9196e
commit aabbe1fa08
42 changed files with 842 additions and 6509 deletions

View File

@ -36,6 +36,12 @@ jobs:
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs" sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
- run: sudo docker exec sharp sh -c "npm install --build-from-source" - run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test" - run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/sharp-linux-arm64=file:./npm/linux-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --upload=$prebuild_upload\" || true" - run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-glibc-node-20: linux-arm64-glibc-node-20:
resource_class: arm.medium resource_class: arm.medium
@ -54,6 +60,12 @@ jobs:
sudo docker cp . sharp:/mnt/sharp/. sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source" - run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test" - run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/sharp-linux-arm64=file:./npm/linux-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
linux-arm64-musl-node-18: linux-arm64-musl-node-18:
resource_class: arm.medium resource_class: arm.medium
machine: machine:
@ -65,6 +77,12 @@ jobs:
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache" sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
- run: sudo docker exec sharp sh -c "npm install --build-from-source" - run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test" - run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --upload=$prebuild_upload\" || true" - run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-musl-node-20: linux-arm64-musl-node-20:
resource_class: arm.medium resource_class: arm.medium
@ -79,3 +97,9 @@ jobs:
sudo docker cp . sharp:/mnt/sharp/. sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source" - run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test" - run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"

View File

@ -12,7 +12,6 @@ labels: installation
<!-- Please place an [x] in the box to confirm. --> <!-- Please place an [x] in the box to confirm. -->
- [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install). - [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install).
- [ ] I have ensured that the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime.
### Are you using the latest version of sharp? ### Are you using the latest version of sharp?
@ -24,13 +23,25 @@ If you cannot confirm this, please upgrade to the latest version and try again b
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead. If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### Is this a problem with filesystem permissions? ### Are you using a supported runtime?
If you are using npm v6 or earlier and installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag? <!-- Please place an [x] in the relevant box to confirm. -->
If you are using npm v7 or later, does the user running `npm install` own the directory it is run in? - [ ] I am using Node.js 18 with a version >= 18.17.0
- [ ] I am using Node.js 20 with a version >= 20.3.0
- [ ] I am using Node.js 21 or later
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag? If you cannot confirm any of these, please upgrade to the latest version and try again before opening an issue.
### Are you using a supported package manager?
<!-- Please place an [x] in the relevant box to confirm. -->
- [ ] I am using npm >= 9.6.5
- [ ] I am using yarn >= 3.2.0
- [ ] I am using pnpm >= 7.1.0
If you cannot confirm any of these, please upgrade to the latest version and try again before opening an issue.
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory? ### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?

View File

@ -37,6 +37,7 @@ jobs:
- os: macos-11 - os: macos-11
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18
platform: darwin-x64 platform: darwin-x64
prebuild: true prebuild: true
- os: macos-11 - os: macos-11
@ -98,13 +99,17 @@ jobs:
architecture: ${{ matrix.nodejs_arch }} architecture: ${{ matrix.nodejs_arch }}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Fix working directory ownership
if: matrix.container
run: chown root.root .
- name: Install - name: Install
run: npm install --build-from-source run: npm install --build-from-source
- name: Test - name: Test
run: npm test run: npm test
- name: Test packaging
run: |
npm run package-from-local-build
npm pkg set "optionalDependencies.@sharpen/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
npm run clean
npm install --ignore-scripts
npm test
- name: Prebuild - name: Prebuild
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/') if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env: env:
@ -130,9 +135,13 @@ jobs:
mkdir /opt/nodejs mkdir /opt/nodejs
curl --silent https://unofficial-builds.nodejs.org/download/release/v${nodejs_version}/node-v${nodejs_version}-linux-armv6l.tar.xz | tar xJC /opt/nodejs --strip-components=1 curl --silent https://unofficial-builds.nodejs.org/download/release/v${nodejs_version}/node-v${nodejs_version}-linux-armv6l.tar.xz | tar xJC /opt/nodejs --strip-components=1
export PATH=$PATH:/opt/nodejs/bin export PATH=$PATH:/opt/nodejs/bin
chown root.root .
npm install --build-from-source npm install --build-from-source
npx mocha --no-config --spec=test/unit/io.js npx mocha --no-config --spec=test/unit/io.js
npm run package-from-local-build
npm pkg set "optionalDependencies.@sharpen/sharp-linux-arm=file:./npm/linux-arm"
npm run clean
npm install --ignore-scripts
npx mocha --no-config --spec=test/unit/io.js
[[ -n $prebuild_upload ]] && npx prebuild || true [[ -n $prebuild_upload ]] && npx prebuild || true
macstadium-runner: macstadium-runner:
permissions: permissions:
@ -167,6 +176,13 @@ jobs:
run: npm install --build-from-source run: npm install --build-from-source
- name: Test - name: Test
run: npm test run: npm test
- name: Test packaging
run: |
npm run package-from-local-build
npm pkg set "optionalDependencies.@sharpen/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
npm run clean
npm install --ignore-scripts
npm test
- name: Prebuild - name: Prebuild
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/') if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env: env:

3
.gitignore vendored
View File

@ -1,6 +1,8 @@
build build
node_modules node_modules
/coverage /coverage
npm/*/*
!npm/*/package.json
test/bench/node_modules test/bench/node_modules
test/fixtures/output* test/fixtures/output*
test/fixtures/vips-properties.xml test/fixtures/vips-properties.xml
@ -9,7 +11,6 @@ test/saliency/report.json
test/saliency/Image* test/saliency/Image*
test/saliency/[Uu]serData* test/saliency/[Uu]serData*
!test/saliency/userData.js !test/saliency/userData.js
vendor
.gitattributes .gitattributes
.DS_Store .DS_Store
.nyc_output .nyc_output

View File

@ -1,6 +1,6 @@
{ {
"runtime": "napi", "runtime": "napi",
"include-regex": "(sharp-.+\\.node|libvips-cpp\\.dll)", "include-regex": "(sharp-.+\\.node|libvips-.+\\.dll|libglib-.+\\.dll|libgobject-.+\\.dll)",
"prerelease": true, "prerelease": true,
"strip": true "strip": true
} }

View File

@ -1,11 +1,16 @@
# Copyright 2013 Lovell Fuller and others.
# SPDX-License-Identifier: Apache-2.0
{ {
'variables': { 'variables': {
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")', 'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
'platform_and_arch': '<!(node -p "require(\'./lib/platform\')()")', 'platform_and_arch': '<!(node -p "require(\'./lib/libvips\').buildPlatformArch()")',
'sharp_vendor_dir': './vendor/<(vips_version)/<(platform_and_arch)' 'sharp_libvips_include_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsIncludeDir()")',
'sharp_libvips_cplusplus_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsCPlusPlusDir()")',
'sharp_libvips_lib_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsLibDir()")'
}, },
'targets': [{ 'targets': [{
'target_name': 'libvips-cpp', 'target_name': 'win-libvips-cpp',
'conditions': [ 'conditions': [
['OS == "win"', { ['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes # Build libvips C++ binding for Windows due to MSVC std library ABI changes
@ -15,19 +20,19 @@
'_ALLOW_KEYWORD_MACROS' '_ALLOW_KEYWORD_MACROS'
], ],
'sources': [ 'sources': [
'src/libvips/cplusplus/VConnection.cpp', '<(sharp_libvips_cplusplus_dir)/VConnection.cpp',
'src/libvips/cplusplus/VError.cpp', '<(sharp_libvips_cplusplus_dir)/VError.cpp',
'src/libvips/cplusplus/VImage.cpp', '<(sharp_libvips_cplusplus_dir)/VImage.cpp',
'src/libvips/cplusplus/VInterpolate.cpp', '<(sharp_libvips_cplusplus_dir)/VInterpolate.cpp',
'src/libvips/cplusplus/VRegion.cpp' '<(sharp_libvips_cplusplus_dir)/VRegion.cpp'
], ],
'include_dirs': [ 'include_dirs': [
'<(sharp_vendor_dir)/include', '<(sharp_libvips_include_dir)',
'<(sharp_vendor_dir)/include/glib-2.0', '<(sharp_libvips_include_dir)/glib-2.0',
'<(sharp_vendor_dir)/lib/glib-2.0/include' '<(sharp_libvips_lib_dir)/glib-2.0/include'
], ],
'link_settings': { 'link_settings': {
'library_dirs': ['<(sharp_vendor_dir)/lib'], 'library_dirs': ['<(sharp_libvips_lib_dir)'],
'libraries': [ 'libraries': [
'libvips.lib', 'libvips.lib',
'libglib-2.0.lib', 'libglib-2.0.lib',
@ -76,10 +81,9 @@
], ],
'dependencies': [ 'dependencies': [
'<!(node -p "require(\'node-addon-api\').gyp")', '<!(node -p "require(\'node-addon-api\').gyp")',
'libvips-cpp' 'win-libvips-cpp'
], ],
'variables': { 'variables': {
'runtime_link%': 'shared',
'conditions': [ 'conditions': [
['OS != "win"', { ['OS != "win"', {
'pkg_config_path': '<!(node -p "require(\'./lib/libvips\').pkgConfigPath()")', 'pkg_config_path': '<!(node -p "require(\'./lib/libvips\').pkgConfigPath()")',
@ -106,12 +110,8 @@
['use_global_libvips == "true"', { ['use_global_libvips == "true"', {
# Use pkg-config for include and lib # Use pkg-config for include and lib
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'], 'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)'],
'conditions': [ 'conditions': [
['runtime_link == "static"', {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
}, {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
}],
['OS == "linux"', { ['OS == "linux"', {
'defines': [ 'defines': [
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible. # Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
@ -122,9 +122,9 @@
}, { }, {
# Use pre-built libvips stored locally within node_modules # Use pre-built libvips stored locally within node_modules
'include_dirs': [ 'include_dirs': [
'<(sharp_vendor_dir)/include', '<(sharp_libvips_include_dir)',
'<(sharp_vendor_dir)/include/glib-2.0', '<(sharp_libvips_include_dir)/glib-2.0',
'<(sharp_vendor_dir)/lib/glib-2.0/include' '<(sharp_libvips_lib_dir)/glib-2.0/include'
], ],
'conditions': [ 'conditions': [
['OS == "win"', { ['OS == "win"', {
@ -133,7 +133,7 @@
'_FILE_OFFSET_BITS=64' '_FILE_OFFSET_BITS=64'
], ],
'link_settings': { 'link_settings': {
'library_dirs': ['<(sharp_vendor_dir)/lib'], 'library_dirs': ['<(sharp_libvips_lib_dir)'],
'libraries': [ 'libraries': [
'libvips.lib', 'libvips.lib',
'libglib-2.0.lib', 'libglib-2.0.lib',
@ -143,7 +143,9 @@
}], }],
['OS == "mac"', { ['OS == "mac"', {
'link_settings': { 'link_settings': {
'library_dirs': ['../<(sharp_vendor_dir)/lib'], 'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'libraries': [ 'libraries': [
'libvips-cpp.42.dylib' 'libvips-cpp.42.dylib'
] ]
@ -151,7 +153,9 @@
'xcode_settings': { 'xcode_settings': {
'OTHER_LDFLAGS': [ 'OTHER_LDFLAGS': [
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-rpath,\'@loader_path/../../<(sharp_vendor_dir)/lib\'' '-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\''
] ]
} }
}], }],
@ -160,13 +164,19 @@
'_GLIBCXX_USE_CXX11_ABI=1' '_GLIBCXX_USE_CXX11_ABI=1'
], ],
'link_settings': { 'link_settings': {
'library_dirs': ['../<(sharp_vendor_dir)/lib'], 'library_dirs': [
'<(sharp_libvips_lib_dir)'
],
'libraries': [ 'libraries': [
'-l:libvips-cpp.so.42' '-l:libvips-cpp.so.42'
], ],
'ldflags': [ 'ldflags': [
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../<(sharp_vendor_dir)/lib\'' '-Wl,-s',
'-Wl,--disable-new-dtags',
'-Wl,-rpath=\'$$ORIGIN/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\''
] ]
} }
}] }]
@ -232,5 +242,23 @@
] ]
} }
}, },
}, {
'target_name': 'win-copy-dlls',
'type': 'none',
'dependencies': [
'sharp-<(platform_and_arch)'
],
'conditions': [
['OS == "win"', {
'copies': [{
'destination': 'build/Release',
'files': [
'<(sharp_libvips_lib_dir)/libvips-42.dll',
'<(sharp_libvips_lib_dir)/libglib-2.0-0.dll',
'<(sharp_libvips_lib_dir)/libgobject-2.0-0.dll'
]
}]
}]
]
}] }]
} }

View File

@ -8,6 +8,10 @@ npm install sharp
yarn add sharp yarn add sharp
``` ```
```sh
pnpm add sharp
```
## Prerequisites ## Prerequisites
* Node.js >= 18.17.0 * Node.js >= 18.17.0
@ -18,98 +22,19 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* macOS x64 (>= 10.13) * macOS x64 (>= 10.13)
* macOS ARM64 * macOS ARM64
* Linux x64 (glibc >= 2.17, musl >= 1.1.24, CPU with SSE4.2) * Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
* Linux ARM64 (glibc >= 2.17, musl >= 1.1.24) * Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
* Linux ARM (glibc >= 2.28)
* Windows x64 * Windows x64
* Windows x86 * Windows x86
A ~7MB tarball containing libvips and its most commonly used dependencies
is downloaded via HTTPS, verified via Subresource Integrity
and decompressed into `node_modules/sharp/vendor` during `npm install`.
This provides support for the This provides support for the
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats. JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
The following platforms have prebuilt libvips but not sharp:
* Linux ARMv7 (glibc >= 2.28)
* Linux ARMv6 (glibc >= 2.28)
* Windows ARM64
The following platforms require compilation of both libvips and sharp from source:
* Linux x86
* Linux ARMv7 (glibc <= 2.27, musl)
* Linux ARMv6 (glibc <= 2.27, musl)
* Linux PowerPC
* FreeBSD
* OpenBSD
## Common problems
The architecture and platform of Node.js used for `npm install`
must be the same as the architecture and platform of Node.js used at runtime.
See the [cross-platform](#cross-platform) section if this is not the case.
When using npm v6 or earlier, the `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
When using npm v7 or later, the user running `npm install` must own the directory it is run in.
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
Check the output of running `npm install --verbose --foreground-scripts sharp` for useful error messages.
## Apple M1
Prebuilt sharp and libvips binaries have been provided for macOS on ARM64 since sharp v0.29.0.
## Cross-platform
At `npm install` time, prebuilt binaries are automatically selected for the
current OS platform and CPU architecture, where available.
The target platform and/or architecture can be manually selected using the following flags.
```sh
npm install --platform=... --arch=... --arm-version=... sharp
```
* `--platform`: one of `linux`, `darwin` or `win32`.
* `--arch`: one of `x64`, `ia32`, `arm` or `arm64`.
* `--arm-version`: one of `6`, `7` or `8` (`arm` defaults to `6`, `arm64` defaults to `8`).
* `--libc`: one of `glibc` or `musl`. This option only works with platform `linux`, defaults to `glibc`
* `--sharp-install-force`: skip version compatibility and Subresource Integrity checks.
These values can also be set via environment variables,
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`, `npm_config_libc`
and `SHARP_INSTALL_FORCE` respectively.
For example, if the target machine has a 64-bit ARM CPU and is running Alpine Linux,
use the following flags:
```sh
npm install --arch=arm64 --platform=linux --libc=musl sharp
```
If the current machine is Alpine Linux and the target machine is Debian Linux on x64 cpu,
use the following flags:
```sh
npm install --arch=x64 --platform=linux --libc=glibc sharp
```
Multiple platforms and architectures can be supported within the same installation tree.
The following example for macOS installs x64 binaries then adds (via a rebuild) arm64 binaries:
```sh
npm install --platform=darwin --arch=x64 sharp
npm rebuild --platform=darwin --arch=arm64 sharp
```
## Custom libvips ## Custom libvips
To use a custom, globally-installed version of libvips instead of the provided binaries, To use a custom, globally-installed version of libvips instead of the provided binaries,
make sure it is at least the version listed under `config.libvips` in the `package.json` file make sure it is at least the version listed under `engines.libvips` in the `package.json` file
and that it can be located using `pkg-config --modversion vips-cpp`. and that it can be located using `pkg-config --modversion vips-cpp`.
For help compiling libvips and its dependencies, please see For help compiling libvips and its dependencies, please see
@ -122,8 +47,7 @@ and on macOS when running Node.js under Rosetta.
This module will be compiled from source at `npm install` time when: This module will be compiled from source at `npm install` time when:
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this), * a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this), or
* prebuilt sharp binaries do not exist for the current platform, or
* when the `npm install --build-from-source` flag is used. * when the `npm install --build-from-source` flag is used.
Building from source requires: Building from source requires:
@ -131,83 +55,11 @@ Building from source requires:
* C++11 compiler * C++11 compiler
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies * [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies
## Custom prebuilt binaries If `node-gyp` cannot be found, try adding it to `devDependencies`.
This is an advanced approach that most people will not require. For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
(or the `npm_config_platform`, `npm_config_arch` and `npm_config_libc` environment variables)
### Prebuilt sharp binaries can be used to configure the target environment.
To install the prebuilt sharp binaries from a custom URL,
set the `sharp_binary_host` npm config option
or the `npm_config_sharp_binary_host` environment variable.
To install the prebuilt sharp binaries from a directory on the local filesystem,
set the `sharp_local_prebuilds` npm config option
or the `npm_config_sharp_local_prebuilds` environment variable.
URL example:
if `sharp_binary_host` is set to `https://hostname/path`
and the sharp version is `1.2.3` then the resultant URL will be
`https://hostname/path/v1.2.3/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
Filename example:
if `sharp_local_prebuilds` is set to `/path`
and the sharp version is `1.2.3` then the resultant filename will be
`/path/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
### Prebuilt libvips binaries
To install the prebuilt libvips binaries from a custom URL,
set the `sharp_libvips_binary_host` npm config option
or the `npm_config_sharp_libvips_binary_host` environment variable.
To install the prebuilt libvips binaries from a directory on the local filesystem,
set the `sharp_libvips_local_prebuilds` npm config option
or the `npm_config_sharp_libvips_local_prebuilds` environment variable.
The version subpath and filename are appended to these.
URL example:
if `sharp_libvips_binary_host` is set to `https://hostname/path`
and the libvips version is `4.5.6` then the resultant URL will be
`https://hostname/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
Filename example:
if `sharp_libvips_local_prebuilds` is set to `/path`
and the libvips version is `4.5.6` then the resultant filename will be
`/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
See the Chinese mirror below for a further example.
If these binaries are modified, new integrity hashes can be provided
at install time via `npm_package_config_integrity_platform_arch`
environment variables, for example set
`npm_package_config_integrity_linux_x64` to `sha512-abc...`.
The integrity hash of a file can be generated via:
```sh
sha512sum libvips-x.y.z-platform-arch.tar.br | cut -f1 -d' ' | xxd -r -p | base64 -w 0
```
## Chinese mirror
A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips.
To use this either set the following configuration:
```sh
npm config set sharp_binary_host "https://npmmirror.com/mirrors/sharp"
npm config set sharp_libvips_binary_host "https://npmmirror.com/mirrors/sharp-libvips"
npm install sharp
```
or set the following environment variables:
```sh
npm_config_sharp_binary_host="https://npmmirror.com/mirrors/sharp" \
npm_config_sharp_libvips_binary_host="https://npmmirror.com/mirrors/sharp-libvips" \
npm install sharp
```
## FreeBSD ## FreeBSD
@ -242,15 +94,18 @@ unaffected.
The `node_modules` directory of the The `node_modules` directory of the
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html) [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
must include binaries for the Linux x64 platform. must include binaries for either the linux-x64 or linux-arm64 platforms
depending on the chosen architecture.
When building your deployment package on machines other than Linux x64 (glibc), When building your deployment package on a machine that differs from the target architecture,
run the following additional command after `npm install`: you will need to install either `@sharpen/sharp-linux-x64` or `@sharpen/sharp-linux-arm64` package.
```sh ```sh
npm install npm install --force @sharpen/sharp-linux-x64
rm -rf node_modules/sharp ```
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux --libc=glibc sharp
```sh
npm install --force @sharpen/sharp-linux-arm64
``` ```
To get the best performance select the largest memory available. To get the best performance select the largest memory available.
@ -302,7 +157,7 @@ custom:
- sharp - sharp
packagerOptions: packagerOptions:
scripts: scripts:
- npm install --arch=x64 --platform=linux sharp - npm install --force @sharpen/sharp-linux-x64
``` ```
## TypeScript ## TypeScript
@ -358,14 +213,7 @@ Module did not self-register
### Canvas and Windows ### Canvas and Windows
The prebuilt binaries provided by `canvas` for Windows If both `canvas` and `sharp` modules are used in the same Windows process, the following error may occur:
from v2.7.0 onwards depend on the Visual C++ Runtime (MSVCRT).
These conflict with the binaries provided by sharp,
which depend on the more modern Universal C Runtime (UCRT).
See [Automattic/node-canvas#2155](https://github.com/Automattic/node-canvas/issues/2155).
If both modules are used in the same Windows process, the following error will occur:
``` ```
The specified procedure could not be found. The specified procedure could not be found.
``` ```

View File

@ -1,14 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const libvips = require('../lib/libvips');
try {
if (!(libvips.useGlobalLibvips() || libvips.hasVendoredLibvips())) {
process.exitCode = 1;
}
} catch (err) {
process.exitCode = 1;
}

27
install/check.js Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const { useGlobalLibvips, globalLibvipsVersion, log, gypRebuild } = require('../lib/libvips');
const buildFromSource = (msg) => {
log(msg);
log('Attempting to build from source via node-gyp');
try {
require('node-gyp');
} catch (err) {
log('You might need to install node-gyp');
log('See https://sharp.pixelplumbing.com/install#building-from-source');
}
const status = gypRebuild();
if (status !== 0) {
process.exit(status);
}
};
if (process.env.npm_config_build_from_source) {
buildFromSource('Detected --build-from-source flag');
} else if (useGlobalLibvips()) {
buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
}

View File

@ -1,40 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
const path = require('path');
const libvips = require('../lib/libvips');
const platform = require('../lib/platform');
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
const platformAndArch = platform();
if (platformAndArch.startsWith('win32')) {
const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
libvips.log(`Creating ${buildReleaseDir}`);
try {
libvips.mkdirSync(buildReleaseDir);
} catch (err) {}
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch, 'lib');
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
try {
fs
.readdirSync(vendorLibDir)
.filter(function (filename) {
return /\.dll$/.test(filename);
})
.forEach(function (filename) {
fs.copyFileSync(
path.join(vendorLibDir, filename),
path.join(buildReleaseDir, filename)
);
});
} catch (err) {
libvips.log(err);
process.exit(1);
}
}

View File

@ -1,222 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const stream = require('stream');
const zlib = require('zlib');
const { createHash } = require('crypto');
const detectLibc = require('detect-libc');
const semverCoerce = require('semver/functions/coerce');
const semverLessThan = require('semver/functions/lt');
const semverSatisfies = require('semver/functions/satisfies');
const simpleGet = require('simple-get');
const tarFs = require('tar-fs');
const agent = require('../lib/agent');
const libvips = require('../lib/libvips');
const platform = require('../lib/platform');
const minimumGlibcVersionByArch = {
arm: '2.28',
arm64: '2.17',
x64: '2.17'
};
const hasSharpPrebuild = [
'darwin-x64',
'darwin-arm64',
'linux-arm64',
'linux-x64',
'linuxmusl-x64',
'linuxmusl-arm64',
'win32-ia32',
'win32-x64'
];
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
const fail = function (err) {
libvips.log(err);
if (err.code === 'EACCES') {
libvips.log('Are you trying to install as a root or sudo user?');
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
}
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
process.exit(1);
};
const handleError = function (err) {
if (installationForced) {
libvips.log(`Installation warning: ${err.message}`);
} else {
throw err;
}
};
const verifyIntegrity = function (platformAndArch) {
const expected = libvips.integrity(platformAndArch);
if (installationForced || !expected) {
libvips.log(`Integrity check skipped for ${platformAndArch}`);
return new stream.PassThrough();
}
const hash = createHash('sha512');
return new stream.Transform({
transform: function (chunk, _encoding, done) {
hash.update(chunk);
done(null, chunk);
},
flush: function (done) {
const digest = `sha512-${hash.digest('base64')}`;
if (expected !== digest) {
try {
libvips.removeVendoredLibvips();
} catch (err) {
libvips.log(err.message);
}
libvips.log(`Integrity expected: ${expected}`);
libvips.log(`Integrity received: ${digest}`);
done(new Error(`Integrity check failed for ${platformAndArch}`));
} else {
libvips.log(`Integrity check passed for ${platformAndArch}`);
done();
}
}
});
};
const extractTarball = function (tarPath, platformAndArch) {
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
libvips.mkdirSync(versionedVendorPath);
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
const ignore = function (name) {
return ignoreVendorInclude && name.includes('include/');
};
stream.pipeline(
fs.createReadStream(tarPath),
verifyIntegrity(platformAndArch),
new zlib.BrotliDecompress(),
tarFs.extract(versionedVendorPath, { ignore }),
function (err) {
if (err) {
if (/unexpected end of file/.test(err.message)) {
fail(new Error(`Please delete ${tarPath} as it is not a valid tarball`));
}
fail(err);
}
}
);
};
try {
const useGlobalLibvips = libvips.useGlobalLibvips();
if (useGlobalLibvips) {
const globalLibvipsVersion = libvips.globalLibvipsVersion();
libvips.log(`Detected globally-installed libvips v${globalLibvipsVersion}`);
libvips.log('Building from source via node-gyp');
process.exit(1);
} else if (libvips.hasVendoredLibvips()) {
libvips.log(`Using existing vendored libvips v${minimumLibvipsVersion}`);
} else {
// Is this arch/platform supported?
const arch = process.env.npm_config_arch || process.arch;
const platformAndArch = platform();
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
// Linux libc version check
const libcVersionRaw = detectLibc.versionSync();
if (libcVersionRaw) {
const libcFamily = detectLibc.familySync();
const libcVersion = semverCoerce(libcVersionRaw).version;
if (libcFamily === detectLibc.GLIBC && minimumGlibcVersionByArch[arch]) {
if (semverLessThan(libcVersion, semverCoerce(minimumGlibcVersionByArch[arch]).version)) {
handleError(new Error(`Use with glibc ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
}
}
if (libcFamily === detectLibc.MUSL) {
if (semverLessThan(libcVersion, '1.1.24')) {
handleError(new Error(`Use with musl ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
}
}
}
// Node.js minimum version check
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
}
// Download to per-process temporary file
const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br';
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
if (fs.existsSync(tarPathCache)) {
libvips.log(`Using cached ${tarPathCache}`);
extractTarball(tarPathCache, platformAndArch);
} else if (localLibvipsDir) {
// If localLibvipsDir is given try to use binaries from local directory
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
libvips.log(`Using local libvips from ${tarPathLocal}`);
extractTarball(tarPathLocal, platformAndArch);
} else {
const url = distBaseUrl + tarFilename;
libvips.log(`Downloading ${url}`);
simpleGet({ url: url, agent: agent(libvips.log) }, function (err, response) {
if (err) {
fail(err);
} else if (response.statusCode === 404) {
fail(new Error(`Prebuilt libvips ${minimumLibvipsVersion} binaries are not yet available for ${platformAndArch}`));
} else if (response.statusCode !== 200) {
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
} else {
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
const tmpFileStream = fs.createWriteStream(tarPathTemp);
response
.on('error', function (err) {
tmpFileStream.destroy(err);
})
.on('close', function () {
if (!response.complete) {
tmpFileStream.destroy(new Error('Download incomplete (connection was terminated)'));
}
})
.pipe(tmpFileStream);
tmpFileStream
.on('error', function (err) {
// Clean up temporary file
try {
fs.unlinkSync(tarPathTemp);
} catch (e) {}
fail(err);
})
.on('close', function () {
try {
// Attempt to rename
fs.renameSync(tarPathTemp, tarPathCache);
} catch (err) {
// Fall back to copy and unlink
fs.copyFileSync(tarPathTemp, tarPathCache);
fs.unlinkSync(tarPathTemp);
}
extractTarball(tarPathCache, platformAndArch);
});
}
});
}
}
} catch (err) {
fail(err);
}

View File

@ -1,44 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const url = require('url');
const tunnelAgent = require('tunnel-agent');
const is = require('./is');
const proxies = [
'HTTPS_PROXY',
'https_proxy',
'HTTP_PROXY',
'http_proxy',
'npm_config_https_proxy',
'npm_config_proxy'
];
function env (key) {
return process.env[key];
}
module.exports = function (log) {
try {
const proxy = new url.URL(proxies.map(env).find(is.string));
const tunnel = proxy.protocol === 'https:'
? tunnelAgent.httpsOverHttps
: tunnelAgent.httpsOverHttp;
const proxyAuth = proxy.username && proxy.password
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
: null;
log(`Via proxy ${proxy.protocol}//${proxy.hostname}:${proxy.port} ${proxyAuth ? 'with' : 'no'} credentials`);
return tunnel({
proxy: {
port: Number(proxy.port),
host: proxy.hostname,
proxyAuth
}
});
} catch (err) {
return null;
}
};

View File

@ -7,7 +7,6 @@ const util = require('util');
const stream = require('stream'); const stream = require('stream');
const is = require('./is'); const is = require('./is');
require('./libvips').hasVendoredLibvips();
require('./sharp'); require('./sharp');
// Use NODE_DEBUG=sharp to enable libvips warnings // Use NODE_DEBUG=sharp to enable libvips warnings

6
lib/index.d.ts vendored
View File

@ -91,12 +91,6 @@ declare namespace sharp {
zlib?: string | undefined; zlib?: string | undefined;
}; };
/** An Object containing the platform and architecture of the current and installed vendored binaries. */
const vendor: {
current: string;
installed: string[];
};
/** An Object containing the available interpolators and their proper values */ /** An Object containing the available interpolators and their proper values */
const interpolators: Interpolators; const interpolators: Interpolators;

View File

@ -3,53 +3,30 @@
'use strict'; 'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const spawnSync = require('child_process').spawnSync; const spawnSync = require('child_process').spawnSync;
const semverCoerce = require('semver/functions/coerce'); const semverCoerce = require('semver/functions/coerce');
const semverGreaterThanOrEqualTo = require('semver/functions/gte'); const semverGreaterThanOrEqualTo = require('semver/functions/gte');
const detectLibc = require('detect-libc');
const platform = require('./platform'); const { engines } = require('../package.json');
const { config } = require('../package.json');
const env = process.env; const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */ engines.libvips;
config.libvips;
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version; const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
const prebuiltPlatforms = [
'darwin-arm64', 'darwin-x64',
'linux-arm', 'linux-arm64', 'linux-x64',
'linuxmusl-arm64', 'linuxmusl-x64',
'win32-ia32', 'win32-x64'
];
const spawnSyncOptions = { const spawnSyncOptions = {
encoding: 'utf8', encoding: 'utf8',
shell: true shell: true
}; };
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform()); const log = (item) => {
const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath, { recursive: true });
} catch (err) {
/* istanbul ignore next */
if (err.code !== 'EEXIST') {
throw err;
}
}
};
const cachePath = function () {
const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
(env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
mkdirSync(npmCachePath);
const libvipsCachePath = path.join(npmCachePath, '_libvips');
mkdirSync(libvipsCachePath);
return libvipsCachePath;
};
const integrity = function (platformAndArch) {
return env[`npm_package_config_integrity_${platformAndArch.replace('-', '_')}`] || config.integrity[platformAndArch];
};
const log = function (item) {
if (item instanceof Error) { if (item instanceof Error) {
console.error(`sharp: Installation error: ${item.message}`); console.error(`sharp: Installation error: ${item.message}`);
} else { } else {
@ -57,7 +34,43 @@ const log = function (item) {
} }
}; };
const isRosetta = function () { /* istanbul ignore next */
const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
/* istanbul ignore next */
const buildPlatformArch = () => {
/* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
return `${npm_config_platform || process.platform}${npm_config_libc || runtimeLibc()}-${npm_config_arch || process.arch}`;
};
const buildSharpLibvipsIncludeDir = () => {
try {
return require('@sharpen/sharp-libvips-dev/include');
} catch {}
/* istanbul ignore next */
return '';
};
const buildSharpLibvipsCPlusPlusDir = () => {
try {
return require('@sharpen/sharp-libvips-dev/cplusplus');
} catch {}
/* istanbul ignore next */
return '';
};
const buildSharpLibvipsLibDir = () => {
try {
return require(`@sharpen/sharp-libvips-${buildPlatformArch()}/lib`);
} catch {}
/* istanbul ignore next */
return '';
};
const isRosetta = () => {
/* istanbul ignore next */ /* istanbul ignore next */
if (process.platform === 'darwin' && process.arch === 'x64') { if (process.platform === 'darwin' && process.arch === 'x64') {
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout; const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
@ -66,12 +79,19 @@ const isRosetta = function () {
return false; return false;
}; };
const globalLibvipsVersion = function () { /* istanbul ignore next */
const gypRebuild = () =>
spawnSync('node-gyp rebuild', {
...spawnSyncOptions,
stdio: 'inherit'
}).status;
const globalLibvipsVersion = () => {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', { const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
...spawnSyncOptions, ...spawnSyncOptions,
env: { env: {
...env, ...process.env,
PKG_CONFIG_PATH: pkgConfigPath() PKG_CONFIG_PATH: pkgConfigPath()
} }
}).stdout; }).stdout;
@ -82,17 +102,8 @@ const globalLibvipsVersion = function () {
} }
}; };
const hasVendoredLibvips = function () {
return fs.existsSync(vendorPath);
};
/* istanbul ignore next */ /* istanbul ignore next */
const removeVendoredLibvips = function () { const pkgConfigPath = () => {
fs.rmSync(vendorPath, { recursive: true, maxRetries: 3, force: true });
};
/* istanbul ignore next */
const pkgConfigPath = function () {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
const brewPkgConfigPath = spawnSync( const brewPkgConfigPath = spawnSync(
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2', 'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
@ -100,7 +111,7 @@ const pkgConfigPath = function () {
).stdout || ''; ).stdout || '';
return [ return [
brewPkgConfigPath.trim(), brewPkgConfigPath.trim(),
env.PKG_CONFIG_PATH, process.env.PKG_CONFIG_PATH,
'/usr/local/lib/pkgconfig', '/usr/local/lib/pkgconfig',
'/usr/lib/pkgconfig', '/usr/lib/pkgconfig',
'/usr/local/libdata/pkgconfig', '/usr/local/libdata/pkgconfig',
@ -111,8 +122,9 @@ const pkgConfigPath = function () {
} }
}; };
const useGlobalLibvips = function () { const useGlobalLibvips = () => {
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) { if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
log('Detected SHARP_IGNORE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips');
return false; return false;
} }
/* istanbul ignore next */ /* istanbul ignore next */
@ -127,14 +139,15 @@ const useGlobalLibvips = function () {
module.exports = { module.exports = {
minimumLibvipsVersion, minimumLibvipsVersion,
minimumLibvipsVersionLabelled, prebuiltPlatforms,
cachePath, buildPlatformArch,
integrity, buildSharpLibvipsIncludeDir,
buildSharpLibvipsCPlusPlusDir,
buildSharpLibvipsLibDir,
runtimePlatformArch,
log, log,
gypRebuild,
globalLibvipsVersion, globalLibvipsVersion,
hasVendoredLibvips,
removeVendoredLibvips,
pkgConfigPath, pkgConfigPath,
useGlobalLibvips, useGlobalLibvips
mkdirSync
}; };

View File

@ -1,30 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const detectLibc = require('detect-libc');
const env = process.env;
module.exports = function () {
const arch = env.npm_config_arch || process.arch;
const platform = env.npm_config_platform || process.platform;
const libc = process.env.npm_config_libc ||
/* istanbul ignore next */
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
const platformId = [`${platform}${libcId}`];
if (arch === 'arm') {
const fallback = process.versions.electron ? '7' : '6';
platformId.push(`armv${env.npm_config_arm_version || process.config.variables.arm_version || fallback}`);
} else if (arch === 'arm64') {
platformId.push(`arm64v${env.npm_config_arm_version || '8'}`);
} else {
platformId.push(arch);
}
return platformId.join('-');
};

View File

@ -3,36 +3,58 @@
'use strict'; 'use strict';
const platformAndArch = require('./platform')(); // Inspects the runtime environment and exports the relevant sharp.node binary
const { familySync, versionSync } = require('detect-libc');
const { runtimePlatformArch, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
const runtimePlatform = runtimePlatformArch();
/* istanbul ignore next */ /* istanbul ignore next */
try { try {
module.exports = require(`../build/Release/sharp-${platformAndArch}.node`); // Check for local build
} catch (err) { module.exports = require(`../build/Release/sharp-${runtimePlatform}.node`);
// Bail early if bindings aren't available } catch (errLocal) {
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:']; try {
if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) { // Check for runtime package
help.push('- Update Homebrew: "brew update && brew upgrade vips"'); module.exports = require(`@sharpen/sharp-${runtimePlatform}/sharp.node`);
} catch (errPackage) {
const help = ['Could not load the "sharp" module at runtime'];
if (errLocal.code !== 'MODULE_NOT_FOUND') {
help.push(`${errLocal.code}: ${errLocal.message}`);
}
if (errPackage.code !== 'MODULE_NOT_FOUND') {
help.push(`${errPackage.code}: ${errPackage.message}`);
}
help.push('Possible solutions:');
// Common error messages
if (prebuiltPlatforms.includes(runtimePlatform)) {
help.push(`- Add an explicit dependency for the runtime platform: "npm install --force @sharpen/sharp-${runtimePlatform}"`);
} else { } else {
const [platform, arch] = platformAndArch.split('-'); help.push(`- The ${runtimePlatform} platform requires manual installation of libvips >= ${minimumLibvipsVersion}`);
if (platform === 'linux' && /Module did not self-register/.test(err.message)) {
help.push('- Using worker threads? See https://sharp.pixelplumbing.com/install#worker-threads');
} }
help.push( if (runtimePlatform.startsWith('linux') && /symbol not found/i.test(errPackage)) {
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"', try {
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"` const { engines } = require(`@sharpen/sharp-libvips-${runtimePlatform}/package`);
); const libcFound = `${familySync()} ${versionSync()}`;
const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
help.push(`- Update your OS: found ${libcFound}, requires ${libcRequires}`);
} catch (errEngines) {}
} }
help.push( if (runtimePlatform.startsWith('darwin') && /Incompatible library version/.test(errLocal.message)) {
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install' help.push('- Update Homebrew: "brew update && brew upgrade vips"');
);
// Check loaded
if (process.platform === 'win32' || /symbol/.test(err.message)) {
const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
if (loadedModule) {
const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
help.push(`- Ensure the version of sharp aligns with the ${loadedPackage} package: "npm ls sharp"`);
} }
if (errPackage.code === 'ERR_DLOPEN_DISABLED') {
help.push('- Run Node.js without using the --no-addons flag');
}
// Link to installation docs
if (runtimePlatform.startsWith('linux') && /Module did not self-register/.test(errLocal.message + errPackage.message)) {
help.push('- Using worker threads on Linux? See https://sharp.pixelplumbing.com/install#worker-threads');
} else if (runtimePlatform.startsWith('win32') && /The specified procedure could not be found/.test(errPackage.message)) {
help.push('- Using the canvas package on Windows? See https://sharp.pixelplumbing.com/install#canvas-and-windows');
} else {
help.push('- Consult the installation documentation: https://sharp.pixelplumbing.com/install');
} }
throw new Error(help.join('\n')); throw new Error(help.join('\n'));
}
} }

View File

@ -3,13 +3,11 @@
'use strict'; 'use strict';
const fs = require('fs');
const path = require('path');
const events = require('events'); const events = require('events');
const detectLibc = require('detect-libc'); const detectLibc = require('detect-libc');
const is = require('./is'); const is = require('./is');
const platformAndArch = require('./platform')(); const { runtimePlatformArch } = require('./libvips');
const sharp = require('./sharp'); const sharp = require('./sharp');
/** /**
@ -55,25 +53,14 @@ let versions = {
vips: sharp.libvipsVersion() vips: sharp.libvipsVersion()
}; };
try { try {
versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`); versions = require(`@sharpen/sharp-${runtimePlatformArch()}/versions`);
} catch (_err) { /* ignore */ } } catch (_) {
try {
versions = require(`@sharpen/sharp-libvips-${runtimePlatformArch()}/versions`);
} catch (_) {}
}
versions.sharp = require('../package.json').version; versions.sharp = require('../package.json').version;
/**
* An Object containing the platform and architecture
* of the current and installed vendored binaries.
* @member
* @example
* console.log(sharp.vendor);
*/
const vendor = {
current: platformAndArch,
installed: []
};
try {
vendor.installed = fs.readdirSync(path.join(__dirname, `../vendor/${versions.vips}`));
} catch (_err) { /* ignore */ }
/** /**
* Gets or, when options are provided, sets the limits of _libvips'_ operation cache. * Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
* Existing entries in the cache will be trimmed after any change in limits. * Existing entries in the cache will be trimmed after any change in limits.
@ -280,7 +267,6 @@ module.exports = function (Sharp) {
Sharp.format = format; Sharp.format = format;
Sharp.interpolators = interpolators; Sharp.interpolators = interpolators;
Sharp.versions = versions; Sharp.versions = versions;
Sharp.vendor = vendor;
Sharp.queue = queue; Sharp.queue = queue;
Sharp.block = block; Sharp.block = block;
Sharp.unblock = unblock; Sharp.unblock = unblock;

View File

@ -0,0 +1,42 @@
{
"name": "@sharpen/sharp-darwin-arm64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with macOS ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/darwin-arm64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-darwin-arm64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.26"
},
"os": [
"darwin"
],
"cpu": [
"arm64"
]
}

View File

@ -0,0 +1,39 @@
{
"name": "@sharpen/sharp-darwin-x64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with macOS x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/darwin-x64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"exports": {
"./sharp.node": "./lib/sharp-darwin-x64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.26"
},
"os": [
"darwin"
],
"cpu": [
"x64"
]
}

View File

@ -0,0 +1,51 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
// Populate contents of all packages with the current GitHub release
const { writeFile, copyFile } = require('node:fs/promises');
const path = require('node:path');
const { Readable } = require('node:stream');
const { pipeline } = require('node:stream/promises');
const { createGunzip } = require('node:zlib');
const { extract } = require('tar-fs');
const { workspaces } = require('./package.json');
const { version } = require('../package.json');
const mapTarballEntry = (header) => {
header.name = path.basename(header.name);
return header;
};
workspaces.map(async platform => {
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v7-${platform}.tar.gz`;
const dir = path.join(__dirname, platform);
const response = await fetch(url);
if (!response.ok) {
console.log(`Skipping ${platform}: ${response.statusText}`);
return;
}
// Extract prebuild tarball
await pipeline(
Readable.fromWeb(response.body),
createGunzip(),
extract(path.join(dir, 'lib'), { map: mapTarballEntry })
);
// Generate README
const { name, description } = require(`./${platform}/package.json`);
await writeFile(path.join(dir, 'README.md'), `# ${name}\n${description}`);
// Copy Apache-2.0 LICENSE
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, 'LICENSE'));
// Copy Windows-specific files
if (platform.startsWith('win32-')) {
const sharpLibvipsDir = path.join(require(`@sharpen/sharp-libvips-${platform}/lib`), '..');
await Promise.all(
['versions.json', 'THIRD-PARTY-NOTICES.md'].map(
filename => copyFile(path.join(sharpLibvipsDir, filename), path.join(dir, filename))
)
);
}
});

26
npm/from-local-build.js Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
// Populate contents of a single npm/sharpen-sharp-<build-platform> package
// with the local/CI build directory for local/CI prebuild testing
const fs = require('node:fs');
const path = require('node:path');
const { buildPlatformArch } = require('../lib/libvips');
const platform = buildPlatformArch();
const dest = path.join(__dirname, platform);
// Use same config as prebuild to copy binary files
const release = path.join(__dirname, '..', 'build', 'Release');
const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8'));
const include = new RegExp(prebuildrc['include-regex'], 'i');
fs.cpSync(release, path.join(dest, 'lib'), {
recursive: true,
filter: (file) => {
const name = path.basename(file);
return name === 'Release' || include.test(name);
}
});

View File

@ -0,0 +1,45 @@
{
"name": "@sharpen/sharp-linux-arm",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linux-arm"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-linux-arm.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.28"
},
"os": [
"linux"
],
"libc": [
"glibc"
],
"cpu": [
"arm"
]
}

View File

@ -0,0 +1,45 @@
{
"name": "@sharpen/sharp-linux-arm64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Linux (glibc) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linux-arm64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-linux-arm64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.26"
},
"os": [
"linux"
],
"libc": [
"glibc"
],
"cpu": [
"arm64"
]
}

View File

@ -0,0 +1,45 @@
{
"name": "@sharpen/sharp-linux-x64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Linux (glibc) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linux-x64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-linux-x64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"glibc": ">=2.26"
},
"os": [
"linux"
],
"libc": [
"glibc"
],
"cpu": [
"x64"
]
}

View File

@ -0,0 +1,45 @@
{
"name": "@sharpen/sharp-linuxmusl-arm64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Linux (musl) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linuxmusl-arm64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"musl": ">=1.2.2"
},
"os": [
"linux"
],
"libc": [
"musl"
],
"cpu": [
"arm64"
]
}

View File

@ -0,0 +1,45 @@
{
"name": "@sharpen/sharp-linuxmusl-x64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Linux (musl) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/linuxmusl-x64"
},
"license": "Apache-2.0",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0",
"musl": ">=1.2.2"
},
"os": [
"linux"
],
"libc": [
"musl"
],
"cpu": [
"x64"
]
}

16
npm/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "@sharpen/sharp",
"version": "0.0.1-alpha.1",
"private": "true",
"workspaces": [
"darwin-x64",
"darwin-arm64",
"linux-arm",
"linux-arm64",
"linuxmusl-arm64",
"linuxmusl-x64",
"linux-x64",
"win32-ia32",
"win32-x64"
]
}

View File

@ -0,0 +1,41 @@
{
"name": "@sharpen/sharp-win32-ia32",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/win32-ia32"
},
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-win32-ia32.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0"
},
"os": [
"win32"
],
"cpu": [
"ia32"
]
}

View File

@ -0,0 +1,41 @@
{
"name": "@sharpen/sharp-win32-x64",
"version": "0.0.1-alpha.1",
"description": "Prebuilt sharp for use with Windows x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/win32-x64"
},
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"exports": {
"./sharp.node": "./lib/sharp-win32-x64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"npm": ">=9.6.5",
"yarn": ">=3.2.0",
"pnpm": ">=7.1.0"
},
"os": [
"win32"
],
"cpu": [
"x64"
]
}

View File

@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.32.6", "version": "0.33.0-alpha.1",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp", "homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
@ -89,14 +89,16 @@
"Lachlan Newman <lachnewman007@gmail.com>" "Lachlan Newman <lachnewman007@gmail.com>"
], ],
"scripts": { "scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)", "install": "node install/check",
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*", "clean": "rm -rf build/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types", "test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
"test-lint": "semistandard && cpplint", "test-lint": "semistandard && cpplint",
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha", "test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"", "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-types": "tsd", "test-types": "tsd",
"package-from-local-build": "node npm/from-local-build",
"package-from-github-release": "node npm/from-github-release",
"docs-build": "node docs/build && node docs/search-index/build", "docs-build": "node docs/build && node docs/search-index/build",
"docs-serve": "cd docs && npx serve", "docs-serve": "cd docs && npx serve",
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp" "docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
@ -111,7 +113,7 @@
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/lovell/sharp" "url": "git://github.com/lovell/sharp.git"
}, },
"keywords": [ "keywords": [
"jpeg", "jpeg",
@ -135,13 +137,30 @@
"color": "^4.2.3", "color": "^4.2.3",
"detect-libc": "^2.0.2", "detect-libc": "^2.0.2",
"node-addon-api": "^7.0.0", "node-addon-api": "^7.0.0",
"prebuild-install": "^7.1.1", "semver": "^7.5.4"
"semver": "^7.5.4", },
"simple-get": "^4.0.1", "optionalDependencies": {
"tar-fs": "^3.0.4", "@sharpen/sharp-darwin-arm64": "0.0.1-alpha.1",
"tunnel-agent": "^0.6.0" "@sharpen/sharp-darwin-x64": "0.0.1-alpha.1",
"@sharpen/sharp-linux-arm": "0.0.1-alpha.1",
"@sharpen/sharp-linux-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-linux-x64": "0.0.1-alpha.1",
"@sharpen/sharp-linuxmusl-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-linuxmusl-x64": "0.0.1-alpha.1",
"@sharpen/sharp-win32-ia32": "0.0.1-alpha.1",
"@sharpen/sharp-win32-x64": "0.0.1-alpha.1"
}, },
"devDependencies": { "devDependencies": {
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-dev": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.1",
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.1",
"@types/node": "*", "@types/node": "*",
"async": "^3.2.4", "async": "^3.2.4",
"cc": "^3.0.1", "cc": "^3.0.1",
@ -151,33 +170,16 @@
"jsdoc-to-markdown": "^8.0.0", "jsdoc-to-markdown": "^8.0.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"mocha": "^10.2.0", "mocha": "^10.2.0",
"mock-fs": "^5.2.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"prebuild": "^12.1.0", "prebuild": "^12.1.0",
"semistandard": "^16.0.1", "semistandard": "^16.0.1",
"tar-fs": "^3.0.4",
"tsd": "^0.29.0" "tsd": "^0.29.0"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": {
"libvips": "8.14.5",
"integrity": {
"darwin-arm64v8": "sha512-1QZzICfCJd4wAO0P6qmYI5e5VFMt9iCE4QgefI8VMMbdSzjIXA9L/ARN6pkMQPZ3h20Y9RtJ2W1skgCsvCIccw==",
"darwin-x64": "sha512-sMIKMYXsdU9FlIfztj6Kt/SfHlhlDpP0Ups7ftVFqwjaszmYmpI9y/d/q3mLb4jrzuSiSUEislSWCwBnW7MPTw==",
"linux-arm64v8": "sha512-CD8owELzkDumaom+O3jJ8fKamILAQdj+//KK/VNcHK3sngUcFpdjx36C8okwbux9sml/T7GTB/gzpvReDrAejQ==",
"linux-armv6": "sha512-wk6IPHatDFVWKJy7lI1TJezHGHPQut1wF2bwx256KlZwXUQU3fcVcMpV1zxXjgLFewHq2+uhyMkoSGBPahWzlA==",
"linux-armv7": "sha512-HEZC9KYtkmBK5rUR2MqBhrVarnQVZ/TwLUeLkKq0XuoM2pc/eXI6N0Fh5NGEFwdXI2XE8g1ySf+OYS6DDi+xCQ==",
"linux-x64": "sha512-SlFWrITSW5XVUkaFPQOySAaSGXnhkGJCj8X2wGYYta9hk5piZldQyMp4zwy0z6UeRu1qKTKtZvmq28W3Gnh9xA==",
"linuxmusl-arm64v8": "sha512-ga9iX7WUva3sG/VsKkOD318InLlCfPIztvzCZKZ2/+izQXRbQi8VoXWMHgEN4KHACv45FTl7mJ/8CRqUzhS8wQ==",
"linuxmusl-x64": "sha512-yeaHnpfee1hrZLok2l4eFceHzlfq8gN3QOu0R4Mh8iMK5O5vAUu97bdtxeZZeJJvHw8tfh2/msGi0qysxKN8bw==",
"win32-arm64v8": "sha512-kR91hy9w1+GEXK56hLh51+hBCBo7T+ijM4Slkmvb/2PsYZySq5H7s61n99iDYl6kTJP2y9sW5Xcvm3uuXDaDgg==",
"win32-ia32": "sha512-HrnofEbzHNpHJ0vVnjsTj5yfgVdcqdWshXuwFO2zc8xlEjA83BvXZ0lVj9MxPxkxJ2ta+/UlLr+CFzc5bOceMw==",
"win32-x64": "sha512-BwKckinJZ0Fu/EcunqiLPwOLEBWp4xf8GV7nvmVuKKz5f6B+GxoA2k9aa2wueqv4r4RJVgV/aWXZWFKOIjre/Q=="
},
"runtime": "napi",
"target": 9
},
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
"libvips": ">=8.14.5"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"

View File

@ -1,151 +0,0 @@
/* Object part of the VSource and VTarget class
*/
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/vips8>
#include <vips/debug.h>
/*
#define VIPS_DEBUG
#define VIPS_DEBUG_VERBOSE
*/
VIPS_NAMESPACE_START
VSource
VSource::new_from_descriptor( int descriptor )
{
VipsSource *input;
if( !(input = vips_source_new_from_descriptor( descriptor )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_file( const char *filename )
{
VipsSource *input;
if( !(input = vips_source_new_from_file( filename )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_blob( VipsBlob *blob )
{
VipsSource *input;
if( !(input = vips_source_new_from_blob( blob )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_memory( const void *data,
size_t size )
{
VipsSource *input;
if( !(input = vips_source_new_from_memory( data, size )) )
throw VError();
VSource out( input );
return( out );
}
VSource
VSource::new_from_options( const char *options )
{
VipsSource *input;
if( !(input = vips_source_new_from_options( options )) )
throw VError();
VSource out( input );
return( out );
}
VTarget
VTarget::new_to_descriptor( int descriptor )
{
VipsTarget *output;
if( !(output = vips_target_new_to_descriptor( descriptor )) )
throw VError();
VTarget out( output );
return( out );
}
VTarget
VTarget::new_to_file( const char *filename )
{
VipsTarget *output;
if( !(output = vips_target_new_to_file( filename )) )
throw VError();
VTarget out( output );
return( out );
}
VTarget
VTarget::new_to_memory()
{
VipsTarget *output;
if( !(output = vips_target_new_to_memory()) )
throw VError();
VTarget out( output );
return( out );
}
VIPS_NAMESPACE_END

View File

@ -1,49 +0,0 @@
// Code for error type
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/vips8>
VIPS_NAMESPACE_START
std::ostream &operator<<( std::ostream &file, const VError &err )
{
err.ostream_print( file );
return( file );
}
void VError::ostream_print( std::ostream &file ) const
{
file << _what;
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +0,0 @@
/* Object part of VInterpolate class
*/
/*
Copyright (C) 1991-2001 The National Gallery
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/vips8>
#include <vips/debug.h>
/*
#define VIPS_DEBUG
#define VIPS_DEBUG_VERBOSE
*/
VIPS_NAMESPACE_START
VInterpolate
VInterpolate::new_from_name( const char *name, VOption *options )
{
VipsInterpolate *interp;
if( !(interp = vips_interpolate_new( name )) ) {
delete options;
throw VError();
}
delete options;
VInterpolate out( interp );
return( out );
}
VIPS_NAMESPACE_END

View File

@ -1,27 +0,0 @@
// Object part of VRegion class
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/vips8>
#include <vips/debug.h>
VIPS_NAMESPACE_START
VRegion
VRegion::new_from_image( VImage image )
{
VipsRegion *region;
if( !(region = vips_region_new( image.get_image() )) ) {
throw VError();
}
VRegion out( region );
return( out );
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@ -73,8 +73,6 @@ readableStream.pipe(transformer).pipe(writableStream);
console.log(sharp.format); console.log(sharp.format);
console.log(sharp.versions); console.log(sharp.versions);
console.log(sharp.vendor.current);
console.log(sharp.vendor.installed.join(', '));
sharp.queue.on('change', (queueLength: number) => { sharp.queue.on('change', (queueLength: number) => {
console.log(`Queue contains ${queueLength} task(s)`); console.log(`Queue contains ${queueLength} task(s)`);

View File

@ -1,52 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const assert = require('assert');
const agent = require('../../lib/agent');
describe('HTTP agent', function () {
it('Without proxy', function () {
assert.strictEqual(null, agent());
});
it('HTTPS proxy with auth from HTTPS_PROXY', function () {
process.env.HTTPS_PROXY = 'https://user:pass@secure:123';
let logMsg = '';
const proxy = agent(msg => { logMsg = msg; });
delete process.env.HTTPS_PROXY;
assert.strictEqual('object', typeof proxy);
assert.strictEqual('secure', proxy.options.proxy.host);
assert.strictEqual(123, proxy.options.proxy.port);
assert.strictEqual('user:pass', proxy.options.proxy.proxyAuth);
assert.strictEqual(443, proxy.defaultPort);
assert.strictEqual(logMsg, 'Via proxy https://secure:123 with credentials');
});
it('HTTPS proxy with auth from HTTPS_PROXY using credentials containing special characters', function () {
process.env.HTTPS_PROXY = 'https://user,:pass=@secure:789';
let logMsg = '';
const proxy = agent(msg => { logMsg = msg; });
delete process.env.HTTPS_PROXY;
assert.strictEqual('object', typeof proxy);
assert.strictEqual('secure', proxy.options.proxy.host);
assert.strictEqual(789, proxy.options.proxy.port);
assert.strictEqual('user,:pass=', proxy.options.proxy.proxyAuth);
assert.strictEqual(443, proxy.defaultPort);
assert.strictEqual(logMsg, 'Via proxy https://secure:789 with credentials');
});
it('HTTP proxy without auth from npm_config_proxy', function () {
process.env.npm_config_proxy = 'http://plaintext:456';
let logMsg = '';
const proxy = agent(msg => { logMsg = msg; });
delete process.env.npm_config_proxy;
assert.strictEqual('object', typeof proxy);
assert.strictEqual('plaintext', proxy.options.proxy.host);
assert.strictEqual(456, proxy.options.proxy.port);
assert.strictEqual(null, proxy.options.proxy.proxyAuth);
assert.strictEqual(443, proxy.defaultPort);
assert.strictEqual(logMsg, 'Via proxy http://plaintext:456 no credentials');
});
});

View File

@ -7,7 +7,6 @@ const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
const semver = require('semver'); const semver = require('semver');
const libvips = require('../../lib/libvips'); const libvips = require('../../lib/libvips');
const mockFS = require('mock-fs');
const originalPlatform = process.platform; const originalPlatform = process.platform;
@ -59,10 +58,6 @@ describe('libvips binaries', function () {
assert.strictEqual('string', typeof minimumLibvipsVersion); assert.strictEqual('string', typeof minimumLibvipsVersion);
assert.notStrictEqual(null, semver.valid(minimumLibvipsVersion)); assert.notStrictEqual(null, semver.valid(minimumLibvipsVersion));
}); });
it('hasVendoredLibvips returns a boolean', function () {
const hasVendoredLibvips = libvips.hasVendoredLibvips();
assert.strictEqual('boolean', typeof hasVendoredLibvips);
});
it('useGlobalLibvips can be ignored via an env var', function () { it('useGlobalLibvips can be ignored via an env var', function () {
process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1; process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1;
@ -71,62 +66,48 @@ describe('libvips binaries', function () {
delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS; delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
}); });
it('cachePath returns a valid path ending with _libvips', function () { });
const cachePath = libvips.cachePath();
assert.strictEqual('string', typeof cachePath); describe('Build time platform detection', () => {
assert.strictEqual('_libvips', cachePath.substr(-8)); it('Can override platform with npm_config_platform and npm_config_libc', () => {
assert.strictEqual(true, fs.existsSync(cachePath)); process.env.npm_config_platform = 'testplatform';
process.env.npm_config_libc = 'testlibc';
const [platform] = libvips.buildPlatformArch().split('-');
assert.strictEqual(platform, 'testplatformtestlibc');
delete process.env.npm_config_platform;
delete process.env.npm_config_libc;
});
it('Can override arch with npm_config_arch', () => {
process.env.npm_config_arch = 'test';
const [, arch] = libvips.buildPlatformArch().split('-');
assert.strictEqual(arch, 'test');
delete process.env.npm_config_arch;
}); });
}); });
describe('integrity', function () { describe('Build time directories', () => {
it('reads value from environment variable', function () { it('sharp-libvips include', () => {
const prev = process.env.npm_package_config_integrity_platform_arch; const dir = libvips.buildSharpLibvipsIncludeDir();
process.env.npm_package_config_integrity_platform_arch = 'sha512-test'; assert.strictEqual(fs.statSync(dir).isDirectory(), true);
const integrity = libvips.integrity('platform-arch');
assert.strictEqual('sha512-test', integrity);
process.env.npm_package_config_integrity_platform_arch = prev;
}); });
it('reads value from package.json', function () { it('sharp-libvips cplusplus', () => {
const prev = process.env.npm_package_config_integrity_linux_x64; const dir = libvips.buildSharpLibvipsCPlusPlusDir();
delete process.env.npm_package_config_integrity_linux_x64; assert.strictEqual(fs.statSync(dir).isDirectory(), true);
});
const integrity = libvips.integrity('linux-x64'); it('sharp-libvips lib', () => {
assert.strictEqual('sha512-', integrity.substr(0, 7)); const dir = libvips.buildSharpLibvipsLibDir();
assert.strictEqual(fs.statSync(dir).isDirectory(), true);
process.env.npm_package_config_integrity_linux_x64 = prev;
}); });
}); });
describe('safe directory creation', function () { describe('Runtime detection', () => {
before(function () { it('platform', () => {
mockFS({ const [platform] = libvips.runtimePlatformArch().split('-');
exampleDirA: { assert.strict(['darwin', 'linux', 'linuxmusl', 'win32'].includes(platform));
exampleDirB: {
exampleFile: 'Example test file'
}
}
}); });
}); it('arch', () => {
after(function () { mockFS.restore(); }); const [, arch] = libvips.runtimePlatformArch().split('-');
assert.strict(['arm', 'arm64', 'ia32', 'x64'].includes(arch));
it('mkdirSync creates a directory', function () {
const dirPath = 'createdDir';
libvips.mkdirSync(dirPath);
assert.strictEqual(true, fs.existsSync(dirPath));
});
it('mkdirSync does not throw error or overwrite an existing dir', function () {
const dirPath = 'exampleDirA';
const nestedDirPath = 'exampleDirA/exampleDirB';
assert.strictEqual(true, fs.existsSync(dirPath));
libvips.mkdirSync(dirPath);
assert.strictEqual(true, fs.existsSync(dirPath));
assert.strictEqual(true, fs.existsSync(nestedDirPath));
}); });
}); });

View File

@ -1,91 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
const assert = require('assert');
const platform = require('../../lib/platform');
describe('Platform-detection', function () {
it('Can override arch with npm_config_arch', function () {
process.env.npm_config_arch = 'test';
assert.strictEqual('test', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
it('Can override platform with npm_config_platform', function () {
process.env.npm_config_platform = 'test';
assert.strictEqual('test', platform().split('-')[0]);
delete process.env.npm_config_platform;
});
it('Can override ARM version via --arm-version', function () {
process.env.npm_config_arch = 'arm';
process.env.npm_config_arm_version = 'test';
assert.strictEqual('armvtest', platform().split('-')[1]);
delete process.env.npm_config_arm_version;
delete process.env.npm_config_arch;
});
it('Can override ARM64 version via --arm-version', function () {
process.env.npm_config_arch = 'arm64';
process.env.npm_config_arm_version = 'test';
assert.strictEqual('arm64vtest', platform().split('-')[1]);
delete process.env.npm_config_arm_version;
delete process.env.npm_config_arch;
});
if (process.config.variables.arm_version) {
it('Can detect ARM version via process.config', function () {
process.env.npm_config_arch = 'arm';
assert.strictEqual(`armv${process.config.variables.arm_version}`, platform().split('-')[1]);
delete process.env.npm_config_arch;
});
}
if (!process.config.variables.arm_version) {
it('Defaults to ARMv6 for 32-bit', function () {
process.env.npm_config_arch = 'arm';
assert.strictEqual('armv6', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
}
it('Defaults to ARMv8 for 64-bit', function () {
process.env.npm_config_arch = 'arm64';
assert.strictEqual('arm64v8', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
it('Can ensure version ARMv7 if electron version is present', function () {
process.env.npm_config_arch = 'arm';
process.versions.electron = 'test';
assert.strictEqual('armv7', platform().split('-')[1]);
delete process.env.npm_config_arch;
delete process.versions.electron;
});
it('Can override libc if platform is linux', function () {
process.env.npm_config_platform = 'linux';
process.env.npm_config_libc = 'test';
assert.strictEqual('linuxtest', platform().split('-')[0]);
delete process.env.npm_config_platform;
delete process.env.npm_config_libc;
});
it('Handles libc value "glibc" as default linux', function () {
process.env.npm_config_platform = 'linux';
process.env.npm_config_libc = 'glibc';
assert.strictEqual('linux', platform().split('-')[0]);
delete process.env.npm_config_platform;
delete process.env.npm_config_libc;
});
it('Discards libc value on non-linux platform', function () {
process.env.npm_config_platform = 'win32';
process.env.npm_config_libc = 'gnuwin32';
assert.strictEqual('win32', platform().split('-')[0]);
delete process.env.npm_config_platform;
delete process.env.npm_config_libc;
});
});

View File

@ -146,14 +146,6 @@ describe('Utilities', function () {
}); });
}); });
describe('Vendor', function () {
it('Contains expected attributes', function () {
assert.strictEqual('object', typeof sharp.vendor);
assert.strictEqual('string', typeof sharp.vendor.current);
assert.strictEqual(true, Array.isArray(sharp.vendor.installed));
});
});
describe('Block', () => { describe('Block', () => {
it('Can block a named operation', () => { it('Can block a named operation', () => {
sharp.block({ operation: ['test'] }); sharp.block({ operation: ['test'] });