Created
July 1, 2018 14:00
-
-
Save shumbo/408840d5fbaa63ff6e330870cb53a134 to your computer and use it in GitHub Desktop.
Hugo image helper
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 v = require('vorpal')(); | |
const sharp = require('sharp'); | |
const child_process = require('child_process'); | |
const fs = require('fs'); | |
const path = require('path'); | |
const max_page_width = 967; // possible maximum page width | |
const max_width = { // list of sizes | |
full: Math.ceil(max_page_width * 2), // full width | |
normal: Math.ceil(max_page_width * 2 * 0.6), // 60% | |
small: Math.ceil(max_page_width * 2 * 0.4), // 40% | |
thumbnail: Math.ceil(1110 * 2) // thumbnail | |
}; | |
const postType = 'posts'; | |
const now = new Date(); | |
const zeroPadding = (x, l = 2) => String(x).padStart(l, '0'); | |
const yearString = now.getFullYear(); | |
const monthString = zeroPadding(now.getMonth() + 1); | |
const dateString = zeroPadding(now.getDate()); | |
const imageDestination = `./static/uploads/${yearString}/${monthString}`; | |
v.delimiter('blog$').show(); | |
v.command('hi', 'say hi').action(function(args, callback) { | |
this.log('hi'); | |
callback(); | |
}); | |
/** | |
* Make sure specified file is readable | |
* @param {string} p path to the file or directory | |
*/ | |
function fileExistCheck(p) { | |
return new Promise((resolve, reject) => | |
fs.access(p, fs.constants.R_OK, error => { | |
if (error) { | |
reject(error); | |
} else { | |
resolve(); | |
} | |
}) | |
); | |
} | |
/** | |
* Copy image while resizing to the specified size | |
* @param {string} input Input file path | |
* @param {string} output Output file path | |
* @param {string | number} size Size in name or number(width) | |
*/ | |
function resizeCopy(input, output, size) { | |
const maxWidth = | |
typeof size == 'string' | |
? max_width[size] | |
: typeof size == 'number' | |
? size | |
: 1200; | |
return sharp(input) | |
.resize(maxWidth) | |
.max() | |
.toFile(output); | |
} | |
/** | |
* Read text file | |
* @param {string} p | |
* @param {string} encoding | |
*/ | |
function readTextFile(p, encoding = 'utf-8') { | |
return new Promise((resolve, reject) => | |
fs.readFile( | |
p, | |
{ encoding }, | |
(err, data) => (err ? reject(err) : resolve(data)) | |
) | |
); | |
} | |
/** | |
* | |
* @param {string} p path to the file | |
* @param {string} data data to write | |
* @param {string} encoding | |
*/ | |
function writeTextFile(p, data, encoding = 'utf-8') { | |
return new Promise((resolve, reject) => { | |
fs.writeFile(p, data, { encoding }, err => (err ? reject(err) : resolve())); | |
}); | |
} | |
/** | |
* Resolve absolute path that can be used as url | |
* @param {string} p Path to the file | |
*/ | |
function resolveAbsoluteUrl(p) { | |
return p.replace('./static', ''); | |
} | |
/** | |
* mkdir -p | |
* @param {string} p | |
*/ | |
function mkdirp(p) { | |
return new Promise((resolve, reject) => | |
child_process.exec( | |
`mkdir -p ${p}`, | |
error => (error ? reject(error) : resolve()) | |
) | |
); | |
} | |
v.command( | |
'image <path> [size]', | |
'add image to appropriate directory and resize if needed' | |
) | |
.option('-o, --original', 'Do not resize and save original') | |
.action(function(args, callback) { | |
const tasks = async () => { | |
this.log('start image tasks', args); | |
const imgpath = args['path']; | |
const original = args.options['original'] || false; | |
const filename = path.basename(imgpath); | |
if (!filename) { | |
return; // path must be a file | |
} | |
// make sure file exists and readable | |
await fileExistCheck(imgpath); | |
let size = Object.keys(max_width).includes(args.size) ? args.size : null; | |
if (!size && !original) { | |
size = (await new Promise((resolve, reject) => { | |
this.prompt( | |
{ | |
type: 'list', | |
name: 'size', | |
message: 'Which size would you want to resize to?', | |
choices: Object.keys(max_width) | |
}, | |
resolve | |
); | |
})).size; | |
} | |
this.log('Creating directory...'); | |
await mkdirp(imageDestination); | |
let copiedImagePath; | |
if (args.options['original']) { | |
this.log('Copying file...'); | |
copiedImagePath = imageDestination + `/${filename}`; | |
await new Promise((resolve, reject) => | |
fs.copyFile( | |
imgpath, | |
copiedImagePath, | |
error => (error ? reject(error) : resolve()) | |
) | |
); | |
} else { | |
this.log('Resize and copy...'); | |
const elements = filename.split('.'); | |
elements.splice(-1, 0, size); | |
copiedImagePath = imageDestination + `/${elements.join('.')}`; | |
await resizeCopy(imgpath, copiedImagePath, size); | |
} | |
// Create snippet | |
if (size == 'thumbnail') { | |
this.log('Append the following to the top of the markdown:'); | |
const url = resolveAbsoluteUrl(copiedImagePath); | |
this.log(`thumbnail: "${url}"`); | |
} else { | |
this.log('Append the following to where you want the image to be:'); | |
const url = resolveAbsoluteUrl(copiedImagePath); | |
this.log(`![](${url})`); | |
} | |
}; | |
tasks() | |
.then(callback) | |
.catch(e => { | |
this.log(e); | |
this.ui.cancel(); | |
}); | |
}); | |
v.command('post <permalink> [title] [thumbnail]').action(function( | |
args, | |
callback | |
) { | |
const tasks = async () => { | |
this.log('Create file with hugo...'); | |
const mdfile = `${postType}/${yearString}${monthString}${dateString}-${ | |
args['permalink'] | |
}.md`; | |
await new Promise((resolve, reject) => | |
child_process.exec( | |
`hugo new ${mdfile}`, | |
error => (error ? reject(error) : resolve()) | |
) | |
); | |
const mdpath = path.resolve(`./content/${mdfile}`); | |
await fileExistCheck(mdpath); | |
if (args['thumbnail']) { | |
const imgpath = args['thumbnail']; | |
try { | |
await fileExistCheck(imgpath); | |
await mkdirp(imageDestination); | |
const filename = path.basename(imgpath); | |
if (!filename) { | |
throw new Error('A directory is specified as a thumbnail'); // path must be a file | |
} | |
const elements = filename.split('.'); | |
const size = 'thumbnail'; | |
elements.splice(-1, 0, size); | |
const copiedImagePath = imageDestination + `/${elements.join('.')}`; | |
await resizeCopy(imgpath, copiedImagePath, size); | |
const thumbnailUrl = resolveAbsoluteUrl(copiedImagePath); | |
// write to file | |
const current = await readTextFile(mdpath); | |
const lines = current.split('\n'); | |
lines.splice(1, 0, `thumbnail: "${resolveAbsoluteUrl(thumbnailUrl)}"`); | |
await writeTextFile(mdpath, lines.join('\n')); | |
} catch (e) { | |
this.log('Failed to create thumbnail: ', e); | |
} | |
} | |
if (args['title']) { | |
const content = (await readTextFile(mdpath)).split('\n').map(line => { | |
if(line.startsWith('title:')) { | |
line = `title: "${args['title']}"` | |
} | |
return line; | |
}).join('\n'); | |
await writeTextFile(mdpath, content); | |
} | |
}; | |
tasks() | |
.then(callback) | |
.catch(e => { | |
this.log(e); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment