diff --git a/admin/scripts/resizeImage.js b/admin/scripts/resizeImage.js index 39f4c8a993..59baff0de3 100644 --- a/admin/scripts/resizeImage.js +++ b/admin/scripts/resizeImage.js @@ -8,46 +8,95 @@ import fs from 'fs-extra'; import path from 'path'; import {fileURLToPath} from 'url'; +import {program} from 'commander'; import logger from '@docusaurus/logger'; import sharp from 'sharp'; import imageSize from 'image-size'; -const allImages = ( - await fs.readdir(new URL('../../website/src/data/showcase', import.meta.url)) -).filter((file) => ['.png', 'jpg', '.jpeg'].includes(path.extname(file))); +// You can use it as: +// +// # Resize all images in showcase (which is most likely) +// node admin/scripts/resizeImage.js +// +// # Resize specified images / all images in a folder +// # This does not read folders recursively as of now +// node admin/scripts/resizeImage.js image1.png some-folder ... +// +// By default, showcase images are resized to 640×320; everything else is +// resized to width 1000. You can explicitly give a width/height as arguments. +// node admin/scripts/resizeImage.js --width 640 --height 320 image1.png -const [, , ...selectedImages] = process.argv; -const images = selectedImages.length > 0 ? selectedImages : allImages; +function maybeParseInt(n) { + const res = Number.parseInt(n, 10); + if (Number.isNaN(res)) { + return undefined; + } + return res; +} -const stats = { - skipped: 0, - resized: 0, -}; +const showcasePath = 'website/src/data/showcase'; -await Promise.all( - images.map(async (img) => { - const imgPath = fileURLToPath( - new URL(`../../website/src/data/showcase/${img}`, import.meta.url), - ); - const {width, height} = imageSize(imgPath); - if (width === 640 && height === 320 && imgPath.endsWith('.png')) { - // Do not emit if not resized. Important because we can't guarantee - // idempotency during resize -> optimization - stats.skipped += 1; - return; +program + .arguments('[imagePaths...]') + .option('-w, --width ', 'Image width', maybeParseInt) + .option('-h, --height ', 'Image height', maybeParseInt) + .action(async (imagePaths, options) => { + if (imagePaths.length === 0) { + imagePaths.push(showcasePath); } - logger.info`Resized path=${imgPath}: Before number=${width}×number=${height}`; - const data = await sharp(imgPath) - .resize(640, 320, {fit: 'cover', position: 'top'}) - .png() - .toBuffer(); - await fs.writeFile(imgPath.replace(/jpe?g/, 'png'), data); - stats.resized += 1; - }), -); + const rootDir = fileURLToPath(new URL('../..', import.meta.url)); + const images = ( + await Promise.all( + imagePaths.map(async (p) => + path.extname(p) + ? [path.resolve(rootDir, p)] + : (await fs.readdir(p)).map((f) => path.resolve(rootDir, p, f)), + ), + ) + ) + .flat() + .filter((p) => ['.png', 'jpg', '.jpeg'].includes(path.extname(p))); -console.log(`Showcase images resizing complete. -${JSON.stringify(stats, null, 2)}`); + const stats = { + skipped: 0, + resized: 0, + }; + + await Promise.all( + images.map(async (imgPath) => { + const {width, height} = imageSize(imgPath); + const targetWidth = + options.width ?? (imgPath.includes(showcasePath) ? 640 : 1000); + const targetHeight = + options.height ?? (imgPath.includes(showcasePath) ? 320 : undefined); + if ( + width <= targetWidth && + (!targetHeight || height <= targetHeight) && + imgPath.endsWith('.png') + ) { + // Do not emit if not resized. Important because we can't guarantee + // idempotency during resize -> optimization + stats.skipped += 1; + return; + } + logger.info`Resized path=${imgPath}: before number=${width}×number=${height}; now number=${targetWidth}×number=${ + targetHeight ?? Math.floor((height / width) * targetWidth) + }`; + const data = await sharp(imgPath) + .resize(targetWidth, targetHeight, {fit: 'cover', position: 'top'}) + .png() + .toBuffer(); + await fs.writeFile(imgPath.replace(/jpe?g/, 'png'), data); + stats.resized += 1; + }), + ); + + logger.info`Images resizing complete. + resized: number=${stats.resized} + skipped: number=${stats.skipped}`; + }); + +program.parse(process.argv); // You should also run // optimizt `find website/src/data/showcase -type f -name '*.png'`. diff --git a/admin/scripts/resizeImageBlog.js b/admin/scripts/resizeImageBlog.js deleted file mode 100644 index 75db68e951..0000000000 --- a/admin/scripts/resizeImageBlog.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import fs from 'fs-extra'; -import path from 'path'; -import logger from '@docusaurus/logger'; -import sharp from 'sharp'; -import imageSize from 'image-size'; -import globby from 'globby'; - -// TODO duplicate temporary script: factorize! - -const imgDir = 'website/blog/2022-08-01-announcing-docusaurus-2.0/img'; - -const imgWidth = 1200; - -const allImages = (await globby(`${imgDir}/**`)).filter((file) => - ['.png', 'jpg', '.jpeg'].includes(path.extname(file)), -); - -const [, , ...selectedImages] = process.argv; -const images = selectedImages.length > 0 ? selectedImages : allImages; - -const stats = { - skipped: 0, - resized: 0, -}; - -await Promise.all( - images.map(async (imgPath) => { - const {width, height} = imageSize(imgPath); - if (width === imgWidth && imgPath.endsWith('.png')) { - // Do not emit if not resized. Important because we can't guarantee - // idempotency during resize -> optimization - stats.skipped += 1; - return; - } - logger.info`Resized path=${imgPath}: Before number=${width}×number=${height}`; - const data = await sharp(imgPath) - .resize(imgWidth) - .png({quality: 100}) - .toBuffer(); - await fs.writeFile(imgPath.replace(/jpe?g/, 'png'), data); - stats.resized += 1; - }), -); - -console.log(`Blog images resizing complete. -${JSON.stringify(stats, null, 2)}`); diff --git a/project-words.txt b/project-words.txt index 6659806d2f..6860a8bf88 100644 --- a/project-words.txt +++ b/project-words.txt @@ -1,6 +1,7 @@ abernathyca adriaan agan +alexbdebrie alexey algoliasearch anonymized @@ -50,9 +51,12 @@ contravariance corejs crawlable creativecommons +csapo cssnano csvg customizability +dabit +dabit daishi datagit datas @@ -63,6 +67,7 @@ deduplicated déja deps devcontainers +devs devspace devto dmitry @@ -100,6 +105,7 @@ formik fouc froms funboxteam +gabrielcsapo getopts gitpod globbing @@ -109,7 +115,10 @@ goyal gruntfuggly gtag hahaha +hamel hardcoding +hasura +heavener héctor héllô heuristical @@ -118,6 +127,7 @@ hola horiz hostman hoverable +husain ianad idempotency immer @@ -126,11 +136,13 @@ inlines intelli interactiveness interpolatable +investec jakepartusch jamstack janvier javadoc jmarcey +jodyheavener joshcena jscodeshift jssdk @@ -157,6 +169,7 @@ marcey marocchino massoud mathjax +maxlynch maxresdefault mdast mdxa @@ -195,7 +208,7 @@ npmrc nprogress ntfs nuxt -O’Shannessy +o’shannessy onboarded openapi opensearch @@ -204,6 +217,7 @@ opensource optimizt optind orta +outerbounds overrideable pageview palenight @@ -212,6 +226,7 @@ palo paraiso pathinfo pathnames +paularmstrong pbcopy pcss peaceiris @@ -222,6 +237,7 @@ picomatch playbtn pluggable plushie +plushies pnpm posthog preactjs @@ -250,6 +266,7 @@ qovery quasis quddus quddús +quickwit quotify rachelnabors ramón @@ -281,6 +298,8 @@ sensical serializers setaf setext +shiki +shiki showinfo sida simen @@ -288,11 +307,14 @@ slorber sluggified sluggifies sluggify +solana +solana spâce stackblitz stackblitzrc strikethrough strikethroughs +styl stylelint stylelintrc subdir @@ -310,11 +332,11 @@ subsubsubfolder sucipto supabase svgr -styl swizzlable teik templating thanos +therox toolset toplevel transifex @@ -325,6 +347,7 @@ treeify treosh triaging tses +twoslash typecheck typechecks typedoc @@ -348,6 +371,7 @@ vetter vfile vicenti vieira +viet viewports vinnik vjeux @@ -367,27 +391,3 @@ yangshunz zhou zoomable zpao -paularmstrong -devs -Viet -dabit -Dabit -alexbdebrie -Investec -Quickwit -Hamel -Husain -Outerbounds -jodyheavener -Heavener -maxlynch -gabrielcsapo -Csapo -Hasura -Solana -solana -shiki -twoslash -Shiki -Therox -plushies