├── 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 |
--------------------------------------------------------------------------------