Last active
January 27, 2022 22:55
-
-
Save emiliobondioli/5ce8ece783e7256fc7530738a2968ea9 to your computer and use it in GitHub Desktop.
Nuxt Magpie module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs') | |
const { URL } = require('url') | |
const { join } = require('path') | |
const axios = require('axios') | |
const consola = require('consola') | |
const defaults = { | |
path: '/_images', // dir where downloaded images will be stored | |
extensions: ['jpg', 'jpeg', 'gif', 'png', 'webp'], | |
baseUrl: '' // cms url | |
// TODO: add option to allow keeping the original folder structure | |
} | |
export default function Magpie(moduleOptions) { | |
const options = { ...defaults, ...moduleOptions } | |
const baseDir = join(this.options.generate.dir, options.path) | |
this.nuxt.hook('generate:distCopied', () => { | |
if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir) | |
}) | |
this.nuxt.hook('generate:page', async (page) => { | |
const urls = [] | |
const test = new RegExp('(http(s?):)([/|.|\\w|\\s|-])*\.(?:' + options.extensions.join('|') + ')', 'g') | |
const matches = page.html.matchAll(test) | |
for (const match of matches) { | |
const baseUrl = URL(moduleOptions.baseUrl) | |
const url = URL(match[0]) | |
if (baseUrl.hostname === url.hostname && !urls.find(u => u.href === url.href)) { | |
urls.push(url) | |
} | |
} | |
if (!urls.length) return | |
consola.info(`${page.route}: Magpie is replacing ${urls.length} images with local copies`) | |
return await replaceRemoteImages(page.html, urls).then(html => page.html = html) | |
}) | |
async function replaceRemoteImages(html, urls) { | |
await Promise.all(urls.map(async (url) => { | |
const ext = '.' + url.pathname.split('.').pop() | |
const name = slugify(url.pathname.split(ext).join('')) + ext | |
const imgPath = join(baseDir, name) | |
return saveRemoteImage(url.href, imgPath) | |
.then(() => { | |
html = html.split(url.href).join(options.path + '/' + name) | |
}) | |
.catch(e => consola.error(e)) | |
})) | |
return html | |
} | |
} | |
function saveRemoteImage(url, path) { | |
return axios({ | |
url, | |
responseType: 'stream' | |
}).then( | |
response => | |
new Promise((resolve, reject) => { | |
response.data | |
.pipe(fs.createWriteStream(path)) | |
.on('finish', () => resolve()) | |
.on('error', e => reject(e)) | |
}) | |
) | |
} | |
// https://gist.github.com/codeguy/6684588 | |
function slugify(text) { | |
return text | |
.toString() | |
.toLowerCase() | |
.normalize('NFD') | |
.trim() | |
.replace('/', '') | |
.replace(/\s+/g, '-') | |
.replace(/[^\w\-]+/g, '-') | |
.replace(/\-\-+/g, '-') | |
} |
I can think of 2 options:
- Hooking into nuxt export process and replacing the urls in the payload before it is saved, for this we'd need an additional hook since from what I can see there is no export hook which gives me the payload for each route. I've tried adding it in the nuxt source files (somewhere around here) and it is working fine, albeit a bit hackish because the payload has already been stringified at that point. I've asked the team about adding that hook but I've received no answer so far.
- Hooking into
export:routeCreated
to get the payload.js for each route, read it, replace image urls and save it again. This would be the best approach right now because it doesn't require changes to nuxt source, but is also way less efficient because it adds a read and write operation for each generated route.
Well, I'm going to explore the second option... less efficient is better than nothing, at least in my case 😄
If you want, I'd be happy to see what solution you came up with!
As a heads up, each time nuxt runs a full static generation, a new dir with the version number is created, so to get the correct path for each route you can do:
this.nuxt.hook('export:routeCreated', async ({ route, path, errors }) => {
const routePath = join(this.options.generate.dir, this.options.generate.staticAssets.versionBase, route)
const payloadPath = join(routePath, 'payload.js')
// ex. payloadPath = /dist/_nuxt/static/1592921605/about/payload.js
Thanks for the payload path information, it helped me. After a hard time on RegEx and string manipulation... I've managed to complete a working version. However, it needs more testing to be production ready.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you! I've managed to create a little module adapted for v2.13 nuxt-image-extractor, it's working fine, the only missing thing is payload replacement.
Please if you have any idea on this, I'll be glad to help!