modularize bundler and minify scripts

This commit is contained in:
Leo Horie 2016-06-23 01:10:30 -04:00
parent 639967c767
commit 89492a4956
8 changed files with 173 additions and 124 deletions

87
bundler/bundle.js Normal file
View file

@ -0,0 +1,87 @@
"use strict"
var fs = require("fs")
var path = require("path")
module.exports = function(input, output) {
function run(e, file) {
var modules = {}
var usedVariables = {}
function resolve(dir, data) {
var replacements = []
data = data.replace(/((?:var|let|const|)[\t ]*)([\w_$\.]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) {
usedVariables[variable] = usedVariables[variable] ? usedVariables[variable]++ : 1
var filename = new Function("return " + dep).call()
//resolve npm dependencies
if (filename[0] !== ".") {
var meta = JSON.parse(fs.readFileSync("./node_modules/" + filename + "/package.json"))
var dependencyEntry = "./node_modules/" + filename + "/" + (meta.main || filename + ".js")
try {fs.statSync(dependencyEntry).isFile()} catch (e) {dependencyEntry = "./node_modules/" + filename + "/index.js"}
return resolve(path.dirname(dependencyEntry), exportCode(dependencyEntry, def + variable + eq))
}
//resolve local dependencies
var normalized = path.resolve(dir, filename)
var pathname = path.dirname(normalized)
if (modules[normalized] === undefined) {
modules[normalized] = variable
var exported = exportCode(dir + "/" + filename + ".js", def + variable + eq)
return resolve(pathname, exported)
}
else {
if (modules[normalized] !== variable) {
replacements.push({variable: variable, replacement: modules[normalized]})
}
return ""
}
})
if (replacements.length > 0) {
for (var i = 0; i < replacements.length; i++) {
data = data.replace(new RegExp("\\b" + replacements[i].variable + "\\b", "g"), replacements[i].replacement)
}
}
return data
.replace(/(?:var|let|const)[\t ]([\w_$\.]+)(\s*=\s*)\1([\r\n;]+)/g, "$3") // remove assignments to itself
.replace(/([\r\n]){2,}/g, "$1") // remove multiple consecutive line breaks
.replace(/\}[\r\n]+\(/g, "}(") // remove space from iife
}
function exportCode(file, assignment) {
return fixCollisions(fs.readFileSync(file, "utf8"))
.replace(/("|')use strict\1;?\s*/gm, "") // remove extraneous "use strict"
.replace(/module\.exports\s*=\s*/gm, assignment)
}
function fixCollisions(code) {
for (var variable in usedVariables) {
var collision = new RegExp("\\b" + variable + "\\b(?![\"'`])", "g")
var exported = new RegExp("module\\.exports\\s*=\\s*" + variable)
if (collision.test(code) && !exported.test(code)) {
var fixed = variable + usedVariables[variable]++
code = code.replace(collision, fixed)
}
}
return code
}
function setVersion(code) {
var metadata = JSON.parse(fs.readFileSync("./package.json"))
return code.replace("bleeding-edge", metadata.version)
}
function bundle(input, output) {
console.log("bundling...")
var code = setVersion(resolve(path.dirname(input), fs.readFileSync(input, "utf8")))
if (new Function(code)) fs.writeFileSync(output, code, "utf8")
console.log("done")
}
if (file !== output && file !== output.replace(/\.js$/, ".min.js")) bundle(input, output)
}
run()
//fs.watch(process.cwd(), {recursive: true}, run)
}

View file

@ -1,78 +0,0 @@
"use strict"
var fs = require("fs")
var path = require("path")
var modules = {}
var usedVariables = {}
function resolve(dir, data) {
var replacements = []
data = data.replace(/((?:var|let|const|)[\t ]*)([\w_$\.]+)(\s*=\s*)require\(([^\)]+)\)/g, function(match, def, variable, eq, dep) {
usedVariables[variable] = usedVariables[variable] ? usedVariables[variable]++ : 1
var filename = new Function("return " + dep).call()
//resolve npm dependencies
if (filename[0] !== ".") {
var meta = JSON.parse(fs.readFileSync("./node_modules/" + filename + "/package.json"))
var dependencyEntry = "./node_modules/" + filename + "/" + (meta.main || filename + ".js")
try {fs.statSync(dependencyEntry).isFile()} catch (e) {dependencyEntry = "./node_modules/" + filename + "/index.js"}
return resolve(path.dirname(dependencyEntry), exportCode(dependencyEntry, def + variable + eq))
}
//resolve local dependencies
var normalized = path.resolve(dir, filename)
var pathname = path.dirname(normalized)
if (modules[normalized] === undefined) {
modules[normalized] = variable
var exported = exportCode(dir + "/" + filename + ".js", def + variable + eq)
return resolve(pathname, exported)
}
else {
if (modules[normalized] !== variable) {
replacements.push({variable: variable, replacement: modules[normalized]})
}
return ""
}
})
if (replacements.length > 0) {
for (var i = 0; i < replacements.length; i++) {
data = data.replace(new RegExp("\\b" + replacements[i].variable + "\\b", "g"), replacements[i].replacement)
}
}
return data
.replace(/(?:var|let|const)[\t ]([\w_$\.]+)(\s*=\s*)\1([\r\n;]+)/g, "$3") // remove assignments to itself
.replace(/([\r\n]){2,}/g, "$1") // remove multiple consecutive line breaks
.replace(/\}[\r\n]+\(/g, "}(") // remove space from iife
}
function exportCode(file, assigment) {
return fixCollisions(fs.readFileSync(file, "utf8"))
.replace(/("|')use strict\1;?\s*/gm, "") // remove extraneous "use strict"
.replace(/module\.exports\s*=\s*/gm, assigment)
}
function fixCollisions(code) {
for (var variable in usedVariables) {
var collision = new RegExp("\\b" + variable + "\\b(?![\"'`])", "g")
var exported = new RegExp("module\\.exports\\s*=\\s*" + variable)
if (collision.test(code) && !exported.test(code)) {
var fixed = variable + usedVariables[variable]++
code = code.replace(collision, fixed)
}
}
return code
}
function setVersion(code) {
var metadata = JSON.parse(fs.readFileSync("./package.json"))
return code.replace("bleeding-edge", metadata.version)
}
function bundle(input, output) {
var code = setVersion(resolve(".", fs.readFileSync(input, "utf8")))
if (new Function(code)) fs.writeFileSync(output, code, "utf8")
}
bundle("index.js", "mithril.js")

21
bundler/cli.js Normal file
View file

@ -0,0 +1,21 @@
"use strict"
var bundle = require("./bundle")
var minify = require("./minify")
var aliases = {o: "output"}
var params = {}
var args = process.argv.slice(2), command = null
console.log(args)
for (var i = 0; i < args.length; i++) {
if (args[i][0] === '"') args[i] = args[i].slice(1, -1)
if (args[i][0] === "-") command = args[i].replace(/\-+/g, "")
else if (command != null) {
params[aliases[command] || command] = args[i]
command = null
}
else params.input = args[i]
}
bundle(params.input, params.output)
minify(params.output, params.output.replace(/\.js$/, ".min.js"))

View file

@ -2,48 +2,64 @@ var http = require("http")
var querystring = require("querystring")
var fs = require("fs")
var code = fs.readFileSync("./mithril.js", "utf8")
var data = {
output_format: "json",
output_info: ["compiled_code", "warnings", "errors", "statistics"],
compilation_level: "SIMPLE_OPTIMIZATIONS",//ADVANDED_OPTIMIZATIONS
warning_level: "default",
output_file_name: "default.js",
js_code: code,
}
var body = querystring.stringify(data)
var req = http.request({
method: "POST",
protocol: "http:",
hostname: "closure-compiler.appspot.com",
path: "/compile",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Content-Length": body.length
module.exports = function(input, output) {
function format(n) {
return n.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
}
}, function(res) {
res.on("data", function(chunk) {
response += chunk.toString()
})
res.on("end", function() {
var results = JSON.parse(response)
var stats = results.statistics
console.log("Original size: " + format(stats.originalGzipSize) + " bytes gzipped (" + format(stats.originalSize) + " bytes uncompressed)")
console.log("Compiled size: " + format(stats.compressedGzipSize) + " bytes gzipped (" + format(stats.compressedSize) + " bytes uncompressed)")
fs.writeFileSync("mithril.min.js", results.compiledCode, "utf8")
})
})
var response = ""
function minify(input, output) {
var code = fs.readFileSync(input, "utf8")
req.write(body)
req.end()
console.log("compiling...")
var data = {
output_format: "json",
output_info: ["compiled_code", "warnings", "errors", "statistics"],
compilation_level: "SIMPLE_OPTIMIZATIONS",//ADVANDED_OPTIMIZATIONS
warning_level: "default",
output_file_name: "default.js",
js_code: code,
}
function format(n) {
return n.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
}
var body = querystring.stringify(data)
var response = ""
var req = http.request({
method: "POST",
protocol: "http:",
hostname: "closure-compiler.appspot.com",
path: "/compile",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"Content-Length": body.length
}
}, function(res) {
res.on("data", function(chunk) {
response += chunk.toString()
})
res.on("end", function() {
var results = JSON.parse(response)
if (results.errors) {
for (var i = 0; i < results.errors.length; i++) console.log(results.errors[i])
}
else {
fs.writeFileSync(output, results.compiledCode, "utf8")
var stats = results.statistics
console.log("done")
console.log("Original size: " + format(stats.originalGzipSize) + " bytes gzipped (" + format(stats.originalSize) + " bytes uncompressed)")
console.log("Compiled size: " + format(stats.compressedGzipSize) + " bytes gzipped (" + format(stats.compressedSize) + " bytes uncompressed)")
}
})
})
req.write(body)
req.end()
console.log("minifying...")
}
function run() {
minify(input, output)
}
run()
//fs.watchFile(input, run)
}

View file

@ -24,7 +24,9 @@ m.withAttr = require("./util/withAttr")
m.render = renderService.render
m.redraw = redrawService.publish
if (typeof module === "object") module.exports = m
if (typeof module === "object") {
module.exports = m
}
else window.m = m
})()

View file

@ -1048,6 +1048,8 @@ m.withAttr = function(attrName, callback, context) {
}
m.render = renderService.render
m.redraw = redrawService.publish
if (typeof module === "object") module.exports = m
if (typeof module === "object") {
module.exports = m
}
else window.m = m
})()

View file

@ -33,7 +33,7 @@ function traverseDirectory(pathname, callback) {
traverseDirectory(".", function(pathname, stat, children) {
if (pathname.indexOf("node_modules") > -1) return
if (pathname.match(/(?:^|\/)tests\/.*\.js$/)) {
require("../../" + pathname)
require(path.normalize(process.cwd()) + "/" + pathname)
}
})
.then(o.run)

View file

@ -6,8 +6,7 @@
"license": "MIT",
"main": "index.js",
"scripts": {
"build": "node bundler/bundler",
"minify": "node bundler/minify",
"build": "node bundler/cli index.js -o mithril.js",
"lintdocs": "node bundler/lintdocs",
"lint": "eslint .",
"test": "node ospec/bin/ospec",