diff options
Diffstat (limited to 'js/gulp')
-rw-r--r-- | js/gulp/gulp-bundle/index.js | 429 | ||||
-rw-r--r-- | js/gulp/gulp-slice2js/index.js | 202 |
2 files changed, 631 insertions, 0 deletions
diff --git a/js/gulp/gulp-bundle/index.js b/js/gulp/gulp-bundle/index.js new file mode 100644 index 00000000000..c4b93a00ff8 --- /dev/null +++ b/js/gulp/gulp-bundle/index.js @@ -0,0 +1,429 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +var gutil = require("gulp-util"); +var PluginError = gutil.PluginError; +var PLUGIN_NAME = "gulp-slice2js-bundle"; +var through = require("through2"); +var fs = require("fs"); +var path = require("path"); + +function rmfile(path) +{ + try + { + fs.unlinkSync(path); + } + catch(e) + { + } +} + +function mkdir(path) +{ + try + { + fs.mkdirSync(path); + } + catch(e) + { + if(e.code != "EEXIST") + { + throw e; + } + } +} + +function isnewer(input, output) +{ + return fs.statSync(input).mtime.getTime() > fs.statSync(output).mtime.getTime(); +} + +function isfile(path) +{ + try + { + return fs.statSync(path).isFile(); + } + catch(e) + { + if(e.code == "ENOENT") + { + return false; + } + throw e; + } + return false; +} + +var esprima = require('esprima'); + +var Depends = function() +{ + this.depends = []; +}; + +Depends.prototype.get = function(file) +{ + for(var i = 0; i < this.depends.length; ++i) + { + var obj = this.depends[i]; + if(obj.file.path === file) + { + return obj.depends; + } + } + return []; +}; + +Depends.prototype.expand = function(o) +{ + if(o === undefined) + { + for(var i = 0; i < this.depends.length; ++i) + { + this.expand(this.depends[i]); + } + } + else + { + var newDepends = o.depends.slice(); + for(var j = 0; j < o.depends.length; ++j) + { + var depends = this.get(o.depends[j]); + for(var k = 0; k < depends.length; ++k) + { + if(newDepends.indexOf(depends[k]) === -1) + { + newDepends.push(depends[k]); + } + } + } + + if(o.depends.length != newDepends.length) + { + + o.depends = newDepends; + this.expand(o); + } + } + return this; +}; + +Depends.comparator = function(a, b) +{ + // B depends on A + var i; + var result = 0; + + for(i = 0; i < b.depends.length; ++i) + { + if(b.depends[i] === a.file.path) + { + result = -1; + } + } + // A depends on B + for(i = 0; i < a.depends.length; ++i) + { + if(a.depends[i] === b.file.path) + { + if(result == -1) + { + process.stderr.write("warning: circulary dependency between: " + a.file.path + " and " + b.file.path + "\n"); + return result; + } + result = 1; + } + } + + return result; +}; + +Depends.prototype.sort = function() +{ + var objects = this.depends.slice(); + for(var i = 0; i < objects.length; ++i) + { + for(var j = 0; j < objects.length; ++j) + { + if(j === i) { continue; } + var v = Depends.comparator(objects[i], objects[j]); + if(v < 0) + { + var tmp = objects[j]; + objects[j] = objects[i]; + objects[i] = tmp; + } + } + } + return objects; +}; + +var Parser = {}; + +Parser.add = function(depend, file, srcDir) +{ + if(file.indexOf("../Ice/") === 0 || + file.indexOf("../IceGrid/") === 0 || + file.indexOf("../IceStorm/") === 0 || + file.indexOf("../Glacier2/") === 0) + { + file = isfile(path.join(srcDir, path.dirname(file), "browser", path.basename(file))) ? + path.resolve(path.join(srcDir, path.dirname(file), "browser", path.basename(file))) : + path.resolve(path.join(srcDir, file)); + + if(depend.depends.indexOf(file) === -1) + { + depend.depends.push(file); + } + } +}; + +Parser.transverse = function(object, depend, srcDir) +{ + function appendfile(arg) + { + Parser.add(depend, arg.value + ".js", srcDir); + } + + for(var key in object) + { + var value = object[key]; + if(value !== null && typeof value == "object") + { + Parser.transverse(value, depend, srcDir); + + if(value.type === "CallExpression") + { + if(value.callee.name === "require") + { + Parser.add(depend, value.arguments[0].value + ".js", srcDir); + } + else if(value.callee.type == "MemberExpression" && + value.callee.property.name == "require" && + (value.callee.object.name == "__M" || + (value.callee.object.property && value.callee.object.property.name == "__M"))) + { + value.arguments[1].elements.forEach(appendfile); + } + } + } + } +}; + +var StringBuffer = function() +{ + this.buffer = new Buffer(0); +}; + +StringBuffer.prototype.write = function(data) +{ + this.buffer = Buffer.concat([this.buffer, new Buffer(data, "utf8")]); +}; + +function bundle(args) +{ + var files = []; + var outputFile = null; + + return through.obj( + function(file, enc, cb) + { + if(file.isNull()) + { + return; + } + + if(file.isStream()) + { + return this.emit('error', new PluginError(PLUGIN_NAME, 'Streaming not supported')); + } + + if(!outputFile) + { + outputFile = file; + } + + files.push(file); + cb(); + }, + function(cb) + { + if(!isfile(args.target) || + files.some(function(f){ return isnewer(f.path, args.target); })) + { + var d = new Depends(); + files.forEach( + function(file) + { + var depend = {file: file, depends:[]}; + d.depends.push(depend); + Parser.transverse(esprima.parse(file.contents.toString()), depend, args.srcDir); + }); + + d.depends = d.expand().sort(); + + // + // Wrap the library in a closure to hold the private __Slice module. + // + var preamble = + "(function()\n" + + "{\n"; + + var epilogue = + "}());\n\n"; + + // + // Wrap contents of each file in a closure to keep local variables local. + // + var modulePreamble = + "\n" + + " (function()\n" + + " {\n"; + + var moduleEpilogue = + " }());\n"; + + + var sb = new StringBuffer(); + + sb.write(preamble); + + args.modules.forEach( + function(m){ + sb.write(" window." + m + " = window." + m + " || {};\n"); + if(m == "Ice") + { + sb.write(" Ice.Slice = Ice.Slice || {};\n"); + } + }); + sb.write(" var Slice = Ice.Slice;"); + + for(var i = 0; i < d.depends.length; ++i) + { + sb.write(modulePreamble); + var data = d.depends[i].file.contents.toString(); + var lines = data.toString().split("\n"); + + var skip = false; + var skipUntil; + var skipAuto = false; + var line; + + for(var j in lines) + { + line = lines[j].trim(); + + if(line == "/* slice2js browser-bundle-skip */") + { + skipAuto = true; + continue; + } + if(line == "/* slice2js browser-bundle-skip-end */") + { + skipAuto = false; + continue; + } + else if(skipAuto) + { + continue; + } + + // + // Get rid of require statements, the bundle include all required files, + // so require statements are not required. + // + if(line.match(/var .* require\(".*"\).*;/)) + { + continue; + } + if(line.match(/__M\.require\(/)) + { + if(line.lastIndexOf(";") === -1) + { + // skip until next semicolon + skip = true; + skipUntil = ";"; + } + continue; + } + + + // + // Get rid of __M.module statements, in browser top level modules are + // global. + // + if(line.match(/var .* = __M.module\(/)) + { + if(line.lastIndexOf(";") === -1) + { + // skip until next semicolon + skip = true; + skipUntil = ";"; + } + continue; + } + + if(skip) + { + if(line.lastIndexOf(skipUntil) !== -1) + { + skip = false; + } + continue; + } + + var out = lines[j]; + if(line.indexOf("module.exports.") === 0) + { + continue; + } + else if(line.indexOf("exports.") === 0) + { + continue; + } + else if(line.indexOf("exports =") === 0) + { + continue; + } + + if(line.indexOf("__M.type") !== -1) + { + out = out.replace(/__M\.type/g, "eval"); + } + + sb.write(" " + out + "\n"); + } + sb.write(moduleEpilogue); + } + sb.write("\n"); + // + // Now exports the modules to the global Window object. + // + args.modules.forEach( + function(m){ + sb.write(" window." + m + " = " + m + ";\n"); + }); + + sb.write(epilogue); + + this.push(new gutil.File( + { + cwd: "", + base:"", + path:path.basename(args.target), + contents:sb.buffer + })); + } + cb(); + }); +} + +module.exports = bundle; diff --git a/js/gulp/gulp-slice2js/index.js b/js/gulp/gulp-slice2js/index.js new file mode 100644 index 00000000000..22b8833a901 --- /dev/null +++ b/js/gulp/gulp-slice2js/index.js @@ -0,0 +1,202 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +var gutil = require("gulp-util"); +var PluginError = gutil.PluginError; +var PLUGIN_NAME = "gulp-slice2js"; + +var through = require("through2"); +var spawn = require("child_process").spawn; +var fs = require("fs"); +var path = require("path"); + +function rmfile(path) +{ + try + { + fs.unlinkSync(path); + } + catch(e) + { + } +} + +function mkdir(path) +{ + try + { + fs.mkdirSync(path); + } + catch(e) + { + if(e.code != "EEXIST") + { + throw e; + } + } +} + +function isfile(path) +{ + try + { + return fs.statSync(path).isFile(); + } + catch(e) + { + if(e.code == "ENOENT") + { + return false; + } + throw e; + } + return false; +} + +var defaultCompileArgs = ["--stdout"]; +var defaultDependArgs = ["--depend-json"]; + +function isnewer(input, output) +{ + return fs.statSync(input).mtime.getTime() > fs.statSync(output).mtime.getTime(); +} + +function isBuildRequired(inputFile, outputFile, dependFile) +{ + if(![inputFile, outputFile, dependFile].every(isfile) || isnewer(inputFile, outputFile)) + { + return true; + } + + function isnewerthan(f) + { + return isnewer(f, outputFile); + } + + var depend = JSON.parse(fs.readFileSync(dependFile, {encoding: "utf8"})); + for(var key in depend) + { + if(path.normalize(key) == path.normalize(inputFile)) + { + return depend[key].some(isnewerthan); + } + } + return false; +} + +function compile(slice2js, file, args, cb) +{ + var p = slice2js(args.concat(defaultCompileArgs).concat([file.path])); + + var buffer = new Buffer(0); + p.stdout.on("data", function(data) + { + buffer = Buffer.concat([buffer, data]); + }); + + p.stderr.on("data", function(data) + { + gutil.log("'slice2js error'", data.toString()); + }); + + p.on('close', function(code) + { + if(code === 0) + { + file.path = gutil.replaceExtension(file.path, ".js"); + file.contents = buffer; + cb(null, file); + } + else + { + cb(new PluginError(PLUGIN_NAME, 'slice2js exit with error code: ' + code)); + } + }); +} + +module.exports = function(options) +{ + var opts = options || {}; + var slice2js; + var args = opts.args || []; + + if(!opts.exe) + { + try + { + slice2js = require("zeroc-slice2js"); + } + catch(e) + { + } + } + + if(!slice2js) + { + slice2js = function(args) + { + return spawn(opts.exe || "slice2js", args); + }; + } + + return through.obj(function(file, enc, cb) + { + if(file.isNull()) + { + cb(); + } + else if(file.isStream()) + { + cb(new PluginError(PLUGIN_NAME, 'Streaming not supported')); + } + else if(opts.dest) + { + var outputFile = path.join(file.cwd, opts.dest, path.basename(file.path, ".ice") + ".js"); + var dependFile = path.join(path.dirname(outputFile), ".depend", path.basename(outputFile, ".js") + ".d"); + + if(isBuildRequired(file.path, outputFile, dependFile)) + { + [outputFile, dependFile].forEach(rmfile); + var build = slice2js(args.concat(defaultDependArgs).concat([file.path])); + mkdir(path.dirname(dependFile)); + var buffer = new Buffer(0); + build.stdout.on("data", function(data) + { + buffer = Buffer.concat([buffer, data]); + }); + + build.stderr.on("data", function(data) + { + gutil.log("'slice2js error'", data.toString()); + }); + + build.on('close', function(code) + { + if(code === 0) + { + fs.writeFileSync(dependFile, buffer); + compile(slice2js, file, args, cb); + } + else + { + cb(new PluginError(PLUGIN_NAME, 'slice2js exit with error code: ' + code)); + } + }); + } + else + { + cb(); + } + } + else + { + compile(slice2js, file, args, cb); + } + }); +}; |