UNPKG

posthtml-inline-assets

Version:
140 lines (109 loc) 4.17 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var filetype = _interopDefault(require('file-type')); var fs = _interopDefault(require('fse')); var path = _interopDefault(require('path')); var defaultTransforms = { image: { resolve(node) { return node.tag === 'img' && node.attrs && node.attrs.src; }, transform(node, data) { if (node.attrs.src.endsWith('.svg')) data.mime = 'image/svg+xml'; node.attrs.src = `data:${data.mime};base64,${data.buffer.toString('base64')}`; } }, script: { resolve(node) { return node.tag === 'script' && node.attrs && node.attrs.src; }, transform(node, data) { delete node.attrs.src; node.content = [data.buffer.toString('utf8')]; } }, style: { resolve(node) { return node.tag === 'link' && node.attrs && node.attrs.rel === 'stylesheet' && node.attrs.href; }, transform(node, data) { delete node.attrs.href; delete node.attrs.rel; node.tag = 'style'; node.content = [data.buffer.toString('utf8')]; } }, favicon: { resolve(node) { return node.tag === 'link' && node.attrs && node.attrs.rel === 'icon' && node.attrs.href; }, transform(node, data) { node.attrs.href = `data:${data.mime};base64,${data.buffer.toString('base64')}`; } } }; function errorHandler(resolution, message, messages) { if (resolution === 'throw') { throw new Error(message); } else if (resolution === 'warn') { messages.push(message); } } // tooling var index = (opts => { // initialize root from options const root = Object(opts).root; // initialize transform from options const transforms = Object(opts).transforms === false ? {} : assign(defaultTransforms, Object(opts).transforms || {}); return function posthtmlInlineAssets(tree) { // initialize current working directory from options const cwd = Object(opts).cwd ? path.resolve(opts.cwd) : Object(tree.options).from ? path.dirname(path.resolve(tree.options.from)) : process.cwd(); // initialize error handling from options const resolution = Object(opts).errors; // file promises const promises = []; // walk html nodes tree.walk(node => { // walk transforms Object.keys(transforms).forEach(type => { // transform const transform = Object(transforms[type]); // resolved asset file path const assetpath = typeof transform.resolve === 'function' && typeof transform.transform === 'function' && transform.resolve(node); if (typeof assetpath === 'string') { // absolute asset file path (used as "from") const from = root && path.isAbsolute(assetpath) ? path.join(root, assetpath) : path.resolve(cwd, assetpath); // add promise of asset contents promises.push(fs.readFile(from).then(buffer => { // file type (used as "mime") const mime = (filetype(buffer) || {}).mime; // transform the node return transform.transform(node, { from, buffer, mime }); }).catch(error => { // otherwise, handle any issues errorHandler(resolution, error, tree.messages); })); } }); return node; }); // resolve all inlined assets return Promise.all(promises).then(() => { // filter errors from messages as warnings const warnings = tree.messages.filter(message => message instanceof Error); if (warnings.length) { // conditionally warn the user about any issues console.warn(`\nWarnings (${warnings.length}):\n${warnings.map(message => ` ${message.message}`).join('\n')}\n`); } // return the ast return tree; }); }; }); function assign(objectA, objectB) { if (typeof objectA === 'object' && typeof objectB === 'object') { const objectC = Object.assign({}, objectA); for (let key in objectB) { objectC[key] = assign(objectA[key], objectB[key]); } return objectC; } else { return objectB; } } module.exports = index;