Skip to content

Instantly share code, notes, and snippets.

@emiliobondioli
Last active January 27, 2022 22:55
Show Gist options
  • Save emiliobondioli/5ce8ece783e7256fc7530738a2968ea9 to your computer and use it in GitHub Desktop.
Save emiliobondioli/5ce8ece783e7256fc7530738a2968ea9 to your computer and use it in GitHub Desktop.
Nuxt Magpie module
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, '-')
}
@d1urno
Copy link

d1urno commented Jun 23, 2020

Well, I'm going to explore the second option... less efficient is better than nothing, at least in my case 😄

@emiliobondioli
Copy link
Author

emiliobondioli commented Jun 23, 2020

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

@d1urno
Copy link

d1urno commented Jun 25, 2020

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