Skip to content

Commit 9b3d44f

Browse files
GatsbyJS Botpieh
GatsbyJS Bot
andauthored
fix(gatsby-plugin-sharp): pass input buffer instead of readStream when processing image jobs (#33685) (#33694)
Co-authored-by: Michal Piechowiak <[email protected]>
1 parent cd0c018 commit 9b3d44f

File tree

3 files changed

+115
-113
lines changed

3 files changed

+115
-113
lines changed

packages/gatsby-plugin-sharp/src/__tests__/gatsby-worker.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ describe(`worker`, () => {
5454
})
5555

5656
it(`should fail a promise when image processing fails`, async () => {
57-
processFile.mockImplementation(() => [
58-
Promise.reject(new Error(`transform failed`)),
59-
])
57+
processFile.mockImplementation(() =>
58+
Promise.reject(new Error(`transform failed`))
59+
)
6060

6161
const job = {
6262
inputPaths: [

packages/gatsby-plugin-sharp/src/gatsby-worker.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,15 @@ exports.IMAGE_PROCESSING_JOB_NAME = `IMAGE_PROCESSING`
2020
*/
2121
const q = queue(
2222
async ({ inputPaths, outputDir, args }) =>
23-
Promise.all(
24-
processFile(
25-
inputPaths[0].path,
26-
args.operations.map(operation => {
27-
return {
28-
outputPath: path.join(outputDir, operation.outputPath),
29-
args: operation.args,
30-
}
31-
}),
32-
args.pluginOptions
33-
)
23+
processFile(
24+
inputPaths[0].path,
25+
args.operations.map(operation => {
26+
return {
27+
outputPath: path.join(outputDir, operation.outputPath),
28+
args: operation.args,
29+
}
30+
}),
31+
args.pluginOptions
3432
),
3533
// When inside query workers, we only want to use the current core
3634
process.env.GATSBY_WORKER_POOL_WORKER ? 1 : Math.max(1, cpuCoreCount() - 1)

packages/gatsby-plugin-sharp/src/process-file.js

+103-99
Original file line numberDiff line numberDiff line change
@@ -53,122 +53,126 @@ sharp.concurrency(1)
5353
* @param {String} file
5454
* @param {Transform[]} transforms
5555
*/
56-
exports.processFile = (file, transforms, options = {}) => {
56+
exports.processFile = async (file, transforms, options = {}) => {
5757
let pipeline
5858
try {
59-
pipeline = !options.failOnError ? sharp({ failOnError: false }) : sharp()
59+
const inputBuffer = await fs.readFile(file)
60+
pipeline = !options.failOnError
61+
? sharp(inputBuffer, { failOnError: false })
62+
: sharp(inputBuffer)
6063

6164
// Keep Metadata
6265
if (!options.stripMetadata) {
6366
pipeline = pipeline.withMetadata()
6467
}
65-
fs.createReadStream(file).pipe(pipeline)
6668
} catch (err) {
6769
throw new SharpError(`Failed to load image ${file} into sharp.`, err)
6870
}
6971

70-
return transforms.map(async transform => {
71-
try {
72-
const { outputPath, args } = transform
73-
debug(`Start processing ${outputPath}`)
74-
await fs.ensureDir(path.dirname(outputPath))
75-
76-
const transformArgs = healOptions(
77-
{ defaultQuality: options.defaultQuality },
78-
args
79-
)
80-
81-
let clonedPipeline = transforms.length > 1 ? pipeline.clone() : pipeline
82-
83-
if (transformArgs.trim) {
84-
clonedPipeline = clonedPipeline.trim(transformArgs.trim)
85-
}
86-
87-
if (!transformArgs.rotate) {
88-
clonedPipeline = clonedPipeline.rotate()
89-
}
90-
91-
// Sharp only allows ints as height/width. Since both aren't always
92-
// set, check first before trying to round them.
93-
let roundedHeight = transformArgs.height
94-
if (roundedHeight) {
95-
roundedHeight = Math.round(roundedHeight)
96-
}
97-
98-
let roundedWidth = transformArgs.width
99-
if (roundedWidth) {
100-
roundedWidth = Math.round(roundedWidth)
101-
}
102-
103-
clonedPipeline
104-
.resize(roundedWidth, roundedHeight, {
105-
position: transformArgs.cropFocus,
106-
fit: transformArgs.fit,
107-
background: transformArgs.background,
108-
})
109-
.png({
110-
compressionLevel: transformArgs.pngCompressionLevel,
111-
adaptiveFiltering: false,
112-
quality: transformArgs.pngQuality || transformArgs.quality,
113-
force: transformArgs.toFormat === `png`,
114-
})
115-
.webp({
116-
quality: transformArgs.webpQuality || transformArgs.quality,
117-
force: transformArgs.toFormat === `webp`,
118-
})
119-
.tiff({
120-
quality: transformArgs.quality,
121-
force: transformArgs.toFormat === `tiff`,
122-
})
123-
.avif({
124-
quality: transformArgs.quality,
125-
force: transformArgs.toFormat === `avif`,
126-
})
127-
.jpeg({
128-
mozjpeg: options.useMozJpeg,
129-
quality: transformArgs.jpegQuality || transformArgs.quality,
130-
progressive: transformArgs.jpegProgressive,
131-
force: transformArgs.toFormat === `jpg`,
132-
})
133-
134-
// grayscale
135-
if (transformArgs.grayscale) {
136-
clonedPipeline = clonedPipeline.grayscale()
137-
}
138-
139-
// rotate
140-
if (transformArgs.rotate && transformArgs.rotate !== 0) {
141-
clonedPipeline = clonedPipeline.rotate(transformArgs.rotate)
142-
}
72+
return Promise.all(
73+
transforms.map(async transform => {
74+
try {
75+
const { outputPath, args } = transform
76+
debug(`Start processing ${outputPath}`)
77+
await fs.ensureDir(path.dirname(outputPath))
14378

144-
// duotone
145-
if (transformArgs.duotone) {
146-
clonedPipeline = await duotone(
147-
transformArgs.duotone,
148-
transformArgs.toFormat,
149-
clonedPipeline
79+
const transformArgs = healOptions(
80+
{ defaultQuality: options.defaultQuality },
81+
args
15082
)
151-
}
15283

153-
try {
154-
const buffer = await clonedPipeline.toBuffer()
155-
await fs.writeFile(outputPath, buffer)
84+
let clonedPipeline = transforms.length > 1 ? pipeline.clone() : pipeline
85+
86+
if (transformArgs.trim) {
87+
clonedPipeline = clonedPipeline.trim(transformArgs.trim)
88+
}
89+
90+
if (!transformArgs.rotate) {
91+
clonedPipeline = clonedPipeline.rotate()
92+
}
93+
94+
// Sharp only allows ints as height/width. Since both aren't always
95+
// set, check first before trying to round them.
96+
let roundedHeight = transformArgs.height
97+
if (roundedHeight) {
98+
roundedHeight = Math.round(roundedHeight)
99+
}
100+
101+
let roundedWidth = transformArgs.width
102+
if (roundedWidth) {
103+
roundedWidth = Math.round(roundedWidth)
104+
}
105+
106+
clonedPipeline
107+
.resize(roundedWidth, roundedHeight, {
108+
position: transformArgs.cropFocus,
109+
fit: transformArgs.fit,
110+
background: transformArgs.background,
111+
})
112+
.png({
113+
compressionLevel: transformArgs.pngCompressionLevel,
114+
adaptiveFiltering: false,
115+
quality: transformArgs.pngQuality || transformArgs.quality,
116+
force: transformArgs.toFormat === `png`,
117+
})
118+
.webp({
119+
quality: transformArgs.webpQuality || transformArgs.quality,
120+
force: transformArgs.toFormat === `webp`,
121+
})
122+
.tiff({
123+
quality: transformArgs.quality,
124+
force: transformArgs.toFormat === `tiff`,
125+
})
126+
.avif({
127+
quality: transformArgs.quality,
128+
force: transformArgs.toFormat === `avif`,
129+
})
130+
.jpeg({
131+
mozjpeg: options.useMozJpeg,
132+
quality: transformArgs.jpegQuality || transformArgs.quality,
133+
progressive: transformArgs.jpegProgressive,
134+
force: transformArgs.toFormat === `jpg`,
135+
})
136+
137+
// grayscale
138+
if (transformArgs.grayscale) {
139+
clonedPipeline = clonedPipeline.grayscale()
140+
}
141+
142+
// rotate
143+
if (transformArgs.rotate && transformArgs.rotate !== 0) {
144+
clonedPipeline = clonedPipeline.rotate(transformArgs.rotate)
145+
}
146+
147+
// duotone
148+
if (transformArgs.duotone) {
149+
clonedPipeline = await duotone(
150+
transformArgs.duotone,
151+
transformArgs.toFormat,
152+
clonedPipeline
153+
)
154+
}
155+
156+
try {
157+
const buffer = await clonedPipeline.toBuffer()
158+
await fs.writeFile(outputPath, buffer)
159+
} catch (err) {
160+
throw new Error(
161+
`Failed to write ${file} into ${outputPath}. (${err.message})`
162+
)
163+
}
156164
} catch (err) {
157-
throw new Error(
158-
`Failed to write ${file} into ${outputPath}. (${err.message})`
159-
)
160-
}
161-
} catch (err) {
162-
if (err instanceof SharpError) {
163-
// rethrow
164-
throw err
165-
}
165+
if (err instanceof SharpError) {
166+
// rethrow
167+
throw err
168+
}
166169

167-
throw new SharpError(`Processing ${file} failed`, err)
168-
}
170+
throw new SharpError(`Processing ${file} failed`, err)
171+
}
169172

170-
return transform
171-
})
173+
return transform
174+
})
175+
)
172176
}
173177

174178
exports.createArgsDigest = args => {

0 commit comments

Comments
 (0)