Compare commits

...

31 Commits

Author SHA1 Message Date
Lovell Fuller
ab00e34d0d Prerelease 0.33.0-alpha.7 2023-10-10 14:47:13 +01:00
Lovell Fuller
7c2f883b67 CI: add pnpm and yarn to npm smoke tests 2023-10-10 11:15:55 +01:00
Lovell Fuller
9b5eecba8f Yarn pnp unsupported, use alternative nodeLinker 2023-10-10 11:14:39 +01:00
Lovell Fuller
4b028edfe9 Revert "Remove extraneous rpath"
This reverts commit 3f54ef7525.

The filesystem layout used by pnpm means that a nesting of 2
directories deep is used in the wild and must remain.
2023-10-10 09:56:07 +01:00
Lovell Fuller
7e25dd7be1 Ensure versions prop detects use of global libvips 2023-10-10 09:18:25 +01:00
Lovell Fuller
09460d7af4 CI: use correct version of write-file action 2023-10-09 13:36:03 +01:00
Lovell Fuller
3b22640077 Use std::snprintf to concat libvips version parts 2023-10-09 13:35:20 +01:00
Lovell Fuller
946976ba8f Prerelease 0.33.0-alpha.6 2023-10-09 10:46:16 +01:00
Lovell Fuller
29a83250a4 CI: Add smoke test for npm-published package
These tests will fail on their first run as a tag will exist but
the matching packages will not yet have been published.

The workflow can be manually re-run after publishing to npm.

Eventually, when there's some form of automated publishing, this
logic can become part of the post-publish checks.
2023-10-09 07:59:47 +01:00
Lovell Fuller
3f54ef7525 Remove extraneous rpath
The sharp.node shared library should now always end up being
nested either 3 subdirectories deep into the package root or exist
within same namespace of the node_modules tree.
2023-10-09 07:58:14 +01:00
Lovell Fuller
4dc2467f49 CI: simplify prebuild file copying 2023-10-08 23:07:02 +01:00
Lovell Fuller
f54ceb0973 CI: create prebuilds in src directory 2023-10-08 21:49:58 +01:00
Lovell Fuller
8d033ae341 Prerelease 0.33.0-alpha.5 2023-10-08 20:27:42 +01:00
Lovell Fuller
f7da2e5970 Building from source now requires node-addon-api in dependencies
If you want to build sharp from source against a globally-installed
libvips then you will now need to add both node-addon-api and
node-gyp to the dependencies section of your package.json file.

The binding.gyp file is "hidden" inside the src directory to
prevent various build and package manager tooling from assuming
that everyone is going to build from source every time.
2023-10-08 15:39:02 +01:00
Lovell Fuller
51e56f994c Package managers cannot handle nested optionalDependencies 2023-10-07 11:09:45 +01:00
Lovell Fuller
68ac12292c Prerelease 0.33.0-alpha.4 2023-10-06 15:02:19 +01:00
Lovell Fuller
a9aa7339ce Prerelease 0.33.0-alpha.3 2023-10-06 09:48:44 +01:00
Lovell Fuller
08108f5fad Use std::once for vips_init call 2023-10-06 09:44:57 +01:00
Lovell Fuller
58e3c4c70e Ensure all package files make use of commonjs explicit 2023-10-05 20:00:32 +01:00
Lovell Fuller
f8cf25ca56 Ensure correct interp of 16-bit raw input #3808 2023-10-05 14:05:36 +01:00
Lovell Fuller
ca95979ecc Prefix node builtins, skips cache lookup 2023-10-05 12:09:19 +01:00
Lovell Fuller
392f6afb5e Commit fcc7e84 but do it properly this time 2023-10-04 21:30:05 +01:00
Lovell Fuller
7c97aabaf8 Ensure win32 packages contain version/notice files 2023-10-04 21:12:16 +01:00
Lovell Fuller
fcc7e84bee Revert target names as these are used for DLLs 2023-10-04 20:46:25 +01:00
Lovell Fuller
226a9a13ef Packaging: prerelease version bump 2023-10-04 15:48:23 +01:00
Lovell Fuller
4d3c9ae3d1 Packaging: clear existing lib directories 2023-10-04 15:47:57 +01:00
Lovell Fuller
f31011d759 CI: increase linux-arm timeout 2023-10-04 15:47:35 +01:00
Lovell Fuller
7cf4ae5648 Prerelease 0.33.0-alpha.2 2023-10-04 10:16:35 +01:00
Lovell Fuller
9161c605e1 Clarify extract-resize-extract operation ordering 2023-10-03 19:28:18 +01:00
Lovell Fuller
70ac6905c7 Use std::atomic for counters 2023-09-30 14:01:04 +01:00
Lovell Fuller
265d70111a Add licensing info to npm readme files 2023-09-28 15:42:13 +01:00
38 changed files with 376 additions and 155 deletions

View File

@@ -42,7 +42,7 @@ jobs:
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 \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-glibc-node-20:
resource_class: arm.medium
machine:
@@ -83,7 +83,7 @@ jobs:
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 \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-musl-node-20:
resource_class: arm.medium
machine:

View File

@@ -114,7 +114,10 @@ jobs:
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: npx prebuild
run: |
node -e "require('fs').cpSync('package.json', 'src/package.json')"
cd src
npx prebuild
github-runner-qemu:
permissions:
contents: write
@@ -141,8 +144,8 @@ jobs:
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
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
[[ -n $prebuild_upload ]] && cd src && ln -s ../package.json && npx prebuild || true
macstadium-runner:
permissions:
contents: write
@@ -187,4 +190,4 @@ jobs:
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: npx prebuild
run: cd src && ln -s ../package.json && npx prebuild

100
.github/workflows/npm.yml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: "CI: npm smoke test"
on:
push:
tags:
- "v**"
permissions: {}
jobs:
release-smoke-test:
name: "${{ github.ref }} ${{ matrix.name }}"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: linux-x64
os: ubuntu-22.04
- name: darwin-x64
os: macos-11
- name: win32-x64
os: windows-2019
steps:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Install Bun
if: ${{ !contains(matrix.os, 'windows') }}
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Version
id: version
uses: actions/github-script@v6
with:
script: |
core.setOutput('semver', context.ref.replace('refs/tags/v',''))
- name: Create package.json
uses: DamianReeves/write-file-action@v1.2
with:
path: package.json
contents: |
{
"dependencies": {
"sharp": "${{ steps.version.outputs.semver }}"
}
}
- name: Create release.mjs
uses: DamianReeves/write-file-action@v1.2
with:
path: release.mjs
contents: |
import { createRequire } from 'node:module';
import { deepStrictEqual } from 'node:assert';
const require = createRequire(import.meta.url);
const sharp = require('sharp');
deepStrictEqual(['.jpg', '.jpeg', '.jpe'], sharp.format.jpeg.input.fileSuffix);
- name: Run with Node.js + npm
run: |
npm install --ignore-scripts
node release.mjs
rm -r node_modules/ package-lock.json
- name: Run with Node.js + pnpm
run: |
pnpm install --ignore-scripts
node release.mjs
rm -r node_modules/ pnpm-lock.yaml
- name: Run with Node.js + yarn
run: |
corepack enable
yarn set version stable
yarn config set enableScripts false
yarn config set nodeLinker node-modules
yarn install
node release.mjs
rm -r node_modules/ .yarn/ yarn.lock .yarnrc.yml
corepack disable
- name: Run with Deno
run: deno run --allow-read --allow-ffi release.mjs
- name: Run with Bun
if: ${{ !contains(matrix.os, 'windows') }}
run: |
bun install --ignore-scripts
bun release.mjs

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build
src/build
src/node_modules
node_modules
/coverage
npm/*/*

View File

@@ -214,7 +214,7 @@ Extract/crop a region of the image.
- Use `extract` before `resize` for pre-resize extraction.
- Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both.
- Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
**Throws**:

View File

@@ -1,7 +1,8 @@
## versions
> versions
An Object containing the version numbers of sharp, libvips and its dependencies.
An Object containing the version numbers of sharp, libvips
and (when using prebuilt binaries) its dependencies.
**Example**

View File

@@ -8,11 +8,18 @@ Requires libvips v8.14.5
* Drop support for Node.js 14 and 16, now requires Node.js >= 18.17.0
* Prebuilt binaries distributed via npm registry and installed via package manager.
* Building from source requires dependency on `node-addon-api`.
* Remove `sharp.vendor`.
* Make `compression` option of `heif` mandatory to help reduce HEIF vs HEIC confusion.
[#3740](https://github.com/lovell/sharp/issues/3740)
* Ensure correct interpretation of 16-bit raw input.
[#3808](https://github.com/lovell/sharp/issues/3808)
## v0.32 - *flow*
Requires libvips v8.14.5

View File

@@ -5,13 +5,15 @@ npm install sharp
```
```sh
yarn add sharp
pnpm add sharp
```
```sh
pnpm add sharp
yarn add sharp
```
Yarn Plug'n'Play is unsupported.
## Prerequisites
* Node.js >= 18.17.0
@@ -53,9 +55,15 @@ This module will be compiled from source at `npm install` time when:
Building from source requires:
* C++11 compiler
* [node-addon-api](https://www.npmjs.com/package/node-addon-api)
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies
If `node-gyp` cannot be found, try adding it to `devDependencies`.
There is an install-time check for these dependencies.
If `node-addon-api` or `node-gyp` cannot be found, try adding them via:
```sh
npm install --save node-addon-api node-gyp
```
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
(or the `npm_config_platform`, `npm_config_arch` and `npm_config_libc` environment variables)

File diff suppressed because one or more lines are too long

View File

@@ -3,18 +3,27 @@
'use strict';
const { useGlobalLibvips, globalLibvipsVersion, log, gypRebuild } = require('../lib/libvips');
const { useGlobalLibvips, globalLibvipsVersion, log, spawnRebuild } = require('../lib/libvips');
const buildFromSource = (msg) => {
log(msg);
log('Attempting to build from source via node-gyp');
try {
require('node-gyp');
require('node-addon-api');
log('Found node-addon-api');
} catch (err) {
log('You might need to install node-gyp');
log('Please add node-addon-api to your dependencies');
return;
}
try {
const gyp = require('node-gyp');
log(`Found node-gyp version ${gyp().version}`);
} catch (err) {
log('Please add node-gyp to your dependencies');
return;
}
log('See https://sharp.pixelplumbing.com/install#building-from-source');
const status = gypRebuild();
const status = spawnRebuild();
if (status !== 0) {
process.exit(status);
}

View File

@@ -3,8 +3,8 @@
'use strict';
const util = require('util');
const stream = require('stream');
const util = require('node:util');
const stream = require('node:stream');
const is = require('./is');
require('./sharp');

View File

@@ -3,7 +3,7 @@
'use strict';
const spawnSync = require('child_process').spawnSync;
const { spawnSync } = require('node:child_process');
const semverCoerce = require('semver/functions/coerce');
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
const detectLibc = require('detect-libc');
@@ -80,8 +80,8 @@ const isRosetta = () => {
};
/* istanbul ignore next */
const gypRebuild = () =>
spawnSync('node-gyp rebuild', {
const spawnRebuild = () =>
spawnSync('node-gyp rebuild --directory=src', {
...spawnSyncOptions,
stdio: 'inherit'
}).status;
@@ -146,7 +146,7 @@ module.exports = {
buildSharpLibvipsLibDir,
runtimePlatformArch,
log,
gypRebuild,
spawnRebuild,
globalLibvipsVersion,
pkgConfigPath,
useGlobalLibvips

View File

@@ -3,7 +3,7 @@
'use strict';
const path = require('path');
const path = require('node:path');
const is = require('./is');
const sharp = require('./sharp');

View File

@@ -250,6 +250,9 @@ function resize (widthOrOptions, height, options) {
if (isResizeExpected(this.options)) {
this.options.debuglog('ignoring previous resize options');
}
if (this.options.widthPost !== -1) {
this.options.debuglog('operation order will be: extract, resize, extract');
}
if (is.defined(widthOrOptions)) {
if (is.object(widthOrOptions) && !is.defined(options)) {
options = widthOrOptions;
@@ -437,7 +440,7 @@ function extend (extend) {
*
* - Use `extract` before `resize` for pre-resize extraction.
* - Use `extract` after `resize` for post-resize extraction.
* - Use `extract` before and after for both.
* - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
*
* @example
* sharp(input)

View File

@@ -9,11 +9,12 @@ const { familySync, versionSync } = require('detect-libc');
const { runtimePlatformArch, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
const runtimePlatform = runtimePlatformArch();
const [isLinux, isMacOs, isWindows] = ['linux', 'darwin', 'win32'].map(os => runtimePlatform.startsWith(os));
/* istanbul ignore next */
try {
// Check for local build
module.exports = require(`../build/Release/sharp-${runtimePlatform}.node`);
module.exports = require(`../src/build/Release/sharp-${runtimePlatform}.node`);
} catch (errLocal) {
try {
// Check for runtime package
@@ -29,11 +30,15 @@ try {
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}"`);
help.push('- Add explicit dependencies for the runtime platform:');
help.push(` npm install --force @sharpen/sharp-${runtimePlatform}`);
if (!isWindows) {
help.push(` npm install --force @sharpen/sharp-libvips-${runtimePlatform}`);
}
} else {
help.push(`- The ${runtimePlatform} platform requires manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (runtimePlatform.startsWith('linux') && /symbol not found/i.test(errPackage)) {
if (isLinux && /symbol not found/i.test(errPackage)) {
try {
const { engines } = require(`@sharpen/sharp-libvips-${runtimePlatform}/package`);
const libcFound = `${familySync()} ${versionSync()}`;
@@ -41,16 +46,21 @@ try {
help.push(`- Update your OS: found ${libcFound}, requires ${libcRequires}`);
} catch (errEngines) {}
}
if (runtimePlatform.startsWith('darwin') && /Incompatible library version/.test(errLocal.message)) {
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
if (isMacOs && /Incompatible library version/.test(errLocal.message)) {
help.push('- Update Homebrew:');
help.push(' brew update && brew upgrade vips');
}
if (errPackage.code === 'ERR_DLOPEN_DISABLED') {
help.push('- Run Node.js without using the --no-addons flag');
}
if (process.versions.pnp) {
help.push('- Use a supported yarn linker, either pnpm or node-modules');
help.push(' yarn config set nodeLinker node-modules');
}
// Link to installation docs
if (runtimePlatform.startsWith('linux') && /Module did not self-register/.test(errLocal.message + errPackage.message)) {
if (isLinux && /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)) {
} else if (isWindows && /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');

View File

@@ -3,13 +3,16 @@
'use strict';
const events = require('events');
const events = require('node:events');
const detectLibc = require('detect-libc');
const is = require('./is');
const { runtimePlatformArch } = require('./libvips');
const sharp = require('./sharp');
const runtimePlatform = runtimePlatformArch();
const libvipsVersion = sharp.libvipsVersion();
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @member
@@ -44,20 +47,25 @@ const interpolators = {
};
/**
* An Object containing the version numbers of sharp, libvips and its dependencies.
* An Object containing the version numbers of sharp, libvips
* and (when using prebuilt binaries) its dependencies.
*
* @member
* @example
* console.log(sharp.versions);
*/
let versions = {
vips: sharp.libvipsVersion()
vips: libvipsVersion.semver
};
try {
versions = require(`@sharpen/sharp-${runtimePlatformArch()}/versions`);
} catch (_) {
/* istanbul ignore next */
if (!libvipsVersion.isGlobal) {
try {
versions = require(`@sharpen/sharp-libvips-${runtimePlatformArch()}/versions`);
} catch (_) {}
versions = require(`@sharpen/sharp-${runtimePlatform}/versions`);
} catch (_) {
try {
versions = require(`@sharpen/sharp-libvips-${runtimePlatform}/versions`);
} catch (_) {}
}
}
versions.sharp = require('../package.json').version;

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-darwin-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with macOS ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-darwin-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-darwin-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with macOS x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,12 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-darwin-x64.node",
"./package": "./package.json"

View File

@@ -5,7 +5,7 @@
// Populate contents of all packages with the current GitHub release
const { writeFile, copyFile } = require('node:fs/promises');
const { writeFile, copyFile, rm } = require('node:fs/promises');
const path = require('node:path');
const { Readable } = require('node:stream');
const { pipeline } = require('node:stream/promises');
@@ -20,8 +20,25 @@ const mapTarballEntry = (header) => {
return header;
};
const licensing = `
## Licensing
Copyright 2013 Lovell Fuller and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
`;
workspaces.map(async platform => {
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v7-${platform}.tar.gz`;
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${platform}.tar.gz`;
const dir = path.join(__dirname, platform);
const response = await fetch(url);
if (!response.ok) {
@@ -29,14 +46,16 @@ workspaces.map(async platform => {
return;
}
// Extract prebuild tarball
const lib = path.join(dir, 'lib');
await rm(lib, { recursive: true });
await pipeline(
Readable.fromWeb(response.body),
createGunzip(),
extract(path.join(dir, 'lib'), { map: mapTarballEntry })
extract(lib, { map: mapTarballEntry })
);
// Generate README
const { name, description } = require(`./${platform}/package.json`);
await writeFile(path.join(dir, 'README.md'), `# ${name}\n${description}`);
await writeFile(path.join(dir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
// Copy Apache-2.0 LICENSE
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, 'LICENSE'));
// Copy Windows-specific files

View File

@@ -14,7 +14,7 @@ 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 release = path.join(__dirname, '..', 'src', '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'), {

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-arm",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-arm.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Linux (glibc) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linux-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Linux (glibc) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-x64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linuxmusl-arm64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Linux (musl) ARM64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-linuxmusl-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Linux (musl) x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,13 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
"./package": "./package.json"

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-win32-ia32",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,18 +13,19 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.1"
},
"files": [
"lib"
"lib",
"versions.json",
"THIRD-PARTY-NOTICES.md"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-win32-ia32.node",
"./package": "./package.json"
"./package": "./package.json",
"./versions": "./versions.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@sharpen/sharp-win32-x64",
"version": "0.0.1-alpha.1",
"version": "0.0.1-alpha.7",
"description": "Prebuilt sharp for use with Windows x64",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
@@ -13,15 +13,15 @@
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"optionalDependencies": {
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.1"
},
"files": [
"lib"
"lib",
"versions.json",
"THIRD-PARTY-NOTICES.md"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-win32-x64.node",
"./package": "./package.json"

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.33.0-alpha.1",
"version": "0.33.0-alpha.7",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -90,7 +90,7 @@
],
"scripts": {
"install": "node install/check",
"clean": "rm -rf build/ .nyc_output/ coverage/ test/fixtures/output.*",
"clean": "rm -rf src/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-lint": "semistandard && cpplint",
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
@@ -103,13 +103,13 @@
"docs-serve": "cd docs && npx serve",
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
},
"type": "commonjs",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"binding.gyp",
"install/**",
"lib/**",
"src/**"
"install",
"lib",
"src/*.{cc,h,gyp}"
],
"repository": {
"type": "git",
@@ -136,31 +136,30 @@
"dependencies": {
"color": "^4.2.3",
"detect-libc": "^2.0.2",
"node-addon-api": "^7.0.0",
"semver": "^7.5.4"
},
"optionalDependencies": {
"@sharpen/sharp-darwin-arm64": "0.0.1-alpha.1",
"@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"
"@sharpen/sharp-darwin-arm64": "0.0.1-alpha.7",
"@sharpen/sharp-darwin-x64": "0.0.1-alpha.7",
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.2",
"@sharpen/sharp-linux-arm": "0.0.1-alpha.7",
"@sharpen/sharp-linux-arm64": "0.0.1-alpha.7",
"@sharpen/sharp-linux-x64": "0.0.1-alpha.7",
"@sharpen/sharp-linuxmusl-arm64": "0.0.1-alpha.7",
"@sharpen/sharp-linuxmusl-x64": "0.0.1-alpha.7",
"@sharpen/sharp-win32-ia32": "0.0.1-alpha.7",
"@sharpen/sharp-win32-x64": "0.0.1-alpha.7"
},
"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",
"@sharpen/sharp-libvips-dev": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.2",
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.2",
"@types/node": "*",
"async": "^3.2.4",
"cc": "^3.0.1",
@@ -170,6 +169,7 @@
"jsdoc-to-markdown": "^8.0.0",
"license-checker": "^25.0.1",
"mocha": "^10.2.0",
"node-addon-api": "^7.0.0",
"nyc": "^15.1.0",
"prebuild": "^12.1.0",
"semistandard": "^17.0.0",

View File

@@ -3,14 +3,14 @@
{
'variables': {
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
'platform_and_arch': '<!(node -p "require(\'./lib/libvips\').buildPlatformArch()")',
'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()")'
'vips_version': '<!(node -p "require(\'../lib/libvips\').minimumLibvipsVersion")',
'platform_and_arch': '<!(node -p "require(\'../lib/libvips\').buildPlatformArch()")',
'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': [{
'target_name': 'win-libvips-cpp',
'target_name': 'libvips-cpp',
'conditions': [
['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
@@ -81,13 +81,13 @@
],
'dependencies': [
'<!(node -p "require(\'node-addon-api\').gyp")',
'win-libvips-cpp'
'libvips-cpp'
],
'variables': {
'conditions': [
['OS != "win"', {
'pkg_config_path': '<!(node -p "require(\'./lib/libvips\').pkgConfigPath()")',
'use_global_libvips': '<!(node -p "Boolean(require(\'./lib/libvips\').useGlobalLibvips()).toString()")'
'pkg_config_path': '<!(node -p "require(\'../lib/libvips\').pkgConfigPath()")',
'use_global_libvips': '<!(node -p "Boolean(require(\'../lib/libvips\').useGlobalLibvips()).toString()")'
}, {
'pkg_config_path': '',
'use_global_libvips': ''
@@ -95,13 +95,13 @@
]
},
'sources': [
'src/common.cc',
'src/metadata.cc',
'src/stats.cc',
'src/operations.cc',
'src/pipeline.cc',
'src/utilities.cc',
'src/sharp.cc'
'common.cc',
'metadata.cc',
'stats.cc',
'operations.cc',
'pipeline.cc',
'utilities.cc',
'sharp.cc'
],
'include_dirs': [
'<!(node -p "require(\'node-addon-api\').include_dir")',
@@ -111,6 +111,9 @@
# 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)'],
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)'],
'defines': [
'SHARP_USE_GLOBAL_LIBVIPS'
],
'conditions': [
['OS == "linux"', {
'defines': [
@@ -243,7 +246,7 @@
}
},
}, {
'target_name': 'win-copy-dlls',
'target_name': 'copy-dll',
'type': 'none',
'dependencies': [
'sharp-<(platform_and_arch)'

View File

@@ -166,10 +166,10 @@ namespace sharp {
}
// How many tasks are in the queue?
volatile int counterQueue = 0;
std::atomic<int> counterQueue{0};
// How many tasks are being processed?
volatile int counterProcess = 0;
std::atomic<int> counterProcess{0};
// Filename extension checkers
static bool EndsWith(std::string const &str, std::string const &end) {
@@ -363,12 +363,13 @@ namespace sharp {
if (descriptor->isBuffer) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
bool const is8bit = vips_band_format_is8bit(descriptor->rawDepth);
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);
if (descriptor->rawChannels < 3) {
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_GREY16;
} else {
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
}
if (descriptor->rawPremultiplied) {
image = image.unpremultiply();

View File

@@ -7,6 +7,7 @@
#include <string>
#include <tuple>
#include <vector>
#include <atomic>
#include <napi.h>
#include <vips/vips8>
@@ -161,10 +162,10 @@ namespace sharp {
};
// How many tasks are in the queue?
extern volatile int counterQueue;
extern std::atomic<int> counterQueue;
// How many tasks are being processed?
extern volatile int counterProcess;
extern std::atomic<int> counterProcess;
// Filename extension checkers
bool IsJpeg(std::string const &str);

View File

@@ -18,7 +18,7 @@ class MetadataWorker : public Napi::AsyncWorker {
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
@@ -281,7 +281,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
sharp::counterQueue++;
return info.Env().Undefined();
}

View File

@@ -44,9 +44,9 @@ class PipelineWorker : public Napi::AsyncWorker {
// libuv worker
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
// Increment processing task counter
g_atomic_int_inc(&sharp::counterProcess);
sharp::counterProcess++;
try {
// Open input
@@ -1289,8 +1289,8 @@ class PipelineWorker : public Napi::AsyncWorker {
delete baton;
// Decrement processing task counter
g_atomic_int_dec_and_test(&sharp::counterProcess);
Napi::Number queueLength = Napi::Number::New(env, static_cast<double>(sharp::counterQueue));
sharp::counterProcess--;
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
queueListener.MakeCallback(Receiver().Value(), { queueLength });
}
@@ -1707,8 +1707,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<double>(sharp::counterQueue));
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
queueListener.MakeCallback(info.This(), { queueLength });
return info.Env().Undefined();

View File

@@ -1,6 +1,8 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <mutex> // NOLINT(build/c++11)
#include <napi.h>
#include <vips/vips8>
@@ -10,14 +12,11 @@
#include "utilities.h"
#include "stats.h"
static void* sharp_vips_init(void*) {
vips_init("sharp");
return nullptr;
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
static GOnce sharp_vips_init_once = G_ONCE_INIT;
g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
static std::once_flag sharp_vips_init_once;
std::call_once(sharp_vips_init_once, []() {
vips_init("sharp");
});
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);

View File

@@ -30,7 +30,7 @@ class StatsWorker : public Napi::AsyncWorker {
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
sharp::counterQueue--;
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
@@ -177,7 +177,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
worker->Queue();
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
sharp::counterQueue++;
return info.Env().Undefined();
}

View File

@@ -3,6 +3,7 @@
#include <cmath>
#include <string>
#include <cstdio>
#include <napi.h>
#include <vips/vips8>
@@ -70,8 +71,8 @@ Napi::Value concurrency(const Napi::CallbackInfo& info) {
*/
Napi::Value counters(const Napi::CallbackInfo& info) {
Napi::Object counters = Napi::Object::New(info.Env());
counters.Set("queue", sharp::counterQueue);
counters.Set("process", sharp::counterProcess);
counters.Set("queue", static_cast<int>(sharp::counterQueue));
counters.Set("process", static_cast<int>(sharp::counterProcess));
return counters;
}
@@ -91,9 +92,18 @@ Napi::Value simd(const Napi::CallbackInfo& info) {
Get libvips version
*/
Napi::Value libvipsVersion(const Napi::CallbackInfo& info) {
char version[9];
g_snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
return Napi::String::New(info.Env(), version);
Napi::Env env = info.Env();
Napi::Object version = Napi::Object::New(env);
char semver[9];
std::snprintf(semver, sizeof(semver), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
version.Set("semver", Napi::String::New(env, semver));
#ifdef SHARP_USE_GLOBAL_LIBVIPS
version.Set("isGlobal", Napi::Boolean::New(env, true));
#else
version.Set("isGlobal", Napi::Boolean::New(env, false));
#endif
return version;
}
/*

View File

@@ -319,5 +319,16 @@ describe('Partial image extraction', function () {
s.extract(options);
assert.strictEqual(warningMessage, 'ignoring previous extract options');
});
it('Multiple extract+resize emits warning', () => {
let warningMessage = '';
const s = sharp();
s.on('warning', function (msg) { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options).extract(options);
assert.strictEqual(warningMessage, '');
s.resize(1);
assert.strictEqual(warningMessage, 'operation order will be: extract, resize, extract');
});
});
});

View File

@@ -284,4 +284,42 @@ describe('Raw pixel data', function () {
);
}
});
describe('16-bit roundtrip', () => {
it('grey', async () => {
const grey = 42000;
const png = await sharp(
Uint16Array.from([grey]),
{ raw: { width: 1, height: 1, channels: 1 } }
)
.toColourspace('grey16')
.png({ compressionLevel: 0 })
.toBuffer();
const raw = await sharp(png)
.toColourspace('grey16')
.raw({ depth: 'ushort' })
.toBuffer();
assert.strictEqual(raw.readUint16LE(0), grey);
});
it('RGB', async () => {
const rgb = [10946, 28657, 46368];
const png = await sharp(
Uint16Array.from(rgb),
{ raw: { width: 1, height: 1, channels: 3 } }
)
.toColourspace('rgb16')
.png({ compressionLevel: 0 })
.toBuffer();
const raw = await sharp(png)
.toColourspace('rgb16')
.raw({ depth: 'ushort' })
.toBuffer();
assert.strictEqual(raw.readUint16LE(0), rgb[0]);
assert.strictEqual(raw.readUint16LE(2), rgb[1]);
assert.strictEqual(raw.readUint16LE(4), rgb[2]);
});
});
});