Socket
Socket
Sign inDemoInstall

postcss-image-inliner

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

postcss-image-inliner - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

139

index.js

@@ -1,100 +0,65 @@

'use strict';
const postcss = require('postcss');
const Bluebird = require('bluebird');
const isString = require('lodash.isstring');
const defaults = require('lodash.defaults');
const debug = require('debug')('image-inliner');
const escape = require('lodash.escaperegexp');
const last = require('lodash.last');
const reduce = require('lodash.reduce');
const filesize = require('filesize');
const getResource = require('asset-resolver').getResource;
const getDataUri = require('./lib/image').getDataUri;
const last = require('lodash/last');
const escapeRegExp = require('lodash/escapeRegExp');
const {getDataUriMapping} = require('./lib/image');
module.exports = postcss.plugin('postcss-image-inliner', opts => {
opts = defaults(opts || {}, {
assetPaths: [],
maxFileSize: 10240,
b64Svg: false,
strict: false
});
const DEFAULTS = {
assetPaths: [],
maxFileSize: 10240,
b64Svg: false,
strict: false
};
if (isString(opts.assetPaths)) {
opts.assetPaths = [opts.assetPaths];
const loop = cb => {
const matcher = /url\(\s*(?:['"]*)(?!['"]*data:)(.*?)(?:['"]*)\s*\)/gm;
return decl => {
let match;
while ((match = matcher.exec(decl.value)) !== null) {
cb({decl, url: last(match)});
}
};
};
opts.assetPaths.push(process.cwd());
module.exports = postcss.plugin('postcss-image-inliner', (opts = {}) => {
const options = {...DEFAULTS, ...opts};
function assertSize(resource) {
const encoding = resource.mime === 'image/svg+xml' ? 'utf-8' : 'binary';
const size = Buffer.byteLength(resource.contents, encoding);
if (opts.maxFileSize && opts.maxFileSize < size) {
const msg = 'Too big. ' + filesize(size) + ' exceeds the allowed ' + filesize(opts.maxFileSize);
debug(msg);
return Bluebird.reject(new Error(msg));
}
if (Array.isArray(options.assetPaths)) {
options.assetPaths = [...options.assetPaths, process.cwd()];
} else {
options.assetPaths = [options.assetPaths, process.cwd()];
}
return resource;
}
return async css => {
const urls = new Set([]);
const filter = /^(background(?:-image)?)|(content)|(cursor)/;
// Get urls
css.walkDecls(filter, loop(({url}) => urls.add(url)));
function resolveUrl(filepath) {
return getResource(filepath, {
base: opts.assetPaths,
filter: assertSize
}).catch(err => {
debug(err.message, filepath, 'could not be resolved');
});
}
const mapping = await getDataUriMapping([...urls], options);
function loop(cb) {
const matcher = /url\(\s*(?:['"]*)(?!['"]*data:)(.*?)(?:['"]*)\s*\)/gm;
return function (decl) {
let match;
while ((match = matcher.exec(decl.value)) !== null) {
cb(decl, last(match));
}
};
}
css.walkDecls(
filter,
loop(({decl, url}) => {
if (mapping[url]) {
const regexp = new RegExp('[\'"]?' + escapeRegExp(url) + '[\'"]?', 'gm');
if (url.startsWith('//')) {
// Console.log({url, regexp, val: decl.value});
}
function compact(data) {
return reduce(data, (acc, file, key) => {
if (file && file.mime) {
acc[key] = file;
}
return acc;
}, {});
}
decl.value = decl.value.replace(regexp, '\'' + mapping[url] + '\'');
if (url.startsWith('//')) {
// Console.log('RESULT: ', decl.value);
}
return function (css) {
const replacements = {};
const filter = /^(background(?:-image)?)|(content)|(cursor)/;
css.walkDecls(filter, loop((decl, url) => {
replacements[url] = resolveUrl(url);
}));
return Bluebird.props(replacements)
.then(compact)
.then(file => {
return getDataUri(file, opts);
})
.then(data => {
css.walkDecls(filter, loop((decl, url) => {
if (data[url]) {
const regexp = new RegExp('[\'"]?' + escape(url) + '[\'"]?');
decl.value = decl.value.replace(regexp, '\'' + data[url] + '\'');
debug(url, 'successfully replaced with ', data[url]);
} else {
debug(url, 'failed');
if (opts.strict) {
throw new Error('No file found for ' + url);
}
}
}));
}).catch(err => {
debug(err);
return new Bluebird((resolve, reject) => {
reject(err);
});
});
};
debug(url, 'successfully replaced with ', mapping[url]);
} else {
debug(url, 'failed');
if (opts.strict) {
throw new Error('No file found for ' + url);
}
}
})
);
};
});
'use strict';
const path = require('path');
const SVGO = require('svgo');
const filesize = require('filesize');
const debug = require('debug')('image-inliner');
const map = require('lodash.map');
const SVGO = require('svgo');
const Bluebird = require('bluebird');
const {getResource} = require('asset-resolver');

@@ -11,49 +12,80 @@ const svgo = new SVGO();

function encodeSvg(content) {
return encodeURIComponent(content
return (
encodeURIComponent(
content
// Strip newlines and tabs
.replace(/[\n\r]/gmi, '')
.replace(/\t/gmi, ' ')
.replace(/[\n\r]/gim, '')
.replace(/\t/gim, ' ')
// Strip comments
.replace(/<!--(.*(?=-->))-->/gmi, '')
.replace(/<!--(.*(?=-->))-->/gim, '')
// Replace
.replace(/'/gmi, '\\i'))
// Encode brackets
.replace(/\(/g, '%28').replace(/\)/g, '%29')
// Replace ' with "
.replace(/'/gm, '"');
.replace(/'/gim, '\\i')
)
// Encode brackets
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
// Replace ' with "
.replace(/'/gm, '"')
);
}
function computeDataUri(opts) {
return function (file, key) {
// If the url is SVG, let's compress and use the XML directly
if (file.mime === 'image/svg+xml' && !opts.b64Svg) {
return svgo.optimize(file.contents.toString('utf-8')).then(result => {
debug('optimising svg');
return {
data: 'data:image/svg+xml;charset=US-ASCII,' + encodeSvg(result.data),
key
};
}).catch(err => {
debug('errored', err.message);
throw err;
});
}
async function reduceAsync(array = [], reducer = r => r, initial) {
for (const index of array.keys()) {
initial = await reducer(initial, array[index], index); /* eslint-disable-line no-await-in-loop */
}
// Otherwise we base64 encode the image
return {
data: 'data:' + file.mime + ';base64,' + Buffer.from(file.contents, 'binary').toString('base64'),
key
};
};
return initial;
}
function getDataUri(res, opts) {
const promises = map(res, computeDataUri(opts));
async function assertSize(resource, maxFileSize) {
const {mime, contents = ''} = resource || {};
const encoding = mime === 'image/svg+xml' ? 'utf-8' : 'binary';
const size = Buffer.byteLength(contents, encoding);
if (maxFileSize && maxFileSize < size) {
const msg = 'Too big. ' + filesize(size) + ' exceeds the allowed ' + filesize(maxFileSize);
throw new Error(msg);
}
return Bluebird.reduce(promises, (uris, resource) => {
uris[resource.key] = resource.data;
return uris;
}, {});
return resource;
}
module.exports.getDataUri = getDataUri;
async function getDataUri(file, opts) {
// If the url is SVG, let's compress and use the XML directly
if (file.mime === 'image/svg+xml' && !opts.b64Svg) {
const optimized = await svgo.optimize(file.contents.toString('utf-8'));
return `data:image/svg+xml;charset=US-ASCII,${encodeSvg(optimized.data)}`;
}
// Otherwise we base64 encode the image
return 'data:' + file.mime + ';base64,' + Buffer.from(file.contents, 'binary').toString('base64');
}
async function resolve(filepath, {assetPaths, maxFileSize, resolver = () => undefined}) {
const filter = resource => assertSize(resource, maxFileSize);
const resolvedFile = await resolver(filepath);
const base = resolvedFile ? path.dirname(resolvedFile) : assetPaths;
const file = resolvedFile ? path.basename(resolvedFile) : filepath;
try {
return await getResource(file, {base, filter});
} catch (err) {
debug(err.message, filepath, 'could not be resolved');
}
}
function getDataUriMapping(urls = [], options = {}) {
return reduceAsync(
[...new Set(urls)],
async (result, url) => {
const file = await resolve(url, options);
if (file && file.mime && /image/.test(file.mime)) {
result[url] = await getDataUri(file, options);
}
return result;
},
{}
);
}
module.exports.getDataUriMapping = getDataUriMapping;
{
"name": "postcss-image-inliner",
"version": "2.0.0",
"version": "3.0.0",
"description": "PostCSS plugin to inline images into css",

@@ -24,39 +24,32 @@ "keywords": [

"dependencies": {
"asset-resolver": "^1.0.0",
"bluebird": "^3.5.1",
"debug": "3.1.0",
"filesize": "^3.5.11",
"lodash.defaults": "4.2.0",
"lodash.escaperegexp": "4.1.2",
"lodash.isstring": "4.0.1",
"lodash.last": "3.0.0",
"lodash.map": "4.6.0",
"lodash.partialright": "4.2.1",
"lodash.reduce": "4.6.0",
"lodash.reject": "4.6.0",
"lodash.result": "4.5.2",
"mime": "2.0.3",
"object-hash": "^1.2.0",
"postcss": "^6.0.14",
"request": "^2.83.0",
"svgo": "^1.0.3",
"then-fs": "2.0.0"
"asset-resolver": "^2.0.0",
"debug": "^4.1.1",
"eslint-config-xo-space": "^0.21.0",
"filesize": "^4.0.0",
"lodash": "^4.17.11",
"postcss": "^7.0.13",
"svgo": "^1.1.1"
},
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^4.13.1",
"eslint-config-xo": "^0.19.0",
"finalhandler": "^1.1.0",
"gulp": "3.9.1",
"gulp-eslint": "^4.0.0",
"gulp-mocha": "4.3.1",
"mocha": "^4.0.1",
"serve-static": "^1.13.1"
"chai": "^4.2.0",
"eslint": "^5.12.1",
"finalhandler": "^1.1.1",
"mocha": "^5.2.0",
"serve-static": "^1.13.2"
},
"scripts": {
"test": "gulp"
"test": "eslint *.js && mocha test/*.js"
},
"prettier": {
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 120,
"bracketSpacing": false
},
"eslintConfig": {
"extends": "xo-space"
},
"engines": {
"node": ">=6.0"
"node": ">=8.6"
}
}

@@ -1,2 +0,2 @@

# PostCSS Image Inliner [![Build Status][ci-img]][ci]
# PostCSS Image Inliner [![Build Status][ci-img]][ci] [![Windows Build Status][winci-img]][winci]

@@ -8,2 +8,4 @@ [PostCSS] plugin to inline local/remote images.

[ci]: https://travis-ci.org/bezoerb/postcss-image-inliner
[winci-img]: https://ci.appveyor.com/api/projects/status/5xaq0ord84y5ho0c?svg=true
[winci]: https://ci.appveyor.com/project/bezoerb/postcss-image-inliner

@@ -10,0 +12,0 @@ ```css

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc