From 06e2c12af574d1bdcbcba4037ff524116c36bcbf Mon Sep 17 00:00:00 2001 From: Kristofer Koishigawa Date: Wed, 24 Jul 2024 19:04:03 +0900 Subject: [PATCH] feat: use node-cache during build (#922) --- package-lock.json | 39 ++++++++++++++++++++++++++++ package.json | 1 + src/_data/site.js | 10 +++++-- utils/cache.js | 12 +++++++++ utils/get-image-dimensions.js | 33 ++++++++++------------- utils/ghost/original-post-handler.js | 5 +++- utils/ghost/process-batch.js | 14 +++++----- utils/hashnode/process-batch.js | 8 +++--- utils/modify-html-content.js | 5 +++- 9 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 utils/cache.js diff --git a/package-lock.json b/package-lock.json index 04ebba1f9..ca4f62762 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "lint-staged": "15.2.7", "lodash": "4.17.21", "md5": "2.3.0", + "node-cache": "5.1.2", "node-fetch": "2.7.0", "npm-run-all2": "6.2.2", "piscina": "4.6.1", @@ -9455,6 +9456,27 @@ "dev": true, "optional": true }, + "node_modules/node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dev": true, + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-cache/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -19354,6 +19376,23 @@ "dev": true, "optional": true }, + "node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dev": true, + "requires": { + "clone": "2.x" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true + } + } + }, "node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", diff --git a/package.json b/package.json index 0a6a0757d..b21b51f37 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "lint-staged": "15.2.7", "lodash": "4.17.21", "md5": "2.3.0", + "node-cache": "5.1.2", "node-fetch": "2.7.0", "npm-run-all2": "6.2.2", "piscina": "4.6.1", diff --git a/src/_data/site.js b/src/_data/site.js index 5a3d82814..022f2d0f1 100644 --- a/src/_data/site.js +++ b/src/_data/site.js @@ -41,8 +41,14 @@ module.exports = async () => { site.icon = iconUrl; // Determine image dimensions before server runs for structured data - const logoDimensions = await getImageDimensions(logoUrl); - const coverImageDimensions = await getImageDimensions(coverImageUrl); + const logoDimensions = await getImageDimensions( + logoUrl, + `Site logo: ${logoUrl}` + ); + const coverImageDimensions = await getImageDimensions( + coverImageUrl, + `Site cover image: ${coverImageUrl}` + ); site.image_dimensions = { logo: { diff --git a/utils/cache.js b/utils/cache.js new file mode 100644 index 000000000..b282d4558 --- /dev/null +++ b/utils/cache.js @@ -0,0 +1,12 @@ +const NodeCache = require('node-cache'); + +const cache = new NodeCache(); + +const getCache = key => cache.get(key); + +const setCache = (key, data) => cache.set(key, data); + +module.exports = { + getCache, + setCache +}; diff --git a/utils/get-image-dimensions.js b/utils/get-image-dimensions.js index b11a3d2f4..938d330f6 100644 --- a/utils/get-image-dimensions.js +++ b/utils/get-image-dimensions.js @@ -1,32 +1,27 @@ const probe = require('probe-image-size'); const errorLogger = require('./error-logger'); -// Cache image dimensions we know will be repeated, like author profile and cover images -const imageDimensionMap = {}; +const { getCache, setCache } = require('./cache'); +// // Cache image dimensions we know will be repeated, like author profile and cover images +// const imageDimensionMap = {}; const defaultDimensions = { width: 600, height: 400 }; -const getImageDimensions = async (url, title, cache) => { +const getImageDimensions = async (url, description) => { try { - if (cache && imageDimensionMap[url]) return imageDimensionMap[url]; + let imageDimensions = getCache(url); + if (imageDimensions) return imageDimensions; const res = await probe(url); - const width = res?.width ? res?.width : defaultDimensions.width; - const height = res?.height ? res?.height : defaultDimensions.height; - - if (cache) { - imageDimensionMap[url] = { width, height }; - } - - return { - width, - height + imageDimensions = { + width: res?.width ? res?.width : defaultDimensions.width, + height: res?.height ? res?.height : defaultDimensions.height }; + setCache(url, imageDimensions); + + return imageDimensions; } catch (err) { - if (err.statusCode) errorLogger({ type: 'image', name: title }); // Only write HTTP status code errors to log + if (err.statusCode) errorLogger({ type: 'image', name: description }); // Only write HTTP status code errors to log - return { - width: defaultDimensions.width, - height: defaultDimensions.height - }; + return defaultDimensions; } }; diff --git a/utils/ghost/original-post-handler.js b/utils/ghost/original-post-handler.js index 83714aaa9..b94cbeee8 100644 --- a/utils/ghost/original-post-handler.js +++ b/utils/ghost/original-post-handler.js @@ -42,7 +42,10 @@ const originalPostHandler = async post => { ...originalPost.primary_author.image_dimensions }; originalPost.primary_author.image_dimensions.profile_image = - await getImageDimensions(originalPost.primary_author.profile_image); + await getImageDimensions( + originalPost.primary_author.profile_image, + `Original author profile image: ${originalPost.primary_author.profile_image}` + ); } // Add an `original_post` object to the current post diff --git a/utils/ghost/process-batch.js b/utils/ghost/process-batch.js index cbed0d06d..0c05fa9e5 100644 --- a/utils/ghost/process-batch.js +++ b/utils/ghost/process-batch.js @@ -58,7 +58,8 @@ const processBatch = async ({ // Set the source of the publication and whether it's a page or post for tracking and later processing obj.source = 'Ghost'; - obj.contentType = contentType === 'posts' ? 'post' : 'page'; + const singularContentType = contentType.slice(0, -1); + obj.contentType = singularContentType; // Set a default feature image for posts if one doesn't exist if (contentType === 'posts' && !obj.feature_image) @@ -70,7 +71,7 @@ const processBatch = async ({ obj.image_dimensions = { ...obj.image_dimensions }; obj.image_dimensions.feature_image = await getImageDimensions( obj.feature_image, - obj.title + `Ghost ${singularContentType} feature image: ${obj.title}` ); } @@ -82,8 +83,7 @@ const processBatch = async ({ obj.primary_author.image_dimensions.profile_image = await getImageDimensions( obj.primary_author.profile_image, - obj.primary_author.name, - true + `Ghost author profile image: ${obj.primary_author.name}` ); } @@ -94,8 +94,7 @@ const processBatch = async ({ obj.primary_author.image_dimensions.cover_image = await getImageDimensions( obj.primary_author.cover_image, - obj.primary_author.name, - true + `Ghost author cover image: ${obj.primary_author.name}` ); } @@ -106,8 +105,7 @@ const processBatch = async ({ tag.image_dimensions = { ...tag.image_dimensions }; tag.image_dimensions.feature_image = await getImageDimensions( tag.feature_image, - tag.name, - true + `Ghost tag feature image: ${tag.name}` ); } }) diff --git a/utils/hashnode/process-batch.js b/utils/hashnode/process-batch.js index 25aee7ef7..4217c9a52 100644 --- a/utils/hashnode/process-batch.js +++ b/utils/hashnode/process-batch.js @@ -24,7 +24,8 @@ const processBatch = async ({ newObj.title = oldObj.title; // Set the source of the publication and whether it's a page or post for tracking and later processing newObj.source = 'Hashnode'; - newObj.contentType = contentType === 'posts' ? 'post' : 'page'; + const singularContentType = contentType.slice(0, -1); + newObj.contentType = singularContentType; newObj.html = await modifyHTMLContent({ postContent: oldObj.content.html, @@ -56,7 +57,7 @@ const processBatch = async ({ newObj.image_dimensions = {}; newObj.image_dimensions.feature_image = await getImageDimensions( newObj.feature_image, - newObj.title + `Hashnode ${singularContentType} feature image: ${newObj.title}` ); const newObjAuthor = {}; @@ -88,8 +89,7 @@ const processBatch = async ({ newObjAuthor.image_dimensions = {}; newObjAuthor.image_dimensions.profile_image = await getImageDimensions( newObjAuthor.profile_image, - newObjAuthor.name, - true + `Hashnode author profile image: ${newObjAuthor.name}` ); } newObj.primary_author = newObjAuthor; diff --git a/utils/modify-html-content.js b/utils/modify-html-content.js index 779a93a85..156770a73 100644 --- a/utils/modify-html-content.js +++ b/utils/modify-html-content.js @@ -38,7 +38,10 @@ const modifyHTMLContent = async ({ postContent, postTitle, source }) => { images.map(async image => { // To do: swap out the image URLs here once we have them auto synced // with an S3 bucket - const { width, height } = await getImageDimensions(image.src, postTitle); + const { width, height } = await getImageDimensions( + image.src, + `Body image in ${postTitle}: ${image.src}` + ); image.setAttribute('width', width); image.setAttribute('height', height);