Parallelize @astrojs/image transforms (#4626)

This commit is contained in:
Alan 2022-09-06 07:29:20 -07:00 committed by GitHub
parent 5231ec05d2
commit 494c2b8353
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 31 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/image': patch
---
Parallelize image transforms

View file

@ -40,6 +40,7 @@
"test": "mocha --exit --timeout 20000 test" "test": "mocha --exit --timeout 20000 test"
}, },
"dependencies": { "dependencies": {
"@altano/tiny-async-pool": "^1.0.2",
"image-size": "^1.0.2", "image-size": "^1.0.2",
"magic-string": "^0.25.9", "magic-string": "^0.25.9",
"mime": "^3.0.0", "mime": "^3.0.0",

View file

@ -7,6 +7,8 @@ import type { SSRImageService, TransformOptions } from '../loaders/index.js';
import { loadLocalImage, loadRemoteImage } from '../utils/images.js'; import { loadLocalImage, loadRemoteImage } from '../utils/images.js';
import { debug, info, LoggerLevel, warn } from '../utils/logger.js'; import { debug, info, LoggerLevel, warn } from '../utils/logger.js';
import { isRemoteImage } from '../utils/paths.js'; import { isRemoteImage } from '../utils/paths.js';
import OS from 'node:os';
import { doWork } from '@altano/tiny-async-pool';
function getTimeStat(timeStart: number, timeEnd: number) { function getTimeStat(timeStart: number, timeEnd: number) {
const buildTime = timeEnd - timeStart; const buildTime = timeEnd - timeStart;
@ -23,19 +25,26 @@ export interface SSGBuildParams {
export async function ssgBuild({ loader, staticImages, config, outDir, logLevel }: SSGBuildParams) { export async function ssgBuild({ loader, staticImages, config, outDir, logLevel }: SSGBuildParams) {
const timer = performance.now(); const timer = performance.now();
const cpuCount = OS.cpus().length;
info({ info({
level: logLevel, level: logLevel,
prefix: false, prefix: false,
message: `${bgGreen( message: `${bgGreen(
black(` optimizing ${staticImages.size} image${staticImages.size > 1 ? 's' : ''} `) black(
` optimizing ${staticImages.size} image${
staticImages.size > 1 ? 's' : ''
} in batches of ${cpuCount} `
)
)}`, )}`,
}); });
const inputFiles = new Set<string>(); const inputFiles = new Set<string>();
// process transforms one original image file at a time async function processStaticImage([src, transformsMap]: [
for (let [src, transformsMap] of staticImages) { string,
Map<string, TransformOptions>
]): Promise<void> {
let inputFile: string | undefined = undefined; let inputFile: string | undefined = undefined;
let inputBuffer: Buffer | undefined = undefined; let inputBuffer: Buffer | undefined = undefined;
@ -60,15 +69,15 @@ export async function ssgBuild({ loader, staticImages, config, outDir, logLevel
if (!inputBuffer) { if (!inputBuffer) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
warn({ level: logLevel, message: `"${src}" image could not be fetched` }); warn({ level: logLevel, message: `"${src}" image could not be fetched` });
continue; return;
} }
const transforms = Array.from(transformsMap.entries()); const transforms = Array.from(transformsMap.entries());
debug({ level: logLevel, prefix: false, message: `${green('▶')} ${src}` }); debug({ level: logLevel, prefix: false, message: `${green('▶')} transforming ${src}` });
let timeStart = performance.now(); let timeStart = performance.now();
// process each transformed versiono of the // process each transformed version
for (const [filename, transform] of transforms) { for (const [filename, transform] of transforms) {
timeStart = performance.now(); timeStart = performance.now();
let outputFile: string; let outputFile: string;
@ -92,11 +101,14 @@ export async function ssgBuild({ loader, staticImages, config, outDir, logLevel
debug({ debug({
level: logLevel, level: logLevel,
prefix: false, prefix: false,
message: ` ${cyan('└─')} ${dim(pathRelative)} ${dim(timeIncrease)}`, message: ` ${cyan('created')} ${dim(pathRelative)} ${dim(timeIncrease)}`,
}); });
} }
} }
// transform each original image file in batches
await doWork(cpuCount, staticImages, processStaticImage);
info({ info({
level: logLevel, level: logLevel,
prefix: false, prefix: false,

View file

@ -2214,6 +2214,7 @@ importers:
packages/integrations/image: packages/integrations/image:
specifiers: specifiers:
'@altano/tiny-async-pool': ^1.0.2
'@types/sharp': ^0.30.5 '@types/sharp': ^0.30.5
astro: workspace:* astro: workspace:*
astro-scripts: workspace:* astro-scripts: workspace:*
@ -2223,6 +2224,7 @@ importers:
mime: ^3.0.0 mime: ^3.0.0
sharp: ^0.30.6 sharp: ^0.30.6
dependencies: dependencies:
'@altano/tiny-async-pool': 1.0.2
image-size: 1.0.2 image-size: 1.0.2
magic-string: 0.25.9 magic-string: 0.25.9
mime: 3.0.0 mime: 3.0.0
@ -3155,6 +3157,10 @@ packages:
'@algolia/requester-common': 4.14.2 '@algolia/requester-common': 4.14.2
dev: false dev: false
/@altano/tiny-async-pool/1.0.2:
resolution: {integrity: sha512-qQzaI0TBUPdpjZ3qo5b2ziQY9MSNpbziH2ZrE5lvtUZL+kn9GwVuVJwoOubaoNkeDB+rqEefnpu1k+oMpOCYiw==}
dev: false
/@ampproject/remapping/2.2.0: /@ampproject/remapping/2.2.0:
resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}