You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
core/src/factories/media.js

256 lines
6.7 KiB

1 year ago
const path = require('path')
const fs = require('fs')
const sharp = require('sharp')
const mkdirp = require('mkdirp')
const crypto = require('crypto')
const slugify = require('slugify')
1 year ago
const assign = require('assign-deep')
1 year ago
const configStore = require('./../config.js')
/**
1 year ago
* Media
1 year ago
*
1 year ago
* change size, optimize and copy media to assets
*
* @author Björn Hase <me@herr-hase.wtf>
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.node001.net/HerrHase/siteomat-webpack-plugin.git
1 year ago
*
*/
1 year ago
1 year ago
class Media {
1 year ago
constructor(dirPath = NULL) {
this._path = dirPath
1 year ago
this._DIR_ASSETS = '/assets/'
}
/**
1 year ago
* resolve media
1 year ago
*
* @param {string} src
* @param {object} sizes
* @param {Object} [options={}]
* @return {string}
*
*/
1 year ago
resolve(src, sizes = {}, options = {}) {
1 year ago
1 year ago
let extension = path.extname(src)
let sourcePath
const filename = slugify(path.basename(src, extension))
1 year ago
1 year ago
// check for images in path
1 year ago
sourcePath = this._getSourcePath(src)
1 year ago
// getting sharp
const process = sharp(sourcePath)
1 year ago
1 year ago
if (extension === '.gif') {
process
.gif({
reoptimise: true
})
1 year ago
} else {
1 year ago
// change extension
extension = '.webp'
process
.webp({
lossless: true
})
1 year ago
}
1 year ago
// destination
const destinationPath = this._getDestinationPath(sourcePath)
// create files to write
const filesToWrite = this._getFilesToWrite(filename, extension, destinationPath, sizes)
1 year ago
1 year ago
// results contains only path as strings
const results = {}
// create path if not exists
if (!fs.existsSync(configStore.get('destination') + destinationPath)) {
mkdirp.sync(configStore.get('destination') + destinationPath)
}
1 year ago
1 year ago
filesToWrite.forEach((file) => {
if (!fs.existsSync(configStore.get('destination') + file.path)) {
this._writeFile(file, process, options)
}
1 year ago
1 year ago
results[file.name] = file.path
})
1 year ago
1 year ago
return this._reduce(results)
1 year ago
}
1 year ago
/**
* get source path
*
* @param {String} src
* @return {String}
*
*/
_getSourcePath(src) {
let sourcePath = configStore.get('source') + '/' + src
if (Array.isArray(this._path)) {
if (fs.existsSync(configStore.get('source') + this._path[0] + '/' + src)) {
sourcePath = configStore.get('source') + this._path[0] + '/' + src
} else if (fs.existsSync(configStore.get('source') + this._path[1] + '/' + src)) {
sourcePath = configStore.get('source') + this._path[1] + '/' + src
}
} else if (this._path && fs.existsSync(configStore.get('source') + this._path + '/' + src)) {
sourcePath = configStore.get('source') + this._path + '/' + src
}
return sourcePath
}
1 year ago
/**
1 year ago
* if only full is in results, reduce object to string
1 year ago
*
1 year ago
* @param {Object} results
* @return {mixed}
1 year ago
*
1 year ago
*/
_reduce(results) {
if (Object.getOwnPropertyNames(results).length === 1) {
results = results['full']
}
return results
}
/**
* getting files to write
*
* @param {string} src
1 year ago
* @param {string} extension
1 year ago
* @param {Object} sizes
* @return {string}
1 year ago
*/
1 year ago
_getFilesToWrite(filename, extension, destinationPath, sizes) {
const results = []
// add orginal
results.push(this._getFile(filename, destinationPath, extension))
// check for sizes
if (typeof sizes === 'object' && !Array.isArray(sizes)) {
results.push(this._getFile(filename, destinationPath, extension, sizes))
} else if (Array.isArray(sizes)) {
sizes.forEach((size) => {
results.push(this._getFile(filename, destinationPath, extension, size))
})
}
1 year ago
1 year ago
return results
}
/**
* write files to destination
*
* @param {string} file
* @param {object} process
* @param {Object} options
*/
_writeFile(file, process, options) {
1 year ago
// if sizes have height or width with no optional parameters then check for merge of options
if (file.sizes &&
(((!file.sizes.height || !file.sizes.width) && Object.getOwnPropertyNames(file.sizes).length === 1) ||
(file.sizes.height && file.sizes.width && Object.getOwnPropertyNames(file.sizes).length === 2))) {
process.resize(this._mergeOptions(file.sizes, options))
// if already options in sizes ignore options
} else if (file.sizes) {
1 year ago
process.resize(file.sizes)
1 year ago
}
1 year ago
process.toFile(configStore.get('destination') + file.path)
1 year ago
}
1 year ago
/**
* if options are exists merge them with sizes
*
* @param {object} sizes
* @param {Object} options
*/
_mergeOptions(sizes, options) {
if (Object.getOwnPropertyNames(options).length > 0) {
sizes = assign(options, sizes)
}
return sizes
}
1 year ago
/**
1 year ago
* generate destination path from hash of file
1 year ago
*
1 year ago
* @param {string}
1 year ago
* @return {string}
*/
1 year ago
_getDestinationPath(sourcePath) {
1 year ago
const hash = crypto.createHash('sha1')
1 year ago
const file = fs.readFileSync(sourcePath)
// getting hash from file
hash.update(file)
return this._DIR_ASSETS + hash.digest('hex').match(new RegExp('.{1,8}', 'g')).join('/')
}
/**
* create file as object, adding path, name with sizes
*
* @param {string} filename
* @param {object} destinationPath
* @param {Object} extension
* @param {sizes} sizes
* @return {object}
*/
_getFile(filename, destinationPath, extension, sizes = undefined) {
let file = {
name: ''
}
let prefix = ''
// check for sizes
if (sizes && sizes.width) {
file.name += sizes.width
}
if (sizes && sizes.height) {
if (sizes.width) {
prefix = 'x'
}
file.name += prefix + sizes.height
}
// create path before name is set to orginal as fallback
file.path = destinationPath + '/' + filename + file.name + extension
if (!file.name) {
file.name = 'full'
}
if (sizes) {
file.sizes = sizes
}
1 year ago
1 year ago
return file
1 year ago
}
}
1 year ago
module.exports = Media