338 lines
14 KiB
JavaScript
338 lines
14 KiB
JavaScript
|
var Promise = require("bluebird");
|
||
|
var path = require("path");
|
||
|
var jobRunner = require("./job-runner/job-runner.js");
|
||
|
Promise.longStackTraces();
|
||
|
var utils = require("./utils.js");
|
||
|
var glob = Promise.promisify(require("glob"));
|
||
|
var fs = Promise.promisifyAll(require("fs"));
|
||
|
var mkdirp = Promise.promisify(require("mkdirp"));
|
||
|
var rimraf = Promise.promisify(require("rimraf"));
|
||
|
|
||
|
jobRunner.init(path.join(__dirname, ".."), function() {
|
||
|
var fs = Promise.promisifyAll(require("fs"));
|
||
|
var utils = require("./tools/utils.js");
|
||
|
var path = require("path");
|
||
|
var astPasses = require("./tools/ast_passes.js");
|
||
|
var Mocha = require("mocha");
|
||
|
astPasses.readConstants(
|
||
|
fs.readFileSync("./src/constants.js", "utf8"),
|
||
|
"constants.js"
|
||
|
);
|
||
|
function applyOptionalRequires(code, depsRequireCode) {
|
||
|
return code.replace( /};([^}]*)$/, depsRequireCode + "\n};$1");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var optionalModuleRequireMap = {
|
||
|
"race.js": true,
|
||
|
"call_get.js": true,
|
||
|
"generators.js": true,
|
||
|
"map.js": true,
|
||
|
"nodeify.js": true,
|
||
|
"promisify.js": true,
|
||
|
"props.js": true,
|
||
|
"reduce.js": true,
|
||
|
"settle.js": true,
|
||
|
"some.js": true,
|
||
|
"using.js": true,
|
||
|
"timers.js": true,
|
||
|
"filter.js": ["map.js"],
|
||
|
"any.js": ["some.js"],
|
||
|
"each.js": ["reduce.js"]
|
||
|
};
|
||
|
|
||
|
var lastLineCode = " \n\
|
||
|
util.toFastProperties(Promise); \n\
|
||
|
util.toFastProperties(Promise.prototype); \n\
|
||
|
function fillTypes(value) { \n\
|
||
|
var p = new Promise(INTERNAL); \n\
|
||
|
p._fulfillmentHandler0 = value; \n\
|
||
|
p._rejectionHandler0 = value; \n\
|
||
|
p._promise0 = value; \n\
|
||
|
p._receiver0 = value; \n\
|
||
|
} \n\
|
||
|
// Complete slack tracking, opt out of field-type tracking and \n\
|
||
|
// stabilize map \n\
|
||
|
fillTypes({a: 1}); \n\
|
||
|
fillTypes({b: 2}); \n\
|
||
|
fillTypes({c: 3}); \n\
|
||
|
fillTypes(1); \n\
|
||
|
fillTypes(function(){}); \n\
|
||
|
fillTypes(undefined); \n\
|
||
|
fillTypes(false); \n\
|
||
|
fillTypes(new Promise(INTERNAL)); \n\
|
||
|
debug.setBounds(Async.firstLineError, util.lastLineError); \n\
|
||
|
return Promise; \n\
|
||
|
";
|
||
|
|
||
|
function getOptionalRequireCode(srcs) {
|
||
|
return srcs.sort(function(a, b) {
|
||
|
var aHasDeps = Array.isArray(optionalModuleRequireMap[a.sourceFileName]);
|
||
|
var bHasDeps = Array.isArray(optionalModuleRequireMap[b.sourceFileName]);
|
||
|
return aHasDeps - bHasDeps;
|
||
|
}).reduce(function(ret, cur, i) {
|
||
|
if(optionalModuleRequireMap[cur.sourceFileName]) {
|
||
|
ret += "require('./"+cur.sourceFileName+"')("+ cur.deps.join(", ") +");\n";
|
||
|
}
|
||
|
return ret;
|
||
|
}, "") + lastLineCode;
|
||
|
}
|
||
|
|
||
|
function getBrowserBuildHeader(sources, npmPackage) {
|
||
|
var header = "/**\n * bluebird build version " + npmPackage.version + "\n";
|
||
|
var enabledFeatures = ["core"];
|
||
|
var disabledFeatures = [];
|
||
|
featureLoop: for (var key in optionalModuleRequireMap) {
|
||
|
for (var i = 0, len = sources.length; i < len; ++i) {
|
||
|
var source = sources[i];
|
||
|
if(source.sourceFileName === key) {
|
||
|
enabledFeatures.push(key.replace( ".js", ""));
|
||
|
continue featureLoop;
|
||
|
}
|
||
|
}
|
||
|
disabledFeatures.push(key.replace(".js", ""));
|
||
|
}
|
||
|
|
||
|
header += (" * Features enabled: " + enabledFeatures.join(", ") + "\n");
|
||
|
|
||
|
if (disabledFeatures.length) {
|
||
|
header += " * Features disabled: " + disabledFeatures.join(", ") + "\n";
|
||
|
}
|
||
|
header += "*/\n";
|
||
|
return header;
|
||
|
}
|
||
|
|
||
|
function getSourcePaths(features) {
|
||
|
return glob("./src/*.js").map(function(v) {
|
||
|
return path.basename(v);
|
||
|
}).then(function(results) {
|
||
|
if (features) features = features.toLowerCase().split(/\s+/g);
|
||
|
return results.filter(function(fileName) {
|
||
|
if (features && optionalModuleRequireMap[fileName] !== undefined) {
|
||
|
for (var i = 0; i < features.length; ++i) {
|
||
|
if (fileName.indexOf(features[i]) >= 0) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
return fileName !== "constants.js";
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function ensureDirectory(dir, isUsed, clean) {
|
||
|
return (clean ? rimraf(dir) : Promise.resolve()).then(function() {
|
||
|
if (!isUsed) return dir;
|
||
|
return mkdirp(dir).thenReturn(dir);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function buildRelease(sources, depsRequireCode, dir) {
|
||
|
return dir.then(function(dir) {
|
||
|
return Promise.map(sources, function(source) {
|
||
|
return jobRunner.run(function() {
|
||
|
var code = source.source;
|
||
|
var sourceFileName = source.sourceFileName;
|
||
|
code = astPasses.removeAsserts(code, sourceFileName);
|
||
|
code = astPasses.inlineExpansion(code, sourceFileName);
|
||
|
code = astPasses.expandConstants(code, sourceFileName);
|
||
|
code = code.replace( /__DEBUG__/g, "false" );
|
||
|
code = code.replace( /__BROWSER__/g, "false" );
|
||
|
if (sourceFileName === "promise.js") {
|
||
|
code = applyOptionalRequires(code, depsRequireCode);
|
||
|
}
|
||
|
return fs.writeFileAsync(path.join(root, sourceFileName), code);
|
||
|
}, {
|
||
|
context: {
|
||
|
depsRequireCode: depsRequireCode,
|
||
|
source: source,
|
||
|
root: dir
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function buildDebug(sources, depsRequireCode, dir) {
|
||
|
return dir.then(function(dir) {
|
||
|
return Promise.map(sources, function(source) {
|
||
|
return jobRunner.run(function() {
|
||
|
var code = source.source;
|
||
|
var sourceFileName = source.sourceFileName;
|
||
|
code = astPasses.expandAsserts(code, sourceFileName);
|
||
|
code = astPasses.inlineExpansion(code, sourceFileName);
|
||
|
code = astPasses.expandConstants(code, sourceFileName);
|
||
|
code = code.replace( /__DEBUG__/g, "true" );
|
||
|
code = code.replace( /__BROWSER__/g, "false" );
|
||
|
if (sourceFileName === "promise.js") {
|
||
|
code = applyOptionalRequires(code, depsRequireCode);
|
||
|
}
|
||
|
return fs.writeFileAsync(path.join(root, sourceFileName), code);
|
||
|
}, {
|
||
|
context: {
|
||
|
depsRequireCode: depsRequireCode,
|
||
|
source: source,
|
||
|
root: dir
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function buildBrowser(sources, dir, tmpDir, depsRequireCode, minify, npmPackage, license) {
|
||
|
return Promise.join(dir, tmpDir, npmPackage, license, function(dir, tmpDir, npmPackage, license) {
|
||
|
return Promise.map(sources, function(source) {
|
||
|
return jobRunner.run(function() {
|
||
|
var code = source.source;
|
||
|
var sourceFileName = source.sourceFileName;
|
||
|
code = astPasses.removeAsserts(code, sourceFileName);
|
||
|
code = astPasses.inlineExpansion(code, sourceFileName, true);
|
||
|
code = astPasses.expandConstants(code, sourceFileName);
|
||
|
code = code.replace( /__BROWSER__/g, "true" );
|
||
|
if (sourceFileName === "promise.js") {
|
||
|
code = applyOptionalRequires(code, depsRequireCode);
|
||
|
}
|
||
|
return fs.writeFileAsync(path.join(root, sourceFileName), code);
|
||
|
}, {
|
||
|
context: {
|
||
|
depsRequireCode: depsRequireCode,
|
||
|
source: source,
|
||
|
root: tmpDir
|
||
|
}
|
||
|
});
|
||
|
}).then(function() {
|
||
|
var header = getBrowserBuildHeader(sources, npmPackage);
|
||
|
return jobRunner.run(function() {
|
||
|
var UglifyJS = require("uglify-js");
|
||
|
var browserify = require("browserify");
|
||
|
var dest = path.join(root, "bluebird.js");
|
||
|
var minDest = path.join(root, "bluebird.min.js");
|
||
|
var b = browserify({
|
||
|
entries: entries,
|
||
|
detectGlobals: false,
|
||
|
standalone: "Promise"
|
||
|
});
|
||
|
return Promise.promisify(b.bundle, b)().then(function(src) {
|
||
|
var alias = "\
|
||
|
;if (typeof window !== 'undefined' && window !== null) { \
|
||
|
window.P = window.Promise; \
|
||
|
} else if (typeof self !== 'undefined' && self !== null) { \
|
||
|
self.P = self.Promise; \
|
||
|
}";
|
||
|
src = src + alias;
|
||
|
src = src.replace(/\brequire\b/g, "_dereq_");
|
||
|
var minWrite, write;
|
||
|
if (minify) {
|
||
|
var minSrc = src.replace( /__DEBUG__/g, "false" );
|
||
|
minSrc = UglifyJS.minify(minSrc, {
|
||
|
comments: false,
|
||
|
compress: true,
|
||
|
fromString: true
|
||
|
}).code;
|
||
|
minSrc = license + header + minSrc;
|
||
|
minWrite = fs.writeFileAsync(minDest, minSrc);
|
||
|
}
|
||
|
src = src.replace( /__DEBUG__/g, "true" );
|
||
|
src = license + header + src;
|
||
|
write = fs.writeFileAsync(dest, src);
|
||
|
|
||
|
return Promise.all([write, minWrite]);
|
||
|
})
|
||
|
}, {
|
||
|
context: {
|
||
|
header: header,
|
||
|
root: dir,
|
||
|
entries: path.join(tmpDir, "bluebird.js"),
|
||
|
license: license,
|
||
|
minify: minify
|
||
|
}
|
||
|
})
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var root = process.cwd();
|
||
|
// Since rm -rf is called, better be sure...
|
||
|
if (path.basename(root).toLowerCase() !== "bluebird") {
|
||
|
throw new Error("cwd must be se to bluebird project root. Cwd is currently\n\n" +
|
||
|
" " + process.cwd() + "\n");
|
||
|
}
|
||
|
var dirs = {
|
||
|
release: path.join(root, "js", "release"),
|
||
|
debug: path.join(root, "js", "debug"),
|
||
|
browser: path.join(root, "js", "browser"),
|
||
|
browserTmp: path.join(root, "js", "tmp"),
|
||
|
instrumented: path.join(root, "js", "instrumented"),
|
||
|
coverage: path.join(root, "coverage")
|
||
|
};
|
||
|
|
||
|
function build(options) {
|
||
|
options = Object(options);
|
||
|
var clean = (typeof options.clean !== "boolean" ? true : options.clean);
|
||
|
var npmPackage = fs.readFileAsync("./package.json").then(JSON.parse);
|
||
|
var sourceFileNames = getSourcePaths(options.features);
|
||
|
var license = utils.getLicense();
|
||
|
var releaseDir = ensureDirectory(dirs.release, options.release, clean);
|
||
|
var debugDir = ensureDirectory(dirs.debug, options.debug, clean);
|
||
|
var browserDir = ensureDirectory(dirs.browser, options.browser, clean);
|
||
|
var browserTmpDir = ensureDirectory(dirs.browserTmp, options.browser, clean);
|
||
|
return license.then(function(license) {
|
||
|
return sourceFileNames.map(function(sourceFileName) {
|
||
|
return jobRunner.run(function() {
|
||
|
var sourcePath = path.join("./src", sourceFileName);
|
||
|
var source = fs.readFileAsync(sourcePath, "utf8");
|
||
|
return source.then(function(source) {
|
||
|
utils.checkAscii(sourceFileName, source);
|
||
|
var deps = null;
|
||
|
if (optionalModuleRequireMap[sourceFileName] !== undefined) {
|
||
|
deps = utils.parseDeps(source);
|
||
|
}
|
||
|
source = astPasses.removeComments(source, sourceFileName);
|
||
|
return {
|
||
|
sourceFileName: sourceFileName,
|
||
|
source: source,
|
||
|
deps: deps
|
||
|
};
|
||
|
});
|
||
|
}, {
|
||
|
context: {
|
||
|
sourceFileName: sourceFileName,
|
||
|
optionalModuleRequireMap: optionalModuleRequireMap,
|
||
|
license: license
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}).then(function(results) {
|
||
|
var depsRequireCode = getOptionalRequireCode(results);
|
||
|
var release, debug, browser;
|
||
|
if (options.release)
|
||
|
release = buildRelease(results, depsRequireCode, releaseDir);
|
||
|
if (options.debug)
|
||
|
debug = buildDebug(results, depsRequireCode, debugDir);
|
||
|
if (options.browser)
|
||
|
browser = buildBrowser(results, browserDir, browserTmpDir, depsRequireCode, options.minify, npmPackage, license);
|
||
|
|
||
|
return Promise.all([release, debug, browser]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
module.exports = build;
|
||
|
module.exports.ensureDirectory = ensureDirectory;
|
||
|
module.exports.dirs = dirs;
|
||
|
|
||
|
|
||
|
if (require.main === module) {
|
||
|
var argv = require("optimist").argv;
|
||
|
var browser = (typeof argv.browser !== "boolean" ? false : argv.browser) || !!argv.features;
|
||
|
var clean = (typeof argv.clean !== "boolean" ? true : argv.clean);
|
||
|
module.exports({
|
||
|
minify: browser && (typeof argv.minify !== "boolean" ? true : argv.minify),
|
||
|
browser: browser,
|
||
|
debug: !!argv.debug,
|
||
|
release: !!argv.release,
|
||
|
features: argv.features || null,
|
||
|
clean: clean
|
||
|
});
|
||
|
}
|