/* @flow */ | |
import utils from 'loader-utils'; | |
import size from 'image-size'; | |
import path from 'path'; | |
import hasha from 'hasha'; | |
import AssetResolver from './AssetResolver'; | |
import type { Loader } from 'webpack'; | |
type Config = { | |
platform: string, | |
root: string, | |
outputPath?: string | ((path: string) => string), | |
publicPath?: string | ((path: string) => string), | |
}; | |
async function assetLoader() { | |
(this: Loader); | |
this.cacheable(); | |
const callback = this.async(); | |
const query = utils.getOptions(this); | |
const options = this.options[query.config]; | |
const config: Config = Object.assign({}, options, query); | |
let info: ?{ width: number, height: number, type: string }; | |
try { | |
info = size(this.resourcePath); | |
} catch (e) { | |
// Asset is not an image | |
} | |
const filepath = this.resourcePath; | |
const dirname = path.dirname(filepath); | |
const url = path.relative(config.root, dirname); | |
const type = path.extname(filepath).replace(/^\./, ''); | |
const suffix = `(@\\d+(\\.\\d+)?x)?(\\.(${config.platform}|native))?\\.${type}$`; | |
const filename = path.basename(filepath).replace(new RegExp(suffix), ''); | |
const longname = `${`${url.replace(/\//g, '_')}_${filename}` | |
.toLowerCase() | |
.replace(/[^a-z0-9_]/g, '')}`; | |
const result = await new Promise((resolve, reject) => | |
this.fs.readdir(dirname, (err, res) => { | |
if (err) { | |
reject(err); | |
} else { | |
resolve(res); | |
} | |
}) | |
); | |
const map = AssetResolver.collect(result, { | |
name: filename, | |
type, | |
platform: config.platform, | |
}); | |
const scales = Object.keys(map) | |
.map(s => Number(s.replace(/[^\d.]/g, ''))) | |
.sort(); | |
const pairs = await Promise.all( | |
Object.keys(map).map(scale => { | |
this.addDependency(path.join(dirname, map[scale].name)); | |
return new Promise((resolve, reject) => | |
this.fs.readFile(path.join(dirname, map[scale].name), (err, res) => { | |
if (err) { | |
reject(err); | |
} else { | |
const name = `${longname}${scale === '@1x' ? '' : scale}.${type}`; | |
const dest = path.join('assets', name); | |
resolve({ | |
destination: dest, | |
content: res, | |
}); | |
} | |
}) | |
); | |
}) | |
); | |
pairs.forEach(item => { | |
let dest = item.destination; | |
if (config.outputPath) { | |
// support functions as outputPath to generate them dynamically | |
dest = | |
typeof config.outputPath === 'function' | |
? config.outputPath(dest) | |
: path.join(config.outputPath, dest); | |
} | |
this.emitFile(dest, item.content); | |
}); | |
const buffers = pairs.map(item => item.content); | |
const hashes = buffers.map(b => hasha(b, { algorithm: 'md5' })); | |
let publicPath = `__webpack_public_path__ + ${JSON.stringify(path.join('/', 'assets'))}`; | |
if (config.publicPath) { | |
// support functions as publicPath to generate them dynamically | |
publicPath = JSON.stringify( | |
typeof config.publicPath === 'function' | |
? config.publicPath(url) | |
: path.join(config.publicPath, url) | |
); | |
} | |
callback( | |
null, | |
` | |
var AssetRegistry = require('AssetRegistry'); | |
module.exports = AssetRegistry.registerAsset({ | |
httpServerLocation: ${publicPath}, | |
name: ${JSON.stringify(longname)}, | |
width: ${info ? info.width : JSON.stringify(null)}, | |
height: ${info ? info.height : JSON.stringify(null)}, | |
type: ${JSON.stringify(type)}, | |
hash: ${JSON.stringify(hashes.join())}, | |
fileHashes: ${JSON.stringify(hashes)}, | |
scales: ${JSON.stringify(scales)}, | |
}); | |
` | |
); | |
} | |
module.exports = assetLoader; | |
module.exports.raw = true; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment