├── README └── jsmin.c /README: -------------------------------------------------------------------------------- 1 | JSMIN, The JavaScript Minifier 2 | 3 | 4 | Douglas Crockford 5 | douglas@crockford.com 6 | 7 | 2003-12-04 8 | 9 | JSMin is a filter which removes comments and (un)necessary whitespace from 10 | JavaScript files. It typically reduces filesize by half, resulting in faster 11 | downloads. It also encourages a more expressive programming style because it 12 | eliminates the download cost of clean, literate self-documentation. 13 | 14 | What JSMin Does 15 | 16 | JSMin is a filter that omits or modifies some characters. This does not change 17 | the behavior of the program that it is minifying. The result may be harder to 18 | debug. It will definitely be harder to read. 19 | 20 | JSMin first replaces carriage returns ('\r') with linefeeds ('\n'). It replaces 21 | all other control characters (including tab) with spaces. It replaces comments 22 | in the // form with linefeeds. It replaces comments in the /* */ form with 23 | spaces. All runs of spaces are replaced with a single space. All runs of 24 | linefeeds are replaced with a single linefeed. 25 | 26 | It omits spaces except when a space is preceded and followed by a non-ASCII 27 | character or by an ASCII letter or digit, or by one of these characters: 28 | 29 | \ $ _ 30 | 31 | It is more conservative in omitting linefeeds, because linefeeds are sometimes 32 | treated as semicolons. A linefeed is not omitted if it precedes a non-ASCII 33 | character or an ASCII letter or digit or one of these characters: 34 | 35 | \ $ _ { [ ( + - 36 | 37 | and if it follows a non-ASCII character or an ASCII letter or digit or one of 38 | these characters: 39 | 40 | \ $ _ } ] ) + - " ' 41 | 42 | No other characters are omitted or modified. 43 | 44 | JSMin knows to not modify quoted strings and regular expression literals. 45 | 46 | JSMin does not obfuscate, but it does uglify. 47 | 48 | Before: 49 | 50 | // is.js 51 | 52 | // (c) 2001 Douglas Crockford 53 | // 2001 June 3 54 | 55 | 56 | // is 57 | 58 | // The -is- object is used to identify the browser. Every browser edition 59 | // identifies itself, but there is no standard way of doing it, and some of 60 | // the identification is deceptive. This is because the authors of web 61 | // browsers are liars. For example, Microsoft's IE browsers claim to be 62 | // Mozilla 4. Netscape 6 claims to be version 5. 63 | 64 | // Warning: Do not use this awful, awful code. 65 | 66 | var is = { 67 | ie: navigator.appName == 'Microsoft Internet Explorer', 68 | java: navigator.javaEnabled(), 69 | ns: navigator.appName == 'Netscape', 70 | ua: navigator.userAgent.toLowerCase(), 71 | version: parseFloat(navigator.appVersion.substr(21)) || 72 | parseFloat(navigator.appVersion), 73 | win: navigator.platform == 'Win32' 74 | } 75 | 76 | is.mac = is.ua.indexOf('mac') >= 0; 77 | 78 | if (is.ua.indexOf('opera') >= 0) { 79 | is.ie = is.ns = false; 80 | is.opera = true; 81 | } 82 | 83 | if (is.ua.indexOf('gecko') >= 0) { 84 | is.ie = is.ns = false; 85 | is.gecko = true; 86 | } 87 | 88 | After: 89 | 90 | var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'} 91 | is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;} 92 | if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;} 93 | 94 | Character Set 95 | 96 | JSMin requires, but does not verify, that the character set encoding of the 97 | input program is either ASCII or UTF-8. It might not work correctly with other 98 | encodings. 99 | 100 | Caution 101 | 102 | Be sure to retain your original source file. JSMin is a one-way trip: Once done, 103 | it cannot be undone. 104 | 105 | Do not put raw control characters inside a quoted string. That is an extremely 106 | bad practice. Use \xhh notation instead. JSMin will replace control 107 | characters with spaces or linefeeds. 108 | 109 | Use parens with confusing sequences of + or -. For example, minification changes 110 | 111 | a + ++b 112 | 113 | into 114 | 115 | a+++b 116 | 117 | which is interpreted as 118 | 119 | a++ + b 120 | 121 | which is wrong. You can avoid this by using parens: 122 | 123 | a + (++b) 124 | 125 | JSLint checks for all of these problems. It is suggested that JSLint be used 126 | before using JSMin. 127 | 128 | Command Line Options 129 | 130 | Optional parameters will be listed at the beginning of the output as comments. 131 | This is a convenient way of replacing copyright messages and other documentation. 132 | 133 | Example: 134 | 135 | jsmin jslint.js "(c)2002 Douglas Crockford" 136 | 137 | Errors 138 | 139 | JSMin can produce three error messages to stderr: 140 | 141 | Unterminated comment. 142 | Unterminated string constant. 143 | Unterminated regular expression. 144 | 145 | It ignores all other errors that may be present in your source program. 146 | 147 | Get Minified 148 | 149 | You can get a zip file containing an MS-DOS.exe file, or you can get the 150 | C source code and build it yourself. 151 | 152 | Copyright 2001 Douglas Crockford. All Rights Reserved Wrrrldwide. 153 | -------------------------------------------------------------------------------- /jsmin.c: -------------------------------------------------------------------------------- 1 | /* jsmin.c 2 | 2012-01-09 3 | 4 | Copyright (c) 2002 Douglas Crockford (www.crockford.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | The Software shall be used for Good, not Evil. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | static int theA; 31 | static int theB; 32 | static int theLookahead = EOF; 33 | 34 | 35 | /* isAlphanum -- return true if the character is a letter, digit, underscore, 36 | dollar sign, or non-ASCII character. 37 | */ 38 | 39 | static int 40 | isAlphanum(int c) 41 | { 42 | return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || 43 | (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || 44 | c > 126); 45 | } 46 | 47 | 48 | /* get -- return the next character from stdin. Watch out for lookahead. If 49 | the character is a control character, translate it to a space or 50 | linefeed. 51 | */ 52 | 53 | static int 54 | get() 55 | { 56 | int c = theLookahead; 57 | theLookahead = EOF; 58 | if (c == EOF) { 59 | c = getc(stdin); 60 | } 61 | if (c >= ' ' || c == '\n' || c == EOF) { 62 | return c; 63 | } 64 | if (c == '\r') { 65 | return '\n'; 66 | } 67 | return ' '; 68 | } 69 | 70 | 71 | /* peek -- get the next character without getting it. 72 | */ 73 | 74 | static int 75 | peek() 76 | { 77 | theLookahead = get(); 78 | return theLookahead; 79 | } 80 | 81 | 82 | /* next -- get the next character, excluding comments. peek() is used to see 83 | if a '/' is followed by a '/' or '*'. 84 | */ 85 | 86 | static int 87 | next() 88 | { 89 | int c = get(); 90 | if (c == '/') { 91 | switch (peek()) { 92 | case '/': 93 | for (;;) { 94 | c = get(); 95 | if (c <= '\n') { 96 | return c; 97 | } 98 | } 99 | case '*': 100 | get(); 101 | for (;;) { 102 | switch (get()) { 103 | case '*': 104 | if (peek() == '/') { 105 | get(); 106 | return ' '; 107 | } 108 | break; 109 | case EOF: 110 | fprintf(stderr, "Error: JSMIN Unterminated comment.\n"); 111 | exit(1); 112 | } 113 | } 114 | default: 115 | return c; 116 | } 117 | } 118 | return c; 119 | } 120 | 121 | 122 | /* action -- do something! What you do is determined by the argument: 123 | 1 Output A. Copy B to A. Get the next B. 124 | 2 Copy B to A. Get the next B. (Delete A). 125 | 3 Get the next B. (Delete B). 126 | action treats a string as a single character. Wow! 127 | action recognizes a regular expression if it is preceded by ( or , or =. 128 | */ 129 | 130 | static void 131 | action(int d) 132 | { 133 | switch (d) { 134 | case 1: 135 | putc(theA, stdout); 136 | case 2: 137 | theA = theB; 138 | if (theA == '\'' || theA == '"' || theA == '`') { 139 | for (;;) { 140 | putc(theA, stdout); 141 | theA = get(); 142 | if (theA == theB) { 143 | break; 144 | } 145 | if (theA == '\\') { 146 | putc(theA, stdout); 147 | theA = get(); 148 | } 149 | if (theA == EOF) { 150 | fprintf(stderr, "Error: JSMIN unterminated string literal."); 151 | exit(1); 152 | } 153 | } 154 | } 155 | case 3: 156 | theB = next(); 157 | if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' || 158 | theA == ':' || theA == '[' || theA == '!' || 159 | theA == '&' || theA == '|' || theA == '?' || 160 | theA == '{' || theA == '}' || theA == ';' || 161 | theA == '\n')) { 162 | putc(theA, stdout); 163 | putc(theB, stdout); 164 | for (;;) { 165 | theA = get(); 166 | if (theA == '[') { 167 | for (;;) { 168 | putc(theA, stdout); 169 | theA = get(); 170 | if (theA == ']') { 171 | break; 172 | } 173 | if (theA == '\\') { 174 | putc(theA, stdout); 175 | theA = get(); 176 | } 177 | if (theA == EOF) { 178 | fprintf(stderr, 179 | "Error: JSMIN unterminated set in Regular Expression literal.\n"); 180 | exit(1); 181 | } 182 | } 183 | } else if (theA == '/') { 184 | break; 185 | } else if (theA =='\\') { 186 | putc(theA, stdout); 187 | theA = get(); 188 | } 189 | if (theA == EOF) { 190 | fprintf(stderr, 191 | "Error: JSMIN unterminated Regular Expression literal.\n"); 192 | exit(1); 193 | } 194 | putc(theA, stdout); 195 | } 196 | theB = next(); 197 | } 198 | } 199 | } 200 | 201 | 202 | /* jsmin -- Copy the input to the output, deleting the characters which are 203 | insignificant to JavaScript. Comments will be removed. Tabs will be 204 | replaced with spaces. Carriage returns will be replaced with linefeeds. 205 | Most spaces and linefeeds will be removed. 206 | */ 207 | 208 | static void 209 | jsmin() 210 | { 211 | if (peek() == 0xEF) { 212 | get(); 213 | get(); 214 | get(); 215 | } 216 | theA = '\n'; 217 | action(3); 218 | while (theA != EOF) { 219 | switch (theA) { 220 | case ' ': 221 | if (isAlphanum(theB)) { 222 | action(1); 223 | } else { 224 | action(2); 225 | } 226 | break; 227 | case '\n': 228 | switch (theB) { 229 | case '{': 230 | case '[': 231 | case '(': 232 | case '+': 233 | case '-': 234 | action(1); 235 | break; 236 | case ' ': 237 | action(3); 238 | break; 239 | default: 240 | if (isAlphanum(theB)) { 241 | action(1); 242 | } else { 243 | action(2); 244 | } 245 | } 246 | break; 247 | default: 248 | switch (theB) { 249 | case ' ': 250 | if (isAlphanum(theA)) { 251 | action(1); 252 | break; 253 | } 254 | action(3); 255 | break; 256 | case '\n': 257 | switch (theA) { 258 | case '}': 259 | case ']': 260 | case ')': 261 | case '+': 262 | case '-': 263 | case '"': 264 | case '\'': 265 | case '`': 266 | action(1); 267 | break; 268 | default: 269 | if (isAlphanum(theA)) { 270 | action(1); 271 | } else { 272 | action(3); 273 | } 274 | } 275 | break; 276 | default: 277 | action(1); 278 | break; 279 | } 280 | } 281 | } 282 | } 283 | 284 | 285 | /* main -- Output any command line arguments as comments 286 | and then minify the input. 287 | */ 288 | extern int 289 | main(int argc, char* argv[]) 290 | { 291 | int i; 292 | for (i = 1; i < argc; i += 1) { 293 | fprintf(stdout, "// %s\n", argv[i]); 294 | } 295 | jsmin(); 296 | return 0; 297 | } 298 | --------------------------------------------------------------------------------