Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@ const hello="world";for(let l=0;l<hello.length;l++)console.log(hello[l]);

```js
const minify = require('minify');

minify('./client.js')
const options = {
html: {
removeAttributeQuotes: false,
removeOptionalTags: false
},
};

minify('./client.js', options)
.then(console.log)
.catch(console.error);

Expand All @@ -54,9 +60,15 @@ Or with `async-await` and [try-to-catch](https://github.com/coderaiser/try-to-ca
```js
const minify = require('minify');
const tryToCatch = require('try-to-catch');
const options = {
html: {
removeAttributeQuotes: false,
removeOptionalTags: false
}
};

async () => {
const [error, data] = await tryToCatch(minify, './client.js');
const [error, data] = await tryToCatch(minify, './client.js', options);

if (error)
return console.error(error.message);
Expand All @@ -65,6 +77,52 @@ async () => {
}();
```

## Options

The options object accepts configuration for `html`, `css`, `js`, and `img` like so:

```
const options = {
html: {
removeAttributeQuotes: false,
},
css: {
compatibility: '*',
},
js: {
ecma: 5,
},
img: {
maxSize: 4096,
}
}
```

Full documentation for options that each file type accepts can be found on the pages of the libraries used by minify to process the files:
- HTML: https://github.com/kangax/html-minifier
- CSS: https://github.com/jakubpawlowicz/clean-css
- JS: https://github.com/terser/terser
- IMG: https://github.com/Filirom1/css-base64-images

```

minify sets a few defaults for HTML that may differ from the base `html-minifier` settings:
- removeComments: true
- removeCommentsFromCDATA: true
- removeCDATASectionsFromCDATA: true
- collapseWhitespace: true
- collapseBooleanAttributes: true
- removeAttributeQuotes: true
- removeRedundantAttributes: true
- useShortDoctype: true
- removeEmptyAttributes: true
- removeEmptyElements: false
- removeOptionalTags: true
- removeScriptTypeAttributes: true
- removeStyleLinkTypeAttributes: true
- minifyJS: true
- minifyCSS: true

## License

MIT
Expand Down
7 changes: 5 additions & 2 deletions lib/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ const Clean = require('clean-css');
* minify css data.
*
* @param data
* @param userOptions - (optional) object that may contain a `css` key with an object of options
*/
module.exports = (data) => {
module.exports = (data, userOptions) => {
assert(data);

const options = userOptions && userOptions.css || {};

const {
styles,
errors,
} = new Clean().minify(data);
} = new Clean(options).minify(data);

const [error] = errors;

Expand Down
13 changes: 9 additions & 4 deletions lib/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
const assert = require('assert');
const Minifier = require('html-minifier');

const Options = {
const defaultOptions = {
removeComments: true,
removeCommentsFromCDATA: true,
removeCDATASectionsFromCDATA: true,
Expand All @@ -32,11 +32,16 @@ const Options = {
* minify html data.
*
* @param data
* @param callback
* @param userOptions - (optional) object that may contain an `html` key with an object of options
*/
module.exports = (data) => {
module.exports = (data, userOptions) => {
assert(data);

return Minifier.minify(data, Options);
const options = {
...defaultOptions,
...userOptions && userOptions.html || {},
};

return Minifier.minify(data, options);
};

16 changes: 11 additions & 5 deletions lib/img.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@ const fromString = promisify(require('css-b64-images').fromString);

const ONE_KB = 2 ** 10;

const maxSize = 100 * ONE_KB;
const defaultOptions = {
maxSize: 100 * ONE_KB,
};

/**
* minify css data.
* if can not minify return data
*
* @param name
* @param data
* @param userOptions - (optional) object that may contain an `img` key with an object of options
*/
module.exports = async (name, data) => {
module.exports = async (name, data, userOptions) => {
const dir = path.dirname(name);
const dirRelative = dir + '/../';

const options = {
...defaultOptions,
...userOptions && userOptions.img || {},
};

assert(name);
assert(data);

return fromString(data, dir, dirRelative, {
maxSize,
});
return fromString(data, dir, dirRelative, options);
};

7 changes: 5 additions & 2 deletions lib/js.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ const assert = require('assert');
* minify js data.
*
* @param data
* @param userOptions - (optional) object that may contain a `js` key with an object of options
*/
module.exports = (data) => {
module.exports = (data, userOptions) => {
assert(data);

const options = userOptions && userOptions.js || {};

const {
error,
code,
} = terser.minify(data);
} = terser.minify(data, options);

if (error)
throw error;
Expand Down
23 changes: 13 additions & 10 deletions lib/minify.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function check(name) {
throw Error('name could not be empty!');
}

async function minify(name) {
async function minify(name, userOptions) {
const EXT = ['js', 'html', 'css'];

check(name);
Expand All @@ -32,7 +32,7 @@ async function minify(name) {
throw Error(`File type "${ext}" not supported.`);

log('optimizing ' + path.basename(name));
return optimize(name);
return optimize(name, userOptions);
}

function getName(file) {
Expand All @@ -47,34 +47,37 @@ function getName(file) {
/**
* function minificate js,css and html files
*
* @param files - js, css or html file path
* @param {string} file - js, css or html file path
* @param {object} userOptions - object with optional `html`, `css, `js`, and `img` keys, which each can contain options to be combined with defaults and passed to the respective minifier
*/
async function optimize(file) {
async function optimize(file, userOptions) {
check(file);

const name = getName(file);

log('reading file ' + path.basename(name));

const data = await readFile(name, 'utf8');
return onDataRead(file, data);
return onDataRead(file, data, userOptions);
}

/**
* Processing of files
* @param fileData {name, data}
* Processing of files
* @param {string} filename
* @param {string} data - the contents of the file
* @param {object} userOptions - object with optional `html`, `css, `js`, and `img` keys, which each can contain options to be combined with defaults and passed to the respective minifier
*/
async function onDataRead(filename, data) {
async function onDataRead(filename, data, userOptions) {
log('file ' + path.basename(filename) + ' read');

const ext = path.extname(filename).replace(/^\./, '');

const optimizedData = await minify[ext](data);
const optimizedData = await minify[ext](data, userOptions);

let b64Optimize;

if (ext === 'css')
[, b64Optimize] = await tryToCatch(minify.img, filename, optimizedData);
[, b64Optimize] = await tryToCatch(minify.img, filename, optimizedData, userOptions);

return b64Optimize || optimizedData;
}
Expand Down
85 changes: 72 additions & 13 deletions test/minify.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,30 @@ const htmlMinifier = require('html-minifier');
test('js', async (t) => {
const js = 'function hello(world) {\nconsole.log(world);\n}';

const data = await minify.js(js);
const min = terser.minify(data).code;
const minifyOutput = await minify.js(js);
const terserOutput = terser.minify(js).code;

t.equal(data, min, 'js output should be equal');
t.equal(minifyOutput, terserOutput, 'js output should be equal');
t.end();
});

test('js: with alternate options', async (t) => {
const js = 'function isTrueFalse() { if (true !== false) { return true; } }';

const options = {
js: {
compress: {
booleans_as_integers: true,
},
},
};

const minifyOutputWithoutOptions = await minify.js(js);
const minifyOutput = await minify.js(js, options);
const terserOutput = terser.minify(js, options.js).code;

t.equal(minifyOutput, terserOutput, 'js output should be equal');
t.notEqual(minifyOutput, minifyOutputWithoutOptions, 'options should influence the output');
t.end();
});

Expand Down Expand Up @@ -46,32 +66,71 @@ test('html', async (t) => {
minifyCSS: true,
};

const data = await minify.html(html);
const min = htmlMinifier.minify(data, options);
const minifyOutput = await minify.html(html);
const htmlMinifierOutput = htmlMinifier.minify(html, options);

t.equal(minifyOutput, htmlMinifierOutput, 'html output should be equal');
t.end();
});

test('html: with alternate options', async (t) => {
const html = '<html>\n<body>\nhello world\n</body></html>';

const options = {
html: {
removeOptionalTags: false,
},
};

const minifyOutputWithoutOptions = await minify.html(html);
const minifyOutput = await minify.html(html, options);
const htmlMinifierOutput = htmlMinifier.minify(minifyOutput, options.html);

t.equal(data, min, 'html output should be equal');
t.equal(minifyOutput, htmlMinifierOutput, 'html output should be equal');
t.notEqual(minifyOutput, minifyOutputWithoutOptions, 'options should influence output');
t.end();
});

test('css', async (t) => {
const css = 'color: #FFFFFF';

const data = await minify.css(css);
const minifyOutput = await minify.css(css);
const {styles} = new CleanCSS().minify(css);

t.equal(data, styles, 'css output should be equal');
t.equal(minifyOutput, styles, 'css output should be equal');
t.end();
});

test('css: with alternate options', async (t) => {
const css = '.gradient { -ms-filter: \'progid:DXImageTransform.Microsoft.Gradient(startColorStr="#ffffff", endColorStr="#000000", GradientType=1)\'; background-image: linear-gradient(to right, #ffffff 0%, #000000 100%); }';
const options = {
css: {
compatibility: {
properties: {
ieFilters: true,
},
},
},
};

const minifyOutputWithoutOptions = await minify.css(css);
const minifyOutput = await minify.css(css, options);
const {styles} = new CleanCSS(options.css).minify(css);

t.equal(minifyOutput, styles, 'css output should be equal');
t.notEqual(minifyOutput, minifyOutputWithoutOptions, 'options should influence output');
t.end();
});

test('css: base64', async (t) => {
const dir = `${__dirname}/fixtures`;
const name = `${dir}/style.css`;
const nameMin = `${dir}/style.min.css`;
const pathToCSS = `${dir}/style.css`;
const pathToMinifiedCSS = `${dir}/style.min.css`;

const min = fs.readFileSync(nameMin, 'utf8');
const data = await minify(name);
const minifiedCSS = fs.readFileSync(pathToMinifiedCSS, 'utf8');
const outputCSS = await minify(pathToCSS);

t.equal(data, min, 'should equal');
t.equal(outputCSS, minifiedCSS, 'should be equal');
t.end();
});

Expand Down