html-minifier 1.1.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +43 -30
- package/cli.js +101 -98
- package/dist/htmlminifier.js +32461 -1281
- package/dist/htmlminifier.min.js +14 -2
- package/package.json +13 -7
- package/sample-cli-config-file.conf +21 -14
- package/src/.eslintrc.json +5 -0
- package/src/htmllint.js +139 -142
- package/src/htmlminifier.js +951 -681
- package/src/htmlparser.js +405 -438
- package/src/utils.js +18 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
# HTMLMinifier
|
|
2
|
+
|
|
1
3
|
[](https://www.npmjs.com/package/html-minifier)
|
|
2
4
|
[](https://travis-ci.org/kangax/html-minifier)
|
|
3
5
|
[](https://david-dm.org/kangax/html-minifier)
|
|
4
6
|
[](https://david-dm.org/kangax/html-minifier#info=devDependencies)
|
|
7
|
+
[](https://gitter.im/kangax/html-minifier)
|
|
5
8
|
|
|
6
|
-
[HTMLMinifier](http://kangax.github.io/html-minifier/) is a highly **configurable**, **well-tested**,
|
|
9
|
+
[HTMLMinifier](http://kangax.github.io/html-minifier/) is a highly **configurable**, **well-tested**, JavaScript-based HTML minifier, with lint-like capabilities.
|
|
7
10
|
|
|
8
11
|
See [corresponding blog post](http://perfectionkills.com/experimenting-with-html-minifier/) for all the gory details of [how it works](http://perfectionkills.com/experimenting-with-html-minifier/#how_it_works), [description of each option](http://perfectionkills.com/experimenting-with-html-minifier/#options), [testing results](http://perfectionkills.com/experimenting-with-html-minifier/#field_testing) and [conclusions](http://perfectionkills.com/experimenting-with-html-minifier/#cost_and_benefits).
|
|
9
12
|
|
|
@@ -13,58 +16,64 @@ Also see corresponding [Ruby wrapper](https://github.com/stereobooster/html_mini
|
|
|
13
16
|
|
|
14
17
|
## Minification comparison
|
|
15
18
|
|
|
16
|
-
How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Peavy](http://www.willpeavy.com/minifier/) (1st result in [
|
|
17
|
-
|
|
18
|
-
| Site
|
|
19
|
-
| ---------------------------------------------------------------------------
|
|
20
|
-
| [HTMLMinifier page](https://github.com/kangax/html-minifier) |
|
|
21
|
-
| [
|
|
22
|
-
| [
|
|
23
|
-
| [
|
|
24
|
-
| [
|
|
25
|
-
| [
|
|
26
|
-
| [
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
How does HTMLMinifier compare to other solutions — [HTML Minifier from Will Peavy](http://www.willpeavy.com/minifier/) (1st result in [Google search for "html minifier"](https://www.google.com/#q=html+minifier)) as well as [htmlcompressor.com](http://htmlcompressor.com) and [minimize](https://github.com/Swaagie/minimize)?
|
|
20
|
+
|
|
21
|
+
| Site | Original size *(KB)* | HTMLMinifier | minimize | Will Peavy | htmlcompressor.com |
|
|
22
|
+
| --------------------------------------------------------------------------- |:--------------------:| ------------:| --------:| ----------:| ------------------:|
|
|
23
|
+
| [HTMLMinifier page](https://github.com/kangax/html-minifier) | 49 | **37** | 42 | 44 | 43 |
|
|
24
|
+
| [NBC](http://www.nbc.com/) | 91 | **74** | 84 | 85 | 85 |
|
|
25
|
+
| [ES6 table](http://kangax.github.io/es5-compat-table/es6/) | 118 | **80** | 93 | 93 | 94 |
|
|
26
|
+
| [New York Times](http://www.nytimes.com/) | 131 | **101** | 122 | 125 | 120 |
|
|
27
|
+
| [Google](https://www.google.com/) | 133 | **128** | 132 | 135 | 131 |
|
|
28
|
+
| [MSN](http://www.msn.com/) | 157 | **130** | 138 | 145 | 138 |
|
|
29
|
+
| [Stackoverflow](http://stackoverflow.com/) | 200 | **159** | 165 | 173 | 166 |
|
|
30
|
+
| [Amazon](http://www.amazon.com/) | 246 | **203** | 234 | 230 | 219 |
|
|
31
|
+
| [Bootstrap CSS](http://getbootstrap.com/css/) | 277 | **268** | 274 | 232 | 277 |
|
|
32
|
+
| [Wikipedia](https://en.wikipedia.org/wiki/President_of_the_United_States) | 401 | **367** | 388 | 400 | n/a |
|
|
33
|
+
| [Eloquent Javascript](http://eloquentjavascript.net/1st_edition/print.html) | 870 | **827** | 840 | 864 | n/a |
|
|
34
|
+
| [ES6 draft](https://tc39.github.io/ecma262/) | 3678 | **2991** | 3079 | 3183 | n/a |
|
|
29
35
|
|
|
30
36
|
## Options Quick Reference
|
|
31
37
|
|
|
32
38
|
| Option | Description | Default |
|
|
33
39
|
|--------------------------------|-----------------|---------|
|
|
34
40
|
| `removeComments` | [Strip HTML comments](http://perfectionkills.com/experimenting-with-html-minifier/#remove_comments) | `false` |
|
|
35
|
-
| `removeCommentsFromCDATA` | [Strip HTML comments from scripts and styles](http://perfectionkills.com/experimenting-with-html-minifier/#remove_comments_from_scripts_and_styles) | `false` |
|
|
36
|
-
| `removeCDATASectionsFromCDATA` | [Remove CDATA sections from script and style elements](http://perfectionkills.com/experimenting-with-html-minifier/#remove_cdata_sections) | `false` |
|
|
37
41
|
| `collapseWhitespace` | [Collapse white space that contributes to text nodes in a document tree.](http://perfectionkills.com/experimenting-with-html-minifier/#collapse_whitespace) | `false` |
|
|
38
42
|
| `conservativeCollapse` | Always collapse to 1 space (never remove it entirely). Must be used in conjunction with `collapseWhitespace=true` | `false` |
|
|
43
|
+
| `collapseInlineTagWhitespace` | Don't leave any spaces between `display:inline;` elements when collapsing. Must be used in conjunction with `collapseWhitespace=true` | `false` |
|
|
39
44
|
| `preserveLineBreaks` | Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. Must be used in conjunction with `collapseWhitespace=true` | `false` |
|
|
40
45
|
| `collapseBooleanAttributes` | [Omit attribute values from boolean attributes](http://perfectionkills.com/experimenting-with-html-minifier/#collapse_boolean_attributes) | `false` |
|
|
46
|
+
| `removeTagWhitespace` | Remove space between attributes whenever possible. **Note that this will result in invalid HTML!** | `false` |
|
|
41
47
|
| `removeAttributeQuotes` | [Remove quotes around attributes when possible.](http://perfectionkills.com/experimenting-with-html-minifier/#remove_attribute_quotes) | `false` |
|
|
42
48
|
| `removeRedundantAttributes` | [Remove attributes when value matches default.](http://perfectionkills.com/experimenting-with-html-minifier/#remove_redundant_attributes) | `false` |
|
|
43
49
|
| `preventAttributesEscaping` | Prevents the escaping of the values of attributes. | `false` |
|
|
44
|
-
| `useShortDoctype` | [Replaces the doctype with the short (HTML5) doctype](http://perfectionkills.com/experimenting-with-html-minifier/#use_short_doctype) | `false` |
|
|
50
|
+
| `useShortDoctype` | [Replaces the `doctype` with the short (HTML5) doctype](http://perfectionkills.com/experimenting-with-html-minifier/#use_short_doctype) | `false` |
|
|
45
51
|
| `removeEmptyAttributes` | [Remove all attributes with whitespace-only values](http://perfectionkills.com/experimenting-with-html-minifier/#remove_empty_or_blank_attributes) | `false` |
|
|
46
52
|
| `removeScriptTypeAttributes` | Remove `type="text/javascript"` from `script` tags. Other `type` attribute values are left intact. | `false` |
|
|
47
53
|
| `removeStyleLinkTypeAttributes`| Remove `type="text/css"` from `style` and `link` tags. Other `type` attribute values are left intact. | `false` |
|
|
48
|
-
| `removeOptionalTags` | [Remove
|
|
54
|
+
| `removeOptionalTags` | [Remove optional tags](http://perfectionkills.com/experimenting-with-html-minifier/#remove_optional_tags) | `false` |
|
|
49
55
|
| `removeEmptyElements` | [Remove all elements with empty contents](http://perfectionkills.com/experimenting-with-html-minifier/#remove_empty_elements) | `false` |
|
|
50
56
|
| `lint` | [Toggle linting](http://perfectionkills.com/experimenting-with-html-minifier/#validate_input_through_html_lint) | `false` |
|
|
51
57
|
| `keepClosingSlash` | Keep the trailing slash on singleton elements | `false` |
|
|
52
58
|
| `caseSensitive` | Treat attributes in case sensitive manner (useful for custom HTML tags.) | `false` |
|
|
53
|
-
| `minifyJS` | Minify
|
|
54
|
-
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/
|
|
59
|
+
| `minifyJS` | Minify JavaScript in script elements and event attributes (uses [UglifyJS](https://github.com/mishoo/UglifyJS2)) | `false` (could be `true`, `false`, `Object` (options)) |
|
|
60
|
+
| `minifyCSS` | Minify CSS in style elements and style attributes (uses [clean-css](https://github.com/jakubpawlowicz/clean-css)) | `false` (could be `true`, `false`, `Object` (options)) |
|
|
55
61
|
| `minifyURLs` | Minify URLs in various attributes (uses [relateurl](https://github.com/stevenvachon/relateurl)) | `false` (could be `Object` (options)) |
|
|
62
|
+
| `includeAutoGeneratedTags` | Insert tags generated by HTML parser | `true` |
|
|
56
63
|
| `ignoreCustomComments` | Array of regex'es that allow to ignore certain comments, when matched | `[ ]` |
|
|
57
|
-
| `ignoreCustomFragments`
|
|
64
|
+
| `ignoreCustomFragments` | Array of regex'es that allow to ignore certain fragments, when matched (e.g. `<?php ... ?>`, `{{ ... }}`, etc.) | `[ /<%[\s\S]*?%>/, /<\?[\s\S]*?\?>/ ]` |
|
|
58
65
|
| `processScripts` | Array of strings corresponding to types of script elements to process through minifier (e.g. `text/ng-template`, `text/x-handlebars-template`, etc.) | `[ ]` |
|
|
66
|
+
| `processConditionalComments` | Process contents of conditional comments through minifier | `false` |
|
|
59
67
|
| `maxLineLength` | Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points. |
|
|
68
|
+
| `customEventAttributes` | Arrays of regex'es that allow to support custom event attributes for `minifyJS` (e.g. `ng-click`) | `[ /^on[a-z]{3,}$/ ]` |
|
|
60
69
|
| `customAttrAssign` | Arrays of regex'es that allow to support custom attribute assign expressions (e.g. `'<div flex?="{{mode != cover}}"></div>'`) | `[ ]` |
|
|
61
70
|
| `customAttrSurround` | Arrays of regex'es that allow to support custom attribute surround expressions (e.g. `<input {{#if value}}checked="checked"{{/if}}>`) | `[ ]` |
|
|
62
71
|
| `customAttrCollapse` | Regex that specifies custom attribute to strip newlines from (e.g. `/ng\-class/`) | |
|
|
63
|
-
| `quoteCharacter` | Type of quote to use for attribute values (' or ") |
|
|
72
|
+
| `quoteCharacter` | Type of quote to use for attribute values (' or ") | |
|
|
64
73
|
|
|
65
74
|
## Special cases
|
|
66
75
|
|
|
67
|
-
### Ignoring chunks of markup
|
|
76
|
+
### Ignoring chunks of markup
|
|
68
77
|
|
|
69
78
|
If you have chunks of markup you would like preserved, you can wrap them `<!-- htmlmin:ignore -->`.
|
|
70
79
|
|
|
@@ -74,17 +83,17 @@ SVG tags are automatically recognized, and when they are minified, both case-sen
|
|
|
74
83
|
|
|
75
84
|
### Working with invalid markup
|
|
76
85
|
|
|
77
|
-
HTMLMinifier **can't work with invalid or partial chunks of markup**. This is because it parses markup into a tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be
|
|
86
|
+
HTMLMinifier **can't work with invalid or partial chunks of markup**. This is because it parses markup into a tree structure, then modifies it (removing anything that was specified for removal, ignoring anything that was specified to be ignored, etc.), then it creates a markup out of that tree and returns it.
|
|
78
87
|
|
|
79
88
|
Input markup (e.g. `<p id="">foo`)
|
|
80
89
|
|
|
81
90
|
↓
|
|
82
91
|
|
|
83
|
-
Internal representation of markup in a form of tree (e.g. `{tag: "p", attr: "id", children: ["foo"] }`)
|
|
92
|
+
Internal representation of markup in a form of tree (e.g. `{ tag: "p", attr: "id", children: ["foo"] }`)
|
|
84
93
|
|
|
85
94
|
↓
|
|
86
95
|
|
|
87
|
-
Transformation of internal representation (e.g. removal of
|
|
96
|
+
Transformation of internal representation (e.g. removal of `id` attribute)
|
|
88
97
|
|
|
89
98
|
↓
|
|
90
99
|
|
|
@@ -95,17 +104,20 @@ HTMLMinifier can't know that original markup was only half of the tree; it does
|
|
|
95
104
|
## Installation Instructions
|
|
96
105
|
|
|
97
106
|
From NPM for use as a command line app:
|
|
98
|
-
|
|
107
|
+
|
|
108
|
+
```shell
|
|
99
109
|
npm install html-minifier -g
|
|
100
110
|
```
|
|
101
111
|
|
|
102
112
|
From NPM for programmatic use:
|
|
103
|
-
|
|
113
|
+
|
|
114
|
+
```shell
|
|
104
115
|
npm install html-minifier
|
|
105
116
|
```
|
|
106
117
|
|
|
107
118
|
From Git:
|
|
108
|
-
|
|
119
|
+
|
|
120
|
+
```shell
|
|
109
121
|
git clone git://github.com/kangax/html-minifier.git
|
|
110
122
|
cd html-minifier
|
|
111
123
|
npm link .
|
|
@@ -128,6 +140,7 @@ result; // '<p title=blah id=moo>foo</p>'
|
|
|
128
140
|
## Running benchmarks
|
|
129
141
|
|
|
130
142
|
Benchmarks for minified HTML:
|
|
131
|
-
|
|
143
|
+
|
|
144
|
+
```shell
|
|
132
145
|
node benchmark.js
|
|
133
146
|
```
|
package/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The MIT License (MIT)
|
|
6
6
|
*
|
|
7
|
-
* Copyright (c) 2014-
|
|
7
|
+
* Copyright (c) 2014-2016 Zoltan Frombach
|
|
8
8
|
*
|
|
9
9
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
10
10
|
* this software and associated documentation files (the "Software"), to deal in
|
|
@@ -25,8 +25,6 @@
|
|
|
25
25
|
*
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
/* globals JSON:true */
|
|
29
|
-
|
|
30
28
|
'use strict';
|
|
31
29
|
|
|
32
30
|
var cli = require('cli');
|
|
@@ -36,19 +34,18 @@ var path = require('path');
|
|
|
36
34
|
var fs = require('fs');
|
|
37
35
|
var appName = require('./package.json').name;
|
|
38
36
|
var appVersion = require('./package.json').version;
|
|
39
|
-
var minify = require('
|
|
40
|
-
var HTMLLint = require('./
|
|
37
|
+
var minify = require('.').minify;
|
|
38
|
+
var HTMLLint = require('./src/htmllint').HTMLLint;
|
|
41
39
|
var minifyOptions = {};
|
|
42
|
-
var
|
|
43
|
-
var output = null;
|
|
40
|
+
var output;
|
|
44
41
|
|
|
45
42
|
cli.width = 100;
|
|
46
43
|
cli.option_width = 40;
|
|
47
44
|
cli.setApp(appName, appVersion);
|
|
48
45
|
|
|
49
46
|
var usage = appName + ' [OPTIONS] [FILE(s)]\n\n';
|
|
50
|
-
usage += ' If no input
|
|
51
|
-
usage += ' If more than one input file specified those will be concatenated and minified together.\n\n';
|
|
47
|
+
usage += ' If no input files are specified then STDIN will be used for input.\n';
|
|
48
|
+
usage += ' If more than one input file is specified then those will be concatenated and minified together.\n\n';
|
|
52
49
|
usage += ' When you specify a config file with the --config-file option (see sample-cli-config-file.conf for format)\n';
|
|
53
50
|
usage += ' you can still override some of its contents by providing individual command line options, too.\n\n';
|
|
54
51
|
usage += ' When you want to provide an array of strings for --ignore-custom-comments or --process-scripts options\n';
|
|
@@ -57,35 +54,40 @@ usage += ' on the command line you must escape those such as --ignore-custom-
|
|
|
57
54
|
cli.setUsage(usage);
|
|
58
55
|
|
|
59
56
|
var mainOptions = {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
caseSensitive: [[false, 'Treat attributes in case sensitive manner (useful for SVG; e.g. viewBox)']],
|
|
58
|
+
collapseBooleanAttributes: [[false, 'Omit attribute values from boolean attributes']],
|
|
59
|
+
collapseInlineTagWhitespace: [[false, 'Collapse white space around inline tag']],
|
|
63
60
|
collapseWhitespace: [[false, 'Collapse white space that contributes to text nodes in a document tree.']],
|
|
64
61
|
conservativeCollapse: [[false, 'Always collapse to 1 space (never remove it entirely)']],
|
|
62
|
+
customAttrAssign: [[false, 'Arrays of regex\'es that allow to support custom attribute assign expressions (e.g. \'<div flex?="{{mode != cover}}"></div>\')', 'string'], 'json-regex'],
|
|
63
|
+
customAttrCollapse: [[false, 'Regex that specifies custom attribute to strip newlines from (e.g. /ng\-class/)', 'string'], 'string-regex'],
|
|
64
|
+
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'],
|
|
65
|
+
customEventAttributes: [[false, 'Arrays of regex\'es that allow to support custom event attributes for minifyJS (e.g. ng-click)', 'string'], 'json-regex'],
|
|
66
|
+
html5: [[false, 'Parse input according to HTML5 specifications']],
|
|
67
|
+
ignoreCustomComments: [[false, 'Array of regex\'es that allow to ignore certain comments, when matched', 'string'], 'json-regex'],
|
|
68
|
+
ignoreCustomFragments: [[false, 'Array of regex\'es that allow to ignore certain fragments, when matched (e.g. <?php ... ?>, {{ ... }})', 'string'], 'json-regex'],
|
|
69
|
+
includeAutoGeneratedTags: [[false, 'Insert tags generated by HTML parser'], true],
|
|
70
|
+
keepClosingSlash: [[false, 'Keep the trailing slash on singleton elements']],
|
|
71
|
+
lint: [[false, 'Toggle linting']],
|
|
72
|
+
maxLineLength: [[false, 'Max line length', 'number'], true],
|
|
73
|
+
minifyCSS: [[false, 'Minify CSS in style elements and style attributes (uses clean-css)']],
|
|
74
|
+
minifyJS: [[false, 'Minify Javascript in script elements and on* attributes (uses UglifyJS)']],
|
|
75
|
+
minifyURLs: [[false, 'Minify URLs in various attributes (uses relateurl)', 'string'], 'site-url'],
|
|
65
76
|
preserveLineBreaks: [[false, 'Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break.']],
|
|
66
|
-
collapseInlineTagWhitespace: [[false, 'Collapse white space around inline tag']],
|
|
67
|
-
collapseBooleanAttributes: [[false, 'Omit attribute values from boolean attributes']],
|
|
68
|
-
removeAttributeQuotes: [[false, 'Remove quotes around attributes when possible.']],
|
|
69
|
-
removeRedundantAttributes: [[false, 'Remove attributes when value matches default.']],
|
|
70
77
|
preventAttributesEscaping: [[false, 'Prevents the escaping of the values of attributes.']],
|
|
71
|
-
|
|
78
|
+
processConditionalComments: [[false, 'Process contents of conditional comments through minifier']],
|
|
79
|
+
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'],
|
|
80
|
+
quoteCharacter: [[false, 'Type of quote to use for attribute values (\' or ")', 'string'], true],
|
|
81
|
+
removeAttributeQuotes: [[false, 'Remove quotes around attributes when possible.']],
|
|
82
|
+
removeComments: [[false, 'Strip HTML comments']],
|
|
72
83
|
removeEmptyAttributes: [[false, 'Remove all attributes with whitespace-only values']],
|
|
84
|
+
removeEmptyElements: [[false, 'Remove all elements with empty contents']],
|
|
85
|
+
removeOptionalTags: [[false, 'Remove unrequired tags']],
|
|
86
|
+
removeRedundantAttributes: [[false, 'Remove attributes when value matches default.']],
|
|
73
87
|
removeScriptTypeAttributes: [[false, 'Remove type="text/javascript" from script tags. Other type attribute values are left intact.']],
|
|
74
88
|
removeStyleLinkTypeAttributes: [[false, 'Remove type="text/css" from style and link tags. Other type attribute values are left intact.']],
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
lint: [[false, 'Toggle linting']],
|
|
78
|
-
keepClosingSlash: [[false, 'Keep the trailing slash on singleton elements']],
|
|
79
|
-
caseSensitive: [[false, 'Treat attributes in case sensitive manner (useful for SVG; e.g. viewBox)']],
|
|
80
|
-
minifyJS: [[false, 'Minify Javascript in script elements and on* attributes (uses UglifyJS)']],
|
|
81
|
-
minifyCSS: [[false, 'Minify CSS in style elements and style attributes (uses clean-css)']],
|
|
82
|
-
minifyURLs: [[false, 'Minify URLs in various attributes (uses relateurl)']],
|
|
83
|
-
ignoreCustomComments: [[false, 'Array of regex\'es that allow to ignore certain comments, when matched', 'string'], 'json-regex'],
|
|
84
|
-
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-regex'],
|
|
85
|
-
maxLineLength: [[false, 'Max line length', 'number'], true],
|
|
86
|
-
customAttrAssign: [[false, 'Arrays of regex\'es that allow to support custom attribute assign expressions (e.g. \'<div flex?="{{mode != cover}}"></div>\')', 'string'], 'json-regex'],
|
|
87
|
-
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'],
|
|
88
|
-
customAttrCollapse: [[false, 'Regex that specifies custom attribute to strip newlines from (e.g. /ng\-class/)', 'string'], 'string-regex']
|
|
89
|
+
removeTagWhitespace: [[false, 'Remove space between attributes whenever possible.']],
|
|
90
|
+
useShortDoctype: [[false, 'Replaces the doctype with the short (HTML5) doctype']]
|
|
89
91
|
};
|
|
90
92
|
|
|
91
93
|
var cliOptions = {
|
|
@@ -132,27 +134,19 @@ cli.main(function(args, options) {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
function parseJSONOption(value,
|
|
136
|
-
var opts = options || {};
|
|
137
|
+
function parseJSONOption(value, regexArray) {
|
|
137
138
|
if (value !== null) {
|
|
138
139
|
var jsonArray;
|
|
139
140
|
try {
|
|
140
141
|
jsonArray = JSON.parse(value);
|
|
141
|
-
if (
|
|
142
|
-
jsonArray = jsonArray.map(
|
|
143
|
-
return stringToRegExp(regexString);
|
|
144
|
-
});
|
|
142
|
+
if (regexArray) {
|
|
143
|
+
jsonArray = jsonArray.map(stringToRegExp);
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
catch (e) {
|
|
148
147
|
cli.fatal('Could not parse JSON value \'' + value + '\'');
|
|
149
148
|
}
|
|
150
|
-
|
|
151
|
-
return jsonArray;
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
return [value];
|
|
155
|
-
}
|
|
149
|
+
return Array.isArray(jsonArray) ? jsonArray : [value];
|
|
156
150
|
}
|
|
157
151
|
}
|
|
158
152
|
|
|
@@ -165,7 +159,7 @@ cli.main(function(args, options) {
|
|
|
165
159
|
catch (e) {
|
|
166
160
|
status = 3;
|
|
167
161
|
cli.error('Minification error');
|
|
168
|
-
process.stderr.write(e);
|
|
162
|
+
process.stderr.write((e.stack || e).toString());
|
|
169
163
|
}
|
|
170
164
|
|
|
171
165
|
if (minifyOptions.lint) {
|
|
@@ -174,22 +168,21 @@ cli.main(function(args, options) {
|
|
|
174
168
|
|
|
175
169
|
if (minified !== null) {
|
|
176
170
|
// Write the output
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
if (output) {
|
|
172
|
+
try {
|
|
179
173
|
fs.writeFileSync(path.resolve(output), minified);
|
|
180
174
|
}
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
catch (e) {
|
|
176
|
+
status = 4;
|
|
177
|
+
cli.error('Cannot write to ' + output);
|
|
183
178
|
}
|
|
184
179
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
console.log(output);
|
|
188
|
-
cli.error('Cannot write to output');
|
|
180
|
+
else {
|
|
181
|
+
process.stdout.write(minified);
|
|
189
182
|
}
|
|
190
183
|
}
|
|
191
184
|
|
|
192
|
-
|
|
185
|
+
return status;
|
|
193
186
|
}
|
|
194
187
|
|
|
195
188
|
function createDirectory(path) {
|
|
@@ -197,31 +190,34 @@ cli.main(function(args, options) {
|
|
|
197
190
|
fs.mkdirSync(path);
|
|
198
191
|
}
|
|
199
192
|
catch (e) {
|
|
200
|
-
if (e.code
|
|
201
|
-
|
|
193
|
+
if (e.code !== 'EEXIST') {
|
|
194
|
+
cli.fatal('Can not create directory ' + path);
|
|
195
|
+
return 3;
|
|
202
196
|
}
|
|
203
|
-
cli.fatal('Can not create directory ' + path);
|
|
204
|
-
cli.exit(3);
|
|
205
197
|
}
|
|
198
|
+
return 0;
|
|
206
199
|
}
|
|
207
200
|
|
|
208
201
|
function processDirectory(inputDir, outputDir) {
|
|
209
|
-
createDirectory(outputDir);
|
|
210
202
|
var fileList = fs.readdirSync(inputDir);
|
|
203
|
+
var status = createDirectory(outputDir);
|
|
211
204
|
|
|
212
|
-
fileList.
|
|
205
|
+
for (var i = 0; status === 0 && i < fileList.length; i++) {
|
|
206
|
+
var fileName = fileList[i];
|
|
213
207
|
var inputFilePath = inputDir + '/' + fileName;
|
|
214
208
|
var outputFilePath = outputDir + '/' + fileName;
|
|
215
209
|
|
|
216
210
|
var stat = fs.statSync(inputFilePath);
|
|
217
211
|
if (stat.isDirectory()) {
|
|
218
|
-
processDirectory(inputFilePath, outputFilePath);
|
|
219
|
-
return;
|
|
212
|
+
status = processDirectory(inputFilePath, outputFilePath);
|
|
220
213
|
}
|
|
214
|
+
else {
|
|
215
|
+
var originalContent = fs.readFileSync(inputFilePath, 'utf8');
|
|
216
|
+
status = runMinify(originalContent, outputFilePath);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
runMinify(originalContent, outputFilePath);
|
|
224
|
-
});
|
|
220
|
+
return status;
|
|
225
221
|
}
|
|
226
222
|
|
|
227
223
|
if (options.version) {
|
|
@@ -230,44 +226,58 @@ cli.main(function(args, options) {
|
|
|
230
226
|
}
|
|
231
227
|
|
|
232
228
|
if (options['config-file']) {
|
|
233
|
-
var
|
|
234
|
-
var fileOptionsPath = path.resolve(options['config-file']);
|
|
235
|
-
try {
|
|
236
|
-
fs.accessSync(fileOptionsPath, fs.R_OK);
|
|
237
|
-
}
|
|
238
|
-
catch (e) {
|
|
239
|
-
cli.fatal('The specified config file doesn’t exist or is unreadable:\n' + fileOptionsPath);
|
|
240
|
-
}
|
|
229
|
+
var configPath = path.resolve(options['config-file']);
|
|
241
230
|
try {
|
|
242
|
-
|
|
231
|
+
var configData;
|
|
232
|
+
try {
|
|
233
|
+
configData = fs.readFileSync(configPath, { encoding: 'utf8' });
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
cli.fatal('The specified config file doesn’t exist or is unreadable:\n' + configPath);
|
|
237
|
+
}
|
|
238
|
+
configData = JSON.parse(configData);
|
|
239
|
+
mainOptionKeys.forEach(function(key) {
|
|
240
|
+
var value = configData[key];
|
|
241
|
+
if (value !== undefined) {
|
|
242
|
+
switch (mainOptions[key][1]) {
|
|
243
|
+
case 'json-regex':
|
|
244
|
+
minifyOptions[key] = value.map(stringToRegExp);
|
|
245
|
+
break;
|
|
246
|
+
case 'string-regex':
|
|
247
|
+
minifyOptions[key] = stringToRegExp(value);
|
|
248
|
+
break;
|
|
249
|
+
default:
|
|
250
|
+
minifyOptions[key] = value;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
243
254
|
}
|
|
244
255
|
catch (je) {
|
|
245
256
|
try {
|
|
246
|
-
|
|
257
|
+
minifyOptions = require(configPath);
|
|
247
258
|
}
|
|
248
259
|
catch (ne) {
|
|
249
|
-
cli.fatal('Cannot read the specified config file
|
|
260
|
+
cli.fatal('Cannot read the specified config file.\nAs JSON: ' + je.message + '\nAs module: ' + ne.message);
|
|
250
261
|
}
|
|
251
262
|
}
|
|
252
|
-
|
|
253
|
-
if (fileOptions && typeof fileOptions === 'object') {
|
|
254
|
-
minifyOptions = fileOptions;
|
|
255
|
-
}
|
|
256
263
|
}
|
|
257
264
|
mainOptionKeys.forEach(function(key) {
|
|
258
265
|
var paramKey = changeCase.paramCase(key);
|
|
259
266
|
var value = options[paramKey];
|
|
260
|
-
if (
|
|
267
|
+
if (value !== null) {
|
|
261
268
|
switch (mainOptions[key][1]) {
|
|
262
269
|
case 'json':
|
|
263
270
|
minifyOptions[key] = parseJSONOption(value);
|
|
264
271
|
break;
|
|
265
272
|
case 'json-regex':
|
|
266
|
-
minifyOptions[key] = parseJSONOption(value,
|
|
273
|
+
minifyOptions[key] = parseJSONOption(value, true);
|
|
267
274
|
break;
|
|
268
275
|
case 'string-regex':
|
|
269
276
|
minifyOptions[key] = stringToRegExp(value);
|
|
270
277
|
break;
|
|
278
|
+
case 'site-url':
|
|
279
|
+
minifyOptions[key] = { site: value };
|
|
280
|
+
break;
|
|
271
281
|
case true:
|
|
272
282
|
minifyOptions[key] = value;
|
|
273
283
|
break;
|
|
@@ -281,16 +291,12 @@ cli.main(function(args, options) {
|
|
|
281
291
|
minifyOptions.lint = new HTMLLint();
|
|
282
292
|
}
|
|
283
293
|
|
|
284
|
-
if (args.length) {
|
|
285
|
-
input = args;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
294
|
if (options['input-dir'] || options['output-dir']) {
|
|
289
295
|
var inputDir = options['input-dir'];
|
|
290
296
|
var outputDir = options['output-dir'];
|
|
291
297
|
|
|
292
298
|
if (!inputDir) {
|
|
293
|
-
cli.error('The option output-dir
|
|
299
|
+
cli.error('The option output-dir needs to be used with the option input-dir. If you are working with a single file, use -o.');
|
|
294
300
|
cli.exit(2);
|
|
295
301
|
}
|
|
296
302
|
|
|
@@ -298,7 +304,7 @@ cli.main(function(args, options) {
|
|
|
298
304
|
fs.statSync(inputDir);
|
|
299
305
|
}
|
|
300
306
|
catch (e) {
|
|
301
|
-
cli.error('The input directory does not
|
|
307
|
+
cli.error('The input directory does not exist');
|
|
302
308
|
cli.exit(2);
|
|
303
309
|
}
|
|
304
310
|
|
|
@@ -309,27 +315,23 @@ cli.main(function(args, options) {
|
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
try {
|
|
312
|
-
processDirectory(inputDir, outputDir);
|
|
318
|
+
cli.exit(processDirectory(inputDir, outputDir));
|
|
313
319
|
}
|
|
314
320
|
catch (e) {
|
|
315
|
-
cli.error('
|
|
321
|
+
cli.error('Error while processing input files');
|
|
316
322
|
cli.error(e);
|
|
317
323
|
cli.exit(3);
|
|
318
324
|
}
|
|
319
|
-
|
|
320
|
-
cli.exit(0);
|
|
321
|
-
return;
|
|
322
325
|
}
|
|
323
326
|
|
|
324
327
|
if (options.output) {
|
|
325
328
|
output = options.output;
|
|
326
329
|
}
|
|
327
330
|
|
|
328
|
-
if (
|
|
329
|
-
|
|
331
|
+
if (args.length) { // Minifying one or more files specified on the CMD line
|
|
330
332
|
var original = '';
|
|
331
333
|
|
|
332
|
-
|
|
334
|
+
args.forEach(function(afile) {
|
|
333
335
|
try {
|
|
334
336
|
original += fs.readFileSync(afile, 'utf8');
|
|
335
337
|
}
|
|
@@ -339,10 +341,11 @@ cli.main(function(args, options) {
|
|
|
339
341
|
}
|
|
340
342
|
});
|
|
341
343
|
|
|
342
|
-
runMinify(original, output);
|
|
343
|
-
|
|
344
|
+
cli.exit(runMinify(original, output));
|
|
344
345
|
}
|
|
345
346
|
else { // Minifying input coming from STDIN
|
|
346
|
-
process.stdin.pipe(concat({ encoding: 'string' },
|
|
347
|
+
process.stdin.pipe(concat({ encoding: 'string' }, function(content) {
|
|
348
|
+
cli.exit(runMinify(content, output));
|
|
349
|
+
}));
|
|
347
350
|
}
|
|
348
351
|
});
|