Skip to content

Commit

Permalink
Merge pull request #588 from alexlamsl/cli-config
Browse files Browse the repository at this point in the history
fix cli.js regex option parsing when using JSON file
  • Loading branch information
kangax committed Mar 28, 2016
2 parents ee9358c + 162d4f2 commit f5fe726
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 78 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Pe
| [Google](https://www.google.com/) | 133 | **128** | 132 | 135 | 131 |
| [MSN](http://www.msn.com/) | 157 | **130** | 138 | 145 | 138 |
| [Stackoverflow](http://stackoverflow.com/) | 200 | **159** | 165 | 173 | 166 |
| [Amazon](http://www.amazon.com/) | 246 | **204** | 234 | 230 | 219 |
| [Bootstrap CSS](http://getbootstrap.com/css/) | 278 | **269** | 275 | 232 | 278 |
| [Amazon](http://www.amazon.com/) | 246 | **203** | 234 | 230 | 219 |
| [Bootstrap CSS](http://getbootstrap.com/css/) | 277 | **268** | 274 | 232 | 277 |
| [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) | 401 | **367** | 388 | 400 | n/a |
| [Eloquent Javascript](http://eloquentjavascript.net/1st_edition/print.html) | 870 | **827** | 840 | 864 | n/a |
| [ES6 draft](https://tc39.github.io/ecma262/) | 3678 | **2991** | 3079 | 3183 | n/a |
Expand Down
146 changes: 72 additions & 74 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ var appVersion = require('./package.json').version;
var minify = require('.').minify;
var HTMLLint = require('./src/htmllint').HTMLLint;
var minifyOptions = {};
var input = null;
var output = null;
var output;

cli.width = 100;
cli.option_width = 40;
Expand All @@ -55,38 +54,42 @@ usage += ' on the command line you must escape those such as --ignore-custom-
cli.setUsage(usage);

var mainOptions = {
removeComments: [[false, 'Strip HTML comments']],
removeCommentsFromCDATA: [[false, 'Strip HTML comments from scripts and styles']],
removeCDATASectionsFromCDATA: [[false, 'Remove CDATA sections from script and style elements']],
caseSensitive: [[false, 'Treat attributes in case sensitive manner (useful for SVG; e.g. viewBox)']],
collapseBooleanAttributes: [[false, 'Omit attribute values from boolean attributes']],
collapseInlineTagWhitespace: [[false, 'Collapse white space around inline tag']],
collapseWhitespace: [[false, 'Collapse white space that contributes to text nodes in a document tree.']],
conservativeCollapse: [[false, 'Always collapse to 1 space (never remove it entirely)']],
customAttrAssign: [[false, 'Arrays of regex\'es that allow to support custom attribute assign expressions (e.g. \'<div flex?="{{mode != cover}}"></div>\')', 'string'], 'json-regex'],
customAttrCollapse: [[false, 'Regex that specifies custom attribute to strip newlines from (e.g. /ng\-class/)', 'string'], 'string-regex'],
customAttrSurround: [[false, 'Arrays of regex\'es that allow to support custom attribute surround expressions (e.g. <input {{#if value}}checked="checked"{{/if}}>)', 'string'], 'json-regex'],
customEventAttributes: [[false, 'Arrays of regex\'es that allow to support custom event attributes for minifyJS (e.g. ng-click)', 'string'], 'json-regex'],
html5: [[false, 'Parse input according to HTML5 specifications']],
ignoreCustomComments: [[false, 'Array of regex\'es that allow to ignore certain comments, when matched', 'string'], 'json-regex'],
ignoreCustomFragments: [[false, 'Array of regex\'es that allow to ignore certain fragments, when matched (e.g. <?php ... ?>, {{ ... }})', 'string'], 'json-regex'],
includeAutoGeneratedTags: [[false, 'Insert tags generated by HTML parser'], true],
keepClosingSlash: [[false, 'Keep the trailing slash on singleton elements']],
lint: [[false, 'Toggle linting']],
maxLineLength: [[false, 'Max line length', 'number'], true],
minifyCSS: [[false, 'Minify CSS in style elements and style attributes (uses clean-css)']],
minifyJS: [[false, 'Minify Javascript in script elements and on* attributes (uses UglifyJS)']],
minifyURLs: [[false, 'Minify URLs in various attributes (uses relateurl)', 'string'], 'site-url'],
preserveLineBreaks: [[false, 'Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break.']],
collapseInlineTagWhitespace: [[false, 'Collapse white space around inline tag']],
collapseBooleanAttributes: [[false, 'Omit attribute values from boolean attributes']],
removeTagWhitespace: [[false, 'Remove space between attributes whenever possible.']],
removeAttributeQuotes: [[false, 'Remove quotes around attributes when possible.']],
removeRedundantAttributes: [[false, 'Remove attributes when value matches default.']],
preventAttributesEscaping: [[false, 'Prevents the escaping of the values of attributes.']],
useShortDoctype: [[false, 'Replaces the doctype with the short (HTML5) doctype']],
processConditionalComments: [[false, 'Process contents of conditional comments through minifier']],
processScripts: [[false, 'Array of strings corresponding to types of script elements to process through minifier (e.g. "text/ng-template", "text/x-handlebars-template", etc.)', 'string'], 'json'],
quoteCharacter: [[false, 'Type of quote to use for attribute values (\' or ")', 'string'], true],
removeAttributeQuotes: [[false, 'Remove quotes around attributes when possible.']],
removeCDATASectionsFromCDATA: [[false, 'Remove CDATA sections from script and style elements']],
removeComments: [[false, 'Strip HTML comments']],
removeCommentsFromCDATA: [[false, 'Strip HTML comments from scripts and styles']],
removeEmptyAttributes: [[false, 'Remove all attributes with whitespace-only values']],
removeEmptyElements: [[false, 'Remove all elements with empty contents']],
removeOptionalTags: [[false, 'Remove unrequired tags']],
removeRedundantAttributes: [[false, 'Remove attributes when value matches default.']],
removeScriptTypeAttributes: [[false, 'Remove type="text/javascript" from script tags. Other type attribute values are left intact.']],
removeStyleLinkTypeAttributes: [[false, 'Remove type="text/css" from style and link tags. Other type attribute values are left intact.']],
removeOptionalTags: [[false, 'Remove unrequired tags']],
removeEmptyElements: [[false, 'Remove all elements with empty contents']],
lint: [[false, 'Toggle linting']],
keepClosingSlash: [[false, 'Keep the trailing slash on singleton elements']],
caseSensitive: [[false, 'Treat attributes in case sensitive manner (useful for SVG; e.g. viewBox)']],
minifyJS: [[false, 'Minify Javascript in script elements and on* attributes (uses UglifyJS)']],
minifyCSS: [[false, 'Minify CSS in style elements and style attributes (uses clean-css)']],
minifyURLs: [[false, 'Minify URLs in various attributes (uses relateurl)', 'string'], 'site-url'],
ignoreCustomComments: [[false, 'Array of regex\'es that allow to ignore certain comments, when matched', 'string'], 'json-regex'],
ignoreCustomFragments: [[false, 'Array of regex\'es that allow to ignore certain fragments, when matched (e.g. <?php ... ?>, {{ ... }})', 'string'], 'json-regex'],
processScripts: [[false, 'Array of strings corresponding to types of script elements to process through minifier (e.g. "text/ng-template", "text/x-handlebars-template", etc.)', 'string'], 'json'],
maxLineLength: [[false, 'Max line length', 'number'], true],
customEventAttributes: [[false, 'Arrays of regex\'es that allow to support custom event attributes for minifyJS (e.g. ng-click)', 'string'], 'json-regex'],
customAttrAssign: [[false, 'Arrays of regex\'es that allow to support custom attribute assign expressions (e.g. \'<div flex?="{{mode != cover}}"></div>\')', 'string'], 'json-regex'],
customAttrSurround: [[false, 'Arrays of regex\'es that allow to support custom attribute surround expressions (e.g. <input {{#if value}}checked="checked"{{/if}}>)', 'string'], 'json-regex'],
customAttrCollapse: [[false, 'Regex that specifies custom attribute to strip newlines from (e.g. /ng\-class/)', 'string'], 'string-regex']
removeTagWhitespace: [[false, 'Remove space between attributes whenever possible.']],
useShortDoctype: [[false, 'Replaces the doctype with the short (HTML5) doctype']]
};

var cliOptions = {
Expand Down Expand Up @@ -133,27 +136,19 @@ cli.main(function(args, options) {
}
}

function parseJSONOption(value, options) {
var opts = options || {};
function parseJSONOption(value, regexArray) {
if (value !== null) {
var jsonArray;
try {
jsonArray = JSON.parse(value);
if (opts.regexArray) {
jsonArray = jsonArray.map(function (regexString) {
return stringToRegExp(regexString);
});
if (regexArray) {
jsonArray = jsonArray.map(stringToRegExp);
}
}
catch (e) {
cli.fatal('Could not parse JSON value \'' + value + '\'');
}
if (jsonArray instanceof Array) {
return jsonArray;
}
else {
return [value];
}
return Array.isArray(jsonArray) ? jsonArray : [value];
}
}

Expand All @@ -175,19 +170,17 @@ cli.main(function(args, options) {

if (minified !== null) {
// Write the output
try {
// eslint-disable-next-line eqeqeq
if (output != null) {
if (output) {
try {
fs.writeFileSync(path.resolve(output), minified);
}
else {
process.stdout.write(minified);
catch (e) {
status = 4;
cli.error('Cannot write to ' + output);
}
}
catch (e) {
status = 4;
console.log(output);
cli.error('Cannot write to output');
else {
process.stdout.write(minified);
}
}

Expand Down Expand Up @@ -235,40 +228,51 @@ cli.main(function(args, options) {
}

if (options['config-file']) {
var fileOptions;
var fileOptionsPath = path.resolve(options['config-file']);
try {
fileOptions = fs.readFileSync(fileOptionsPath, { encoding: 'utf8' });
}
catch (e) {
cli.fatal('The specified config file doesn’t exist or is unreadable:\n' + fileOptionsPath);
}
var configPath = path.resolve(options['config-file']);
try {
fileOptions = JSON.parse(fileOptions);
var configData;
try {
configData = fs.readFileSync(configPath, { encoding: 'utf8' });
}
catch (e) {
cli.fatal('The specified config file doesn’t exist or is unreadable:\n' + configPath);
}
configData = JSON.parse(configData);
mainOptionKeys.forEach(function(key) {
var value = configData[key];
if (value !== undefined) {
switch (mainOptions[key][1]) {
case 'json-regex':
minifyOptions[key] = value.map(stringToRegExp);
break;
case 'string-regex':
minifyOptions[key] = stringToRegExp(value);
break;
default:
minifyOptions[key] = value;
}
}
});
}
catch (je) {
try {
fileOptions = require(fileOptionsPath);
minifyOptions = require(configPath);
}
catch (ne) {
cli.fatal('Cannot read the specified config file. \nAs JSON: ' + je.message + '\nAs module: ' + ne.message);
cli.fatal('Cannot read the specified config file.\nAs JSON: ' + je.message + '\nAs module: ' + ne.message);
}
}

if (fileOptions && typeof fileOptions === 'object') {
minifyOptions = fileOptions;
}
}
mainOptionKeys.forEach(function(key) {
var paramKey = changeCase.paramCase(key);
var value = options[paramKey];
if (options[paramKey] !== null) {
if (value !== null) {
switch (mainOptions[key][1]) {
case 'json':
minifyOptions[key] = parseJSONOption(value);
break;
case 'json-regex':
minifyOptions[key] = parseJSONOption(value, { regexArray: true });
minifyOptions[key] = parseJSONOption(value, true);
break;
case 'string-regex':
minifyOptions[key] = stringToRegExp(value);
Expand All @@ -289,10 +293,6 @@ cli.main(function(args, options) {
minifyOptions.lint = new HTMLLint();
}

if (args.length) {
input = args;
}

if (options['input-dir'] || options['output-dir']) {
var inputDir = options['input-dir'];
var outputDir = options['output-dir'];
Expand Down Expand Up @@ -330,11 +330,10 @@ cli.main(function(args, options) {
output = options.output;
}

if (input !== null) { // Minifying one or more files specified on the CMD line

if (args.length) { // Minifying one or more files specified on the CMD line
var original = '';

input.forEach(function(afile) {
args.forEach(function(afile) {
try {
original += fs.readFileSync(afile, 'utf8');
}
Expand All @@ -345,11 +344,10 @@ cli.main(function(args, options) {
});

cli.exit(runMinify(original, output));

}
else { // Minifying input coming from STDIN
process.stdin.pipe(concat({ encoding: 'string' }, function(content) {
runMinify(content, output);
cli.exit(runMinify(content, output));
}));
}
});
8 changes: 6 additions & 2 deletions sample-cli-config-file.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
"minifyJS": true,
"minifyCSS": true,
"includeAutoGeneratedTags": false,
"ignoreCustomComments": [],
"ignoreCustomFragments": [
"<#[\\s\\S]*?#>"
],
"processConditionalComments": true,
"processScripts": []
"processScripts": [
"text/html"
]
}

0 comments on commit f5fe726

Please sign in to comment.