Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Commit

Permalink
Switch to Closure Compiler for generating the minified version.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kit Cambridge committed Apr 25, 2012
1 parent 183974c commit 8194206
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 94 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@
[submodule "vendor/marked"] [submodule "vendor/marked"]
path = vendor/marked path = vendor/marked
url = git://github.com/chjj/marked.git url = git://github.com/chjj/marked.git
[submodule "vendor/uglifyjs"]
path = vendor/uglifyjs
url = git://github.com/mishoo/UglifyJS.git
295 changes: 205 additions & 90 deletions build.js
Original file line number Original file line Diff line number Diff line change
@@ -1,113 +1,228 @@
#!/usr/bin/env node #!/usr/bin/env node


/* JSON 3 Builder | http://bestiejs.github.com/json3 */ /* JSON 3 Builder | http://bestiejs.github.com/json3 */
var path = require("path"), fs = require("fs"), marked = require(path.join(__dirname, "vendor", "marked")), ugly = require(path.join(__dirname, "vendor", "uglifyjs", "uglify-js")); var path = require("path"), fs = require("fs"), spawn = require("child_process").spawn, marked = require(path.join(__dirname, "vendor", "marked")),

// The path to the Closure Compiler `.jar` file.
closurePath = path.join(__dirname, "vendor", "closure-compiler.jar"),

// The Closure Compiler options: enable advanced optimizations and suppress all
// warnings apart from syntax and optimization errors.
closureOptions = ["--compilation_level=advanced_optimizations", "--warning_level=quiet"];


// Enable GitHub-Flavored Markdown. // Enable GitHub-Flavored Markdown.
marked.setOptions({ "gfm": true }); marked.setOptions({ "gfm": true });


// Generate the GitHub project page. // Generate the GitHub project page.
fs.readFile(path.join(__dirname, "README.md"), "utf8", function readSource(exception, source) { fs.readFile(path.join(__dirname, "README.md"), "utf8", function readInfo(exception, source) {
if (exception) { if (exception) {
console.log(exception); console.log(exception);
} else { } else {
// Read the project page template. // Read the project page template.
fs.readFile(path.join(__dirname, "page", "page.html"), "utf8", function readPage(exception, page) { fs.readFile(path.join(__dirname, "page", "page.html"), "utf8", readTemplate);
var headers, lines, lastSection, lastLevel, navigation; }
if (exception) {
console.log(exception); // Interpolates the page template and writes the result to disk.
} else { function readTemplate(exception, page) {
// Generate the page navigation. Ported from `mdtoc.rb` by Sam var headers, lines, lastSection, lastLevel, navigation;
// Stephenson. if (exception) {
headers = []; console.log(exception);
lines = source.split(/\r?\n/); } else {
// First pass: Scan the Markdown source looking for titles of the format: // Generate the page navigation. Ported from `mdtoc.rb` by Sam
// `### Title ###`. Record the line number, header level (number of // Stephenson.
// octothorpes), and text of each matching title. headers = [];
lines.forEach(function (line, index) { lines = source.split(/\r?\n/);
var match = /^(\#{1,6})\s+(.+?)\s+\1$/.exec(line); // First pass: Scan the Markdown source looking for titles of the format:
if (match) { // `### Title ###`. Record the line number, header level (number of
headers.push([index, match[1].length, match[2]]); // octothorpes), and text of each matching title.
} lines.forEach(function (line, index) {
}); var match = /^(\#{1,6})\s+(.+?)\s+\1$/.exec(line);
// Second pass: Iterate over all matched titles and compute their if (match) {
// corresponding section numbers. Then replace the titles with annotated headers.push([index, match[1].length, match[2]]);
// anchors. }
headers.forEach(function(value) { });
var index = value[0], level = value[1], text = value[2], section, length; // Second pass: Iterate over all matched titles and compute their
if (lastSection) { // corresponding section numbers. Then replace the titles with annotated
// Duplicate the last section metadata array. // anchors.
section = lastSection.slice(0); headers.forEach(function (value) {
if (lastLevel < level) { var index = value[0], level = value[1], text = value[2], section, length;
section.push(1); if (lastSection) {
} else { // Clone the last section metadata array.
length = lastLevel - level; section = lastSection.slice(0);
while (length--) { if (lastLevel < level) {
section.pop(); section.push(1);
}
section[section.length - 1] += 1;
}
} else { } else {
section = [1]; length = lastLevel - level;
} while (length--) {
lines[index] = Array(level + 1).join("#") + "<a name=\"section_" + section.join(".") + "\"></a>" + text; section.pop();
value.push(section); }
lastSection = section; section[section.length - 1] += 1;
lastLevel = level;
});
// Third pass: Iterate over matched titles once more to produce the table of
// contents.
navigation = headers.map(function (value) {
var index = value[0], level = value[1], text = value[2], section = value[3], name = section.join(".");
return "<li><a href=\"#section_" + name + "\">" + text + "</a></li>";
});
navigation.push("");
// Write the page source to disk.
fs.writeFile(path.join(__dirname, "index.html"), page.replace(/<%=\s*(.+?)\s*%>/g, function (match, data) {
switch (data) {
case "navigation":
// Insert the table of contents directly into the template.
return navigation.join("\n");
case "source":
// Convert the read me to HTML and insert it into the page body.
return marked(lines.join("\n"));
} }
return ""; } else {
}), function (exception) { section = [1];
console.log(exception || "GitHub project page generated successfully."); }
}); lines[index] = Array(level + 1).join("#") + "<a name=\"section_" + section.join(".") + "\"></a>" + text;
} value.push(section);
}); lastSection = section;
lastLevel = level;
});
// Third pass: Iterate over matched titles once more to produce the table of
// contents.
navigation = headers.map(function (value) {
var index = value[0], level = value[1], text = value[2], section = value[3], name = section.join(".");
return "<li><a href=\"#section_" + name + "\">" + text + "</a></li>";
});
navigation.push("");
// Write the page source to disk.
fs.writeFile(path.join(__dirname, "index.html"), page.replace(/<%=\s*(.+?)\s*%>/g, function interpolate(match, data) {
switch (data) {
case "navigation":
// Insert the table of contents directly into the template.
return navigation.join("\n");
case "source":
// Convert the read me to HTML and insert it into the page body.
return marked(lines.join("\n"));
}
return "";
}), function writePage(exception) {
console.log(exception || "GitHub project page generated successfully.");
});
}
} }
}); });


// Compress JSON 3 using UglifyJS. // Compress JSON 3 using the Closure Compiler.
fs.readFile(path.join(__dirname, "lib", "json3.js"), "utf8", function (exception, source) { fs.readFile(path.join(__dirname, "lib", "json3.js"), "utf8", function readSource(exception, source) {
var results; var results;
if (exception) { if (exception) {
console.log(exception); console.log(exception);
} else { } else {
results = ""; // Shell out to the Closure Compiler executable. Requires Java 6 or higher.
// Preserve the copyright header. invoke("java", ["-jar", closurePath].concat(closureOptions), source, compressSource);
ugly.parser.tokenizer(source)().comments_before.forEach(function (comment) { }
// Remove the leading `!` character from YUI-style comments.
results += comment.type == "comment1" ? "//" + comment.value + "\n" : ("/*" + comment.value.slice(comment.value.charAt(0) == "!" ? 1 : 0) + "*/"); // Post-processes the compressed source and writes the result to disk.
}); function compressSource(exception, compressed) {
results += "\n;" + ugly.uglify.gen_code( if (exception) {
// Enable unsafe transformations. console.log(exception);
ugly.uglify.ast_squeeze_more( } else {
ugly.uglify.ast_squeeze( // Extract the JSON 3 header and wrap the compressed source in an
// Munge variable and function names, excluding the special `define` // immediately-invoked function expression (enabling advanced
// function exposed by asynchronous module loaders. // optimizations causes the Compiler to add global variables).
ugly.uglify.ast_mangle(ugly.parser.parse(source), { compressed = extractComments(source)[0] + "\n;(function(){" + compressed + "}());";
"except": ["define"] // Write the compressed version to disk.
} fs.writeFile(path.join(__dirname, "lib", "json3.min.js"), compressed, writeSource);
))), { }
"ascii_only": true
}) + ";"; // Checks the `gzip`-ped size of the compressed version by shelling out to the
// Split lines at 500 characters for consistency with Closure Compiler. // Unix `gzip` executable.
fs.writeFile(path.join(__dirname, "lib", "json3.min.js"), ugly.uglify.split_lines(results, 500), function (exception) { function writeSource(exception) {
console.log(exception || "Compressed version generated successfully."); console.log(exception || "Compressed version generated successfully.");
}); // Automatically check the `gzip`-ped size of the compressed version.
// This requires shelling out to the Unix `gzip` executable.
invoke("gzip", ["-9f", "-c"], compressed, "binary", function checkSize(exception, results) {
if (!exception) {
console.log("Compressed version size: %d KB.", results.length);
}
});
}
}
});

// Internal: Invokes a process with the given `name`, `parameters,` and standard
// `input`. Yields the result to a `callback` function. The optional `encoding`
// argument specifies the output stream encoding.
function invoke(name, parameters, input, encoding, callback) {
// The standard output stream, error stream, and process instance.
var error = "", output = "", process = spawn(name, parameters);
if (typeof encoding == "string") {
// Explicitly set the encoding of the output stream if one is specified.
process.stdout.setEncoding(encoding);
} else {
callback = encoding;
encoding = null;
}
process.stdout.on("data", function onData(data) {
// Append the data to the output stream.
output += data;
});
process.stderr.on("data", function onError(data) {
// Append the error message to the error stream.
error += data;
});
process.on("exit", function onExit(status) {
var exception;
// `status` specifies the process exit code.
if (status) {
exception = new Error(error);
exception.status = status;
}
callback(exception, output);
});
// Proxy the standard input to the process.
process.stdin.end(input);
}

// Internal: Extracts line and block comments from a JavaScript `source`
// string. Returns an array containing the comments.
function extractComments(source) {
var index = 0, length = source.length, results = [], symbol, position, original;
while (index < length) {
symbol = source[index];
switch (symbol) {
// Parse line and block comments.
case "/":
original = symbol;
symbol = source[++index];
switch (symbol) {
// Extract line comments.
case "/":
position = source.indexOf("\n", index);
if (position < 0) {
// Check for CR line endings.
position = source.indexOf("\r", index);
}
results.push(original + source.slice(index, index = position < 0 ? length : position));
break;
// Extract block comments.
case "*":
position = source.indexOf("*/", index);
if (position < 0) {
throw SyntaxError("Unterminated block comment.");
}
// Advance past the end of the comment.
results.push(original + source.slice(index, index = position += 2));
break;
default:
index++;
}
break;
// Parse strings separately to ensure that any JavaScript comments within
// them are preserved.
case '"':
case "'":
for (position = index, original = symbol; index < length;) {
symbol = source[++index];
if (symbol == "\\") {
// Skip past escaped characters.
index++;
} else if ("\n\r\u2028\u2029".indexOf(symbol) > -1) {
// According to the ES 5.1 spec, strings may not contain unescaped
// line terminators.
throw SyntaxError("Illegal line continuation.");
} else if (symbol == original) {
break;
}
}
if (source[index] == original) {
index++;
break;
}
throw SyntaxError("Unterminated string.");
default:
// Advance to the next character.
index++;
}
} }
}); return results;
}
Binary file added vendor/closure-compiler.jar
Binary file not shown.
1 change: 0 additions & 1 deletion vendor/uglifyjs
Submodule uglifyjs deleted from 37aed3

0 comments on commit 8194206

Please sign in to comment.