├── README.md
├── externs
└── jquery-mobile.js
└── jzbuild.py
/README.md:
--------------------------------------------------------------------------------
1 | I love languages where you need years of experience to write code that works,
2 | and languages where if you don't do everything exactly right, you will shoot
3 | yourself in the foot. (See my article [C++: A language for
4 | next generation web apps](http://stevehanov.ca/blog/?id=95). Naturally, I love
5 | javascript.
6 |
7 | Fortunately, Javascript has tools that will help catch bugs before you run it.
8 | One is JsLint. Following Douglas Crockford's crazy rules eliminates many
9 | cross-browser compatablility problems, and it syntax-checks the code too. The
10 | Google Closure compiler performs static analysis of the code to catch a few
11 | more problems, and as a bonus it will compress and obfuscate your code for you.
12 |
13 | JZBUILD is a build system to simplify the process.
14 |
15 | [Download JZBUILD](http://github.com/smhanov/jzbuild/raw/master/jzbuild.py)
16 |
17 | JZBUILD will:
18 |
19 | * Run all of your javascript through a built-in copy of jslint for error
20 | checking.
21 | * Convert Coffeescript files to Javascript.
22 | * Concatenate files together and feed them into the Closure compiler or YUI
23 | compressor, without sending your code over the web.
24 | * Process include directives. JZBUILD resolves dependencies so your files
25 | will be included in the proper order.
26 | * Include files from other folders. For example, you might have a folder full
27 | of re-usable javascript components that are used in several projects. JZBUILD
28 | will let you pull these files into your current project.
29 |
30 | JZBUILD is designed to be easy to use.
31 |
32 | * JZBUILD only requires python and Java to run. If you don't have another
33 | tool that it needs, it will download it automatically.
34 | * You don't need any configuration file. By default, JZBUILD will process all
35 | files in the current folder.
36 | * JZBUILD includes built-in "externs" for the closure compiler, so it will
37 | work with projects that use the jquery javascript library. It includes other tricks to make the compiler work on your code.
38 | * It works on Linux and Windows
39 |
40 | ## Tutorial
41 |
42 | Although this example is in Windows, JZBUILD works equally well on Linux.
43 |
44 | Suppose you have a folder full of javascript files -- eg, foo.js, and bar.js.
45 |
46 |
47 | Directory of H:\demo
48 |
49 | 07/29/2010 09:25 <DIR> .
50 | 07/29/2010 09:25 <DIR> ..
51 | 07/29/2010 09:26 8392 foo.js
52 | 07/29/2010 09:26 2303 bar.js
53 | 2 File(s) 10293 bytes
54 | 2 Dir(s) 377,483,264 bytes free
55 |
56 |
57 |
58 | Run JsLint on them:
59 |
60 |
61 | jzbuild.py
62 |
63 |
64 | Run JsLint and concatenate them together into MyWebApplication.js:
65 |
66 |
67 | jzbuild.py --out MyWebApplication.js
68 |
69 |
70 | Run JsLint and compress them using the YUI compressor into MyWebApplication.js. The YUI compressor will be downloaded for you.
71 |
72 |
73 | jzbuild.py --out MyWebApplication.js --compiler yui
74 |
75 |
76 | Run JsLint and compress them using the Google closure compiler into
77 | MyWebApplication.js. The compiler will be downloaded for you.
78 |
79 |
80 | jzbuild.py --out MyWebApplication.js --compiler closure
81 |
82 |
83 | ### Included files
84 |
85 | JZBUILD processes include directives by searching the current folder and any
86 | included files. Suppose foo.bar contained a line like this:
87 |
88 |
89 | //#include <bar.js>
90 |
91 |
92 | And bar.js contained a line like this:
93 |
94 |
95 | //#include <MyUsefulFunctions.js>
96 |
97 |
98 | Where MyUsefulFunctions.js is in the folder ../shared. Then you can
99 | compile your whole web application by specifying only "foo.js" on the command
100 | line:
101 |
102 |
103 | jzbuild.py --out MyWebApplication.js --compiler closure -I../shared foo.js
104 |
105 |
106 | The -I option says to search the given path for input or included files.
107 | JZBUILD takes a list of input files on the command line. It reads each of them
108 | and processes included files as well, and sticks all of them together before
109 | sending them to the output file.
110 |
111 | Note: It is incorrect to say JZBUILD "includes" files. The files are only included once, no matter how many times you specify "#include". This directive will be renamed "@require" in a future version.
112 |
113 | ## Advanced Usage
114 |
115 | When it starts, and you didn't specify "--out" on the command line, JZBUILD
116 | will look for a file named "makefile.jz" in the current folder.
117 |
118 | ## Example makefile.jz
119 |
120 | Here's an example makefile.jz. In it, we specify two projects to build. The
121 | "release" project will use the closure compiler to create MyWebApplication.js
122 | from "foo.js" and all files that it includes, searching in the folder
123 | "../shared" for any included files. It will also prepend "license.js" to the
124 | output.
125 |
126 | It also specifies a second project, called "debug". The "debug" project
127 | contains the option "base: release", which means to inherit all the settings
128 | from the "release" project.
129 |
130 | When this makefile is in the current folder, you can build a specific project
131 | by specifying its name on the command line. For example, to build the release
132 | project, use:
133 |
134 |
135 | jzbuild.py release
136 |
137 |
138 |
139 | // You can use comments in a JZBUILD makefile.jz. The file format is exactly like
140 | // JSON, except that quotes and commas are optional.
141 | // A file is an object of projects. Each project produces one output file.
142 | // When you invoke JZBUILD you must specify a project to build unless there
143 | // is only one in the file.
144 | {
145 | // Here is a project description. You only need one project but we will
146 | // define several for completeness.
147 |
148 | // You can give a project a name. You can use it to refer to the project
149 | // from other projects.
150 | release: {
151 | // The output file will be created from the input files. It is a
152 | // string with the path to the output file.
153 | output: MyWebApplication.js
154 |
155 | // 'input' is an array of input files. You should use only the filename
156 | // and not the path. When jsbuild starts it will automatically find
157 | // the files it needs from the include path. It will also expand this
158 | // list based on any //#include directives.
159 | input: [foo.js]
160 |
161 | prepend: [license.js]
162 |
163 | // The include path specifies an array of paths to search for files.
164 | // It always includes the current folder.
165 | include: [../shared]
166 |
167 | // The compiler specifies the compiler to use. The default compiler is
168 | // 'cat' which simply appends files together. The other
169 | // option is 'closure' which refers to Google's closure compiler. If
170 | // you do not have the closure compiler JZBUILD will download it for
171 | // you. However you will need to have Java installed to use it.
172 | compiler: closure
173 |
174 | // Here are the options to the closure compiler.
175 | compilerOptions: [
176 | --compilation_level ADVANCED_OPTIMIZATIONS
177 | --warning_level VERBOSE
178 | ]
179 | }
180 |
181 | // Here is a second project.
182 | debug: {
183 |
184 | // This project is special because it inherits all the properties from
185 | // its parent project. It specifies the parent by name.
186 | base: release
187 |
188 | // Here we override the options to the closure compiler to include
189 | // pretty-printing so we can easily see what it is doing.
190 | compilerOptions: [
191 | --compilation_level ADVANCED_OPTIMIZATIONS
192 | --warning_level VERBOSE
193 | --define=ENABLE_DEBUG=true
194 | --formatting PRETTY_PRINT
195 | ]
196 | }
197 | }
198 |
199 |
200 | ## Coffeescript support
201 | If any .coffee files are specified as input, JZBUILD will download Coffeescript and use it to convert them to
202 | Javascript. It stores the result in the same location as the original .coffee file, except with a .js extension.
203 |
204 | A special version of coffeescript is used that works with the Google closure compiler, and does not depend on
205 | Google libraries.
206 |
207 | If you specify the --closure compiler on the command line, the code will be type checked for you. To get the maximum
208 | benefit from the type checking, you can annotate your code with types. Refer to
209 | [Google's Closure Annotation page](http://code.google.com/closure/compiler/docs/js-for-compiler.html) for examples.
210 | Enclose closure annotations between ###* ### comments. For example:
211 |
212 |
213 |
214 | ###* @param {number} count
215 | @return {string}
216 | ###
217 | makeSpaces: (count) ->
218 | result = []
219 | result.push " " for i in count
220 | result.join ""
221 |
222 |
223 |
224 | ## Goodies to make the Closure compiler work properly
225 |
226 | ### Built in externs
227 |
228 | An externs file is built in to JZBUILD, so you can use the jquery library with the closure compiler, and it will give you useful warnings when you call the functions with the wrong parameter types.
229 |
230 | ### @export annotation
231 | A painful reality (and the whole point) of using the advanced compilation mode of the closure compiler is that it renames everything, so if you need to refer to a property of an object from HTML then it won't work unless you "export" it as described here.
232 |
233 | JZBUILD makes this easy using the @export annotation. For example:
234 |
235 |
236 | //@export MyGreatObject
237 | /** @constructor */
238 | function MyGreatObject()
239 | {
240 |
241 | }
242 |
243 | //@export MyGreatObject.prototype.dostuff
244 | MyGreatObject.prototype.dostuff = function()
245 | {
246 |
247 | }
248 |
249 |
250 | When the compiler is set to "closure", the above will cause JZBUILD to add the required exports to the code. Specifically:
251 |
252 |
253 | window["MyGreatObject"] = MyGreatObject;
254 | MyGreatObject.prototype["dostuff"] = MyGreatObject.prototype.dostuff;
255 |
256 |
257 | ## License
258 | The JZBUILD system is open source. It is released to the public domain. However, it contains portions of code that fall under other licenses. The full license information is found in the source code.
259 |
260 |
261 |
262 |
263 |
264 |
--------------------------------------------------------------------------------
/externs/jquery-mobile.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | */
4 | jQuery.prototype.mobile = {};
5 |
6 | /**
7 | @param {jQuery|string} to
8 | @param {string=} transition
9 | @param {boolean=} back
10 | @param {string=} changeHash
11 | */
12 | jQuery.prototype.mobile.changePage = function(to, transition, back, changeHash
13 | )
14 | {
15 | }
16 |
17 | /**
18 | @param {string} arg
19 | */
20 | jQuery.prototype.listview = function(arg) {}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/jzbuild.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """
3 | JZBUILD Javascript build system
4 |
5 | By Steve Hanov (steve.hanov@gmail.com)
6 |
7 | This is free software. It is released to the public domain.
8 |
9 | ------------------------------------------------------------------------------
10 | ------------------------------------------------------------------------------
11 | ------------------------------------------------------------------------------
12 | The JZBUILD software may include jslint software, which is covered under the
13 | following license.
14 |
15 | /*
16 | Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
17 |
18 | Permission is hereby granted, free of charge, to any person obtaining a copy of
19 | this software and associated documentation files (the "Software"), to deal in
20 | the Software without restriction, including without limitation the rights to
21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
22 | of the Software, and to permit persons to whom the Software is furnished to do
23 | so, subject to the following conditions:
24 |
25 | The above copyright notice and this permission notice shall be included in all
26 | copies or substantial portions of the Software.
27 |
28 | The Software shall be used for Good, not Evil.
29 |
30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36 | SOFTWARE.
37 | */
38 |
39 | ------------------------------------------------------------------------------
40 | ------------------------------------------------------------------------------
41 | ------------------------------------------------------------------------------
42 | Coffeescript is covered under the following license.
43 |
44 | Copyright (c) 2011 Jeremy Ashkenas
45 |
46 | Permission is hereby granted, free of charge, to any person
47 | obtaining a copy of this software and associated documentation
48 | files (the "Software"), to deal in the Software without
49 | restriction, including without limitation the rights to use,
50 | copy, modify, merge, publish, distribute, sublicense, and/or sell
51 | copies of the Software, and to permit persons to whom the
52 | Software is furnished to do so, subject to the following
53 | conditions:
54 |
55 | The above copyright notice and this permission notice shall be
56 | included in all copies or substantial portions of the Software.
57 |
58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
59 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
60 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
61 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
62 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
63 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
64 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
65 | OTHER DEALINGS IN THE SOFTWARE.
66 | ------------------------------------------------------------------------------
67 | ------------------------------------------------------------------------------
68 | ------------------------------------------------------------------------------
69 | JCoffeeScript is covered under the following license:
70 | /*
71 | * Copyright 2010 David Yeung
72 | *
73 | * Licensed under the Apache License, Version 2.0 (the "License");
74 | * you may not use this file except in compliance with the License.
75 | * You may obtain a copy of the License at
76 | *
77 | * http://www.apache.org/licenses/LICENSE-2.0
78 | *
79 | * Unless required by applicable law or agreed to in writing, software
80 | * distributed under the License is distributed on an "AS IS" BASIS,
81 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
82 | * See the License for the specific language governing permissions and
83 | * limitations under the License.
84 | */
85 | """
86 |
87 | import sys
88 | if sys.version_info < (3, 0):
89 | import urllib
90 | import urllib2
91 | import httplib
92 | else:
93 | # for python 3, make the adaptations here and then code as if
94 | # we are in python 2.7
95 | import http.client as httplib
96 | import urllib.request as urllib2
97 | import urllib.parse as urllib_parse
98 |
99 | class urllib: pass
100 | urllib.urlencode = urllib_parse.urlencode
101 | import atexit
102 | import base64
103 | import fnmatch
104 | import glob
105 | import gzip
106 | import io
107 | import json
108 | import os
109 | import platform
110 | import re
111 | import subprocess
112 | import time
113 | import tempfile
114 | import sys
115 | import zipfile
116 | import zlib
117 |
118 | MAKEFILE_NAME = "makefile.jz"
119 |
120 | NEVER_CHECK_THESE_FILES = """
121 | jquery.min.js
122 | jquery.js
123 | prototype.js
124 | """.split("\n")
125 |
126 | COMPILERS = {
127 | # Name of the compiler, as specified in the makefile and the --compiler
128 | # option.
129 | "closure": {
130 | # URL at which to download a zipfile of the compiler
131 | # USed to be at: "http://dl.google.com/closure-compiler/compiler-latest.zip"
132 | "download":
133 | "http://www.hanovsolutions.com/build/compiler-20180910.zip",
134 |
135 | # full path in the zip file of the compiler.
136 | "filename":
137 | "closure-compiler*.jar",
138 |
139 | # These options are always specified.
140 | "requiredOptions": ['--process_common_js_modules', '--module_resolution', 'node'],
141 |
142 | # Command line option that must precede each input filename
143 | "inputOption":
144 | "--js",
145 |
146 | # Command line option to specify the output file
147 | "outputOption":
148 | "--js_output_file",
149 |
150 | # Default options to use if none are specified
151 | "defaultOptions": [
152 | "--compilation_level", "SIMPLE_OPTIMIZATIONS",
153 | "--warning_level", "VERBOSE" ],
154 |
155 | # Options to use if none are specified and user is doing a --release
156 | # build.
157 | "releaseOptions": [
158 | "--compilation_level", "ADVANCED_OPTIMIZATIONS",
159 | "--warning_level", "VERBOSE" ],
160 |
161 | # Set this to True if the tool can't take the input on the command
162 | # line, and instead requires input to be concatenated together and
163 | # piped to its standard input.
164 | "requiresStdin": False,
165 |
166 | },
167 |
168 | "yui": {
169 | "download":
170 | "http://yui.zenfs.com/releases/yuicompressor/yuicompressor-2.4.2.zip",
171 | "filename":
172 | "yuicompressor-2.4.2/build/yuicompressor-2.4.2.jar",
173 | "requiredOptions": ["--type", "js"],
174 | "inputOption": "",
175 | "outputOption": "-o",
176 | "defaultOptions": [],
177 | "requiresStdin": True,
178 | },
179 | }
180 |
181 | EXTERNS = {
182 | "jquery-1.5.js":
183 | "http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/jquery-1.5.js",
184 | "jquery-mobile.js":
185 | "http://github.com/smhanov/jzbuild/raw/master/externs/jquery-mobile.js",
186 | }
187 |
188 | JCOFFEESCRIPT_URL = \
189 | "http://www.hanovsolutions.com/build/jcoffeescript-1.1.jar"
190 | COFFEESCRIPT_URL = \
191 | "https://raw.githubusercontent.com/smhanov/coffee-script/master/extras/coffee-script.js"
192 | def GetStorageFolder():
193 | """Returns the path to a location where we can store downloads"""
194 |
195 | # Seems to work on windows 7 too
196 | path = os.path.join(os.path.expanduser("~"), ".jzbuild")
197 | if not os.path.isdir(path):
198 | print("Creating %s" % path)
199 | os.mkdir(path)
200 | return path
201 |
202 | JAVA_PATH='java'
203 |
204 | JCOFFEESCRIPT_PATH = \
205 | os.path.join(GetStorageFolder(), os.path.basename( JCOFFEESCRIPT_URL ) )
206 |
207 | COFFEESCRIPT_PATH = \
208 | os.path.join(GetStorageFolder(), os.path.basename( COFFEESCRIPT_URL ) )
209 |
210 | COFFEESCRIPT_NODEJS_PATH = \
211 | os.path.join(GetStorageFolder(), "coffee-script-node.js" )
212 |
213 | TYPESCRIPT_URL = "http://www.hanovsolutions.com/build/tsc.js"
214 |
215 | VALID_COMPILERS = list(COMPILERS.keys());
216 | VALID_COMPILERS.append("cat")
217 |
218 | # Path to node js, if found on system. This is used to run coffeescript much
219 | # faster then java.
220 | PATH_TO_NODEJS = None
221 |
222 | MAN_PAGE = """
223 | NAME
224 |
225 | jzbuild - The easy Javascript build system
226 |
227 | SYNOPSIS
228 |
229 | jzbuild [files or projects]
230 |
231 | DESCRIPTION
232 |
233 | Runs jslint and joins together javascript input files, optionally using the
234 | Google closure compiler or YUI compressor to reduce file size and find more
235 | errors.
236 |
237 | Jzbuild looks for a file named "makefile.jz" in the current folder. If
238 | found, it reads the list of projects and options from that file. Otherwise,
239 | it uses the options given from the command line, or defaults.
240 |
241 | Jzbuild will download and use the Coffeescript compiler to seemlessly
242 | handle files ending in ".coffee".
243 |
244 | If node.js is installed on your non-windows system, jzbuild will use it to
245 | compile coffeescript much faster.
246 |
247 | [files]
248 |
249 | If no filenames are given and no makefile is present, Jzbuild will
250 | run jslint on all files matching the pattern "*.js" in the current
251 | folder.
252 |
253 | [projects]
254 |
255 | If a makefile is present, the names given on the command line refer to
256 | projects in the makefile.
257 |
258 | --out