crosswalk-app-tools
Advanced tools
Comparing version 0.6.1 to 0.7.0
@@ -208,3 +208,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
InvalidChannelError.prototype = Error.prototype; | ||
InvalidChannelError.prototype = Object.create(Error.prototype); | ||
InvalidChannelError.prototype.constructor = InvalidChannelError; | ||
@@ -211,0 +212,0 @@ AndroidDependencies.prototype.InvalidChannelError = InvalidChannelError; |
@@ -13,2 +13,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
* @param {String} path Path to manifest.json | ||
* @constructor | ||
*/ | ||
@@ -26,12 +27,19 @@ function AndroidManifest(output, path) { | ||
this._applicationIcon = null; | ||
var node = this.findApplicationNode(doc); | ||
if (node) { | ||
this._applicationIcon = node.getAttribute("android:icon"); | ||
var appNode = this.findApplicationNode(doc); | ||
if (appNode) { | ||
this._applicationIcon = appNode.getAttribute("android:icon"); | ||
} | ||
this._applicationLabel = null; | ||
node = this.findApplicationNode(doc); | ||
if (node) { | ||
this._applicationLabel = node.getAttribute("android:label"); | ||
appNode = this.findApplicationNode(doc); | ||
if (appNode) { | ||
this._applicationLabel = appNode.getAttribute("android:label"); | ||
} | ||
var activityNode = this.findChildNode(appNode, "activity"); | ||
if (activityNode) { | ||
this._screenOrientation = activityNode.getAttribute("android:screenOrientation"); | ||
} | ||
// TODO read other values from manifest and initialize members | ||
} | ||
@@ -135,2 +143,7 @@ | ||
node.setAttribute("android:label", applicationLabel); | ||
// Also set name on the activity | ||
var activityNode = this.findChildNode(node, "activity"); | ||
if (activityNode) | ||
activityNode.setAttribute("android:label", applicationLabel); | ||
// Save | ||
this.write(doc); | ||
@@ -145,2 +158,97 @@ } | ||
/** | ||
* Orientation | ||
* @member {String} screenOrientation Value for <activity android:screenOrientation= in the android manifest | ||
* @instance | ||
* @memberOf AndroidManifest | ||
*/ | ||
Object.defineProperty(AndroidManifest.prototype, "screenOrientation", { | ||
get: function() { | ||
return this._screenOrientation; | ||
}, | ||
set: function(orientation) { | ||
// Check | ||
var values = ["unspecified", "behind", | ||
"landscape", "portrait", | ||
"reverseLandscape", "reversePortrait", | ||
"sensorLandscape", "sensorPortrait", | ||
"userLandscape", "userPortrait", | ||
"sensor", "fullSensor", "nosensor", | ||
"user", "fullUser", "locked"]; | ||
if (values.indexOf(orientation) < 0) { | ||
this._output.warning("Invalid screenOrientation: " + orientation); | ||
return; | ||
} | ||
// Look up <application> node | ||
var doc = this.read(); | ||
var node = this.findApplicationNode(doc); | ||
if (node) { | ||
var activityNode = this.findChildNode(node, "activity"); | ||
if (activityNode) { | ||
activityNode.setAttribute("android:screenOrientation", orientation); | ||
this._screenOrientation = orientation; | ||
} else { | ||
this._output.warning("Did not find <activity> element in AndroidManifest.xml"); | ||
} | ||
// Save | ||
this.write(doc); | ||
} else { | ||
this._output.warning("Did not find <application> element in AndroidManifest.xml"); | ||
} | ||
} | ||
}); | ||
/** | ||
* Permissions | ||
* @member {Array} permissions Android permissisons | ||
* @instance | ||
* @memberOf AndroidManifest | ||
*/ | ||
Object.defineProperty(AndroidManifest.prototype, "permissions", { | ||
get: function() { | ||
var permissions = []; | ||
var doc = this.read(); | ||
for (var idx in doc.documentElement.childNodes) { | ||
var n = doc.documentElement.childNodes[idx]; | ||
if (n.nodeName === "uses-permission") { | ||
var value = n.getAttribute("android:name"); | ||
// e.g. android.permissions.CAMERA, index 2 is actual value | ||
var suffix = value.split(".")[2]; | ||
permissions.push(suffix); | ||
} | ||
} | ||
return permissions; | ||
}, | ||
set: function(permissions) { | ||
if (!(permissions instanceof Array)) { | ||
this._output.warning("Invalid permissions: " + permissions); | ||
return; | ||
} | ||
var doc = this.read(); | ||
// Remove permissions | ||
var child; | ||
var permNodes = doc.getElementsByTagName("uses-permission"); | ||
for (var i = 0; permNodes[i]; i++) { | ||
child = permNodes[i]; | ||
doc.documentElement.removeChild(child); | ||
} | ||
// Add new permissions | ||
this._output.info("Adding permissions " + permissions.join(",")); | ||
permissions.forEach(function (permission) { | ||
child = doc.createElement("uses-permission"); | ||
child.setAttribute("android:name", "android.permission." + permission); | ||
doc.documentElement.appendChild(child); | ||
}); | ||
this.write(doc); | ||
} | ||
}); | ||
/** | ||
* Read AndroidManifest.xml | ||
@@ -195,2 +303,24 @@ * @returns {xmldom.Document} XML Document | ||
/** | ||
* Find named child node | ||
* @param {xmldom.Node} node | ||
* @param {String} name Child name | ||
* @returns {xmldom.Node} Node if found or null | ||
* @private | ||
*/ | ||
AndroidManifest.prototype.findChildNode = | ||
function(node, name) { | ||
var child = null; | ||
for (var idx in node.childNodes) { | ||
child = node.childNodes[idx]; | ||
if (child.nodeName === name) { | ||
return child; | ||
} | ||
} | ||
return null; | ||
}; | ||
module.exports = AndroidManifest; |
@@ -5,5 +5,8 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var ChildProcess = require("child_process"); | ||
var FS = require("fs"); | ||
var Path = require('path'); | ||
var FormatJson = require("format-json"); | ||
var MkTemp = require('mktemp'); | ||
var ShellJS = require("shelljs"); | ||
@@ -14,4 +17,4 @@ | ||
var AndroidSDK = require("./AndroidSDK"); | ||
var CrosswalkZip = require("./CrosswalkZip"); | ||
var JavaActivity = require("./JavaActivity"); | ||
var ProjectProperties = require("./ProjectProperties"); | ||
var XmlTheme = require("./XmlTheme"); | ||
@@ -44,2 +47,4 @@ | ||
instance._channel = "stable"; | ||
instance._shared = false; | ||
instance._apiTarget = null; | ||
@@ -50,3 +55,10 @@ return instance; | ||
/** | ||
* Minimal API level. | ||
* FIXME: this is not the optimal way to specify this. | ||
*/ | ||
AndroidPlatform.MIN_API_LEVEL = 21; | ||
/** | ||
* Accessor function for platform-specific command-line argument spec. | ||
* @static | ||
*/ | ||
@@ -58,3 +70,4 @@ AndroidPlatform.getArgs = | ||
"crosswalk": "\t\t\tChannel name (stable/beta/canary)\n" + | ||
"\t\t\t\t\t\tor version number (w.x.y.z)" | ||
"\t\t\t\t\t\tor version number (w.x.y.z)", | ||
"shared": " Depend on shared crosswalk installation" | ||
} | ||
@@ -66,2 +79,3 @@ }; | ||
* Accessor function for platform-specific environment variables spec. | ||
* @static | ||
*/ | ||
@@ -76,2 +90,137 @@ AndroidPlatform.getEnv = | ||
/** | ||
* Check host setup. | ||
* @param {OutputIface} output Output to write to | ||
* @param {Function} callback Function(success) to be called when done | ||
* @static | ||
*/ | ||
AndroidPlatform.check = | ||
AndroidPlatform.prototype.check = | ||
function(output, callback) { | ||
// Checking deps | ||
var deps = [ | ||
"android", | ||
"ant", | ||
"java" | ||
]; | ||
var found = true; | ||
var msg; | ||
deps.forEach(function (dep) { | ||
var path = ShellJS.which(dep); | ||
msg = "Checking for " + dep + "... " + path; | ||
if (path) { | ||
output.info(msg); | ||
} else { | ||
found = false; | ||
output.error(msg); | ||
} | ||
}); | ||
// Checking env | ||
var androidHome = "ANDROID_HOME"; | ||
msg = "Checking for " + androidHome + "... "; | ||
if (process.env[androidHome]) { | ||
output.info(msg + process.env[androidHome]); | ||
} else { | ||
found = false; | ||
output.info(msg + "empty"); | ||
output.error(androidHome + " needs to be set for builds to work"); | ||
} | ||
if (!found) { | ||
callback(false); | ||
return; | ||
} | ||
// Build dummy project | ||
var app = { | ||
output: output | ||
}; | ||
ShellJS.pushd(ShellJS.tempdir()); | ||
var dir = MkTemp.createDirSync("XXXXXX"); | ||
// Delete dir right after, just allocate name. | ||
ShellJS.rm("-rf", dir); | ||
ShellJS.popd(); | ||
var path = Path.join(ShellJS.tempdir(), dir); | ||
output.info("Testing dummy project in " + path); | ||
var dummyPackageId = "com.example.foo"; | ||
var dummyLog = ""; | ||
var sdk = new AndroidSDK(app); | ||
// Progress display | ||
var indicator = output.createInfiniteProgress("Building " + dummyPackageId); | ||
sdk.onData = function(data) { | ||
dummyLog += data; | ||
// Scan first 7 chars if data starts with a [tag] | ||
var tag = null; | ||
for (var i = 0; i < 7 && i < data.length; i++) { | ||
if (data[i] === '[') { | ||
// Scan on a bit if there's a closing ']' | ||
for (j = i+1; j < i+15; j++) { | ||
if (data[j] === ']') { | ||
tag = data.substring(i+1, j); | ||
indicator.update(tag); | ||
return; | ||
} | ||
} | ||
} else if (data[i] != ' ') { | ||
break; | ||
} | ||
} | ||
}; | ||
sdk.queryTarget(AndroidPlatform.MIN_API_LEVEL, | ||
function(apiTarget, errormsg) { | ||
if (errormsg) { | ||
callback(errormsg); | ||
return; | ||
} | ||
sdk.generateProjectSkeleton(path, dummyPackageId, apiTarget, | ||
function (path, logmsg, errormsg) { | ||
dummyLog += logmsg; | ||
if (!path || errormsg) { | ||
output.error(errormsg); | ||
ShellJS.rm("-rf", path); | ||
output.error("Generating project failed"); | ||
if (dummyLog) { | ||
FS.writeFileSync(path, dummyLog); | ||
output.error("Consult logfile " + path); | ||
} | ||
callback(false); | ||
return; | ||
} | ||
// Build | ||
ShellJS.pushd(path); | ||
sdk.buildProject(false, | ||
function(success) { | ||
ShellJS.popd(); | ||
ShellJS.rm("-rf", path); | ||
indicator.done(); | ||
if (!success) { | ||
output.error("Building project failed"); | ||
if (dummyLog) { | ||
FS.writeFileSync(path, dummyLog); | ||
output.error("Consult logfile " + path); | ||
} | ||
} | ||
callback(success); | ||
}); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Fill template files and put them into the project skeleton. | ||
@@ -93,3 +242,4 @@ * @param {String} apiTarget Android API target (greater android-14) | ||
"packageName" : this.packageId, | ||
"apiTarget" : apiTarget | ||
"apiTarget" : apiTarget, | ||
"xwalkLibrary" : this._shared ? "xwalk_shared_library" : "xwalk_core_library" | ||
}; | ||
@@ -110,6 +260,5 @@ | ||
// Make html5 app dir and copy sample content | ||
var assetsPath = Path.join(platformPath, "assets"); | ||
ShellJS.mkdir("-p", assetsPath); | ||
var wwwPath = Path.join(assetsPath, "www"); | ||
ShellJS.ln("-s", this.appPath, wwwPath); | ||
var wwwPath = Path.join(platformPath, "assets", "www"); | ||
ShellJS.mkdir("-p", wwwPath); | ||
ShellJS.cp("-rf", this.appPath + Path.sep + "*", wwwPath); | ||
@@ -129,2 +278,5 @@ // TODO check for errors | ||
// Namespace util | ||
var util = this.application.util; | ||
var output = this.application.output; | ||
@@ -137,3 +289,3 @@ | ||
try { | ||
zip = new CrosswalkZip(crosswalkPath); | ||
zip = new util.CrosswalkZip(crosswalkPath); | ||
} catch (e) { | ||
@@ -165,8 +317,11 @@ // HACK we're in the midst of a progress display, force line break | ||
// Extract xwalk_core_library | ||
// Extract xwalk_core_library or xwalk_shared_library | ||
var path; | ||
var name = zip.root + "xwalk_core_library/"; | ||
var xwalkLibrary = this._shared ? | ||
"xwalk_shared_library" : | ||
"xwalk_core_library"; | ||
var name = zip.root + xwalkLibrary + "/"; | ||
entry = zip.getEntry(name); | ||
if (entry) { | ||
path = platformPath + Path.sep + "xwalk_core_library"; | ||
path = Path.join(platformPath, xwalkLibrary); | ||
// Remove existing dir to prevent stale files when updating crosswalk | ||
@@ -181,5 +336,11 @@ ShellJS.rm("-rf", path); | ||
// Extract jars | ||
indicator.update(0.5); | ||
// Update project properties | ||
var props = new ProjectProperties(Path.join(this.platformPath, "project.properties"), output); | ||
props.androidLibraryReference1 = xwalkLibrary; | ||
props = new ProjectProperties(Path.join(this.platformPath, xwalkLibrary, "project.properties"), output); | ||
props.target = this._apiTarget; | ||
indicator.update(0.6); | ||
@@ -254,3 +415,16 @@ | ||
if (AndroidDependencies.CHANNELS.indexOf(versionSpec) > -1) { | ||
if (ShellJS.test("-f", versionSpec)) { | ||
// versionSpec is a filename, import directly | ||
var filename = Path.normalize(Path.resolve(versionSpec)); | ||
output.info("Using " + versionSpec); | ||
errormsg = null; | ||
var importedVersion = this.importCrosswalkFromZip(filename, platformPath); | ||
if (!importedVersion) { | ||
errormsg = "Failed to extract " + filename; | ||
} | ||
callback(importedVersion, errormsg); | ||
return; | ||
} else if (AndroidDependencies.CHANNELS.indexOf(versionSpec) > -1) { | ||
// versionSpec is a channel name | ||
@@ -262,2 +436,3 @@ channel = versionSpec; | ||
// Download | ||
this.findCrosswalkVersion(version, channel, | ||
@@ -307,4 +482,7 @@ function(version, channel, errormsg) { | ||
var minApiLevel = 21; | ||
this._sdk.queryTarget(minApiLevel, | ||
if (args.shared) { | ||
this._shared = true; | ||
} | ||
this._sdk.queryTarget(AndroidPlatform.MIN_API_LEVEL, | ||
function(apiTarget, errormsg) { | ||
@@ -317,2 +495,3 @@ | ||
this._apiTarget = apiTarget; | ||
output.info("Building against API level " + apiTarget); | ||
@@ -437,14 +616,25 @@ | ||
this.importCrosswalk(versionSpec, this.platformPath, | ||
function(version, errormsg) { | ||
var sdk = new AndroidSDK(this.application); | ||
sdk.queryTarget(AndroidPlatform.MIN_API_LEVEL, | ||
function(apiTarget, errormsg) { | ||
if (errormsg) { | ||
output.error(errormsg); | ||
callback("Updating crosswalk to '" + version + "' failed"); | ||
callback(errormsg); | ||
return; | ||
} | ||
this._apiTarget = apiTarget; | ||
output.info("Project updated to crosswalk '" + version + "'"); | ||
callback(null); | ||
}); | ||
this.importCrosswalk(versionSpec, this.platformPath, | ||
function(version, errormsg) { | ||
if (errormsg) { | ||
output.error(errormsg); | ||
callback("Updating crosswalk to '" + version + "' failed"); | ||
return; | ||
} | ||
output.info("Project updated to crosswalk '" + version + "'"); | ||
callback(null); | ||
}); | ||
}.bind(this)); | ||
}; | ||
@@ -469,3 +659,10 @@ | ||
if (!ShellJS.test("-d", "xwalk_core_library/libs")) { | ||
// There is no need to enable/disable various ABIs when building shared. | ||
// Only one APK is being built. | ||
if (this._shared) { | ||
return true; | ||
} | ||
var libsDir = "xwalk_core_library/libs"; | ||
if (!ShellJS.test("-d", libsDir)) { | ||
output.error("This does not appear to be the root of a Crosswalk project."); | ||
@@ -475,3 +672,3 @@ return false; | ||
ShellJS.pushd("xwalk_core_library/libs"); | ||
ShellJS.pushd(libsDir); | ||
@@ -521,9 +718,9 @@ var abiMatched = false; | ||
if (release) { | ||
apkInPattern = "*-release-unsigned.apk"; | ||
apkInPattern = "-release-unsigned.apk"; | ||
} else { | ||
apkInPattern = "*-debug.apk"; | ||
apkInPattern = "-debug.apk"; | ||
} | ||
ShellJS.pushd("bin"); | ||
var apkInName = ShellJS.ls(apkInPattern)[0]; | ||
var apkInName = ShellJS.ls("*" + apkInPattern)[0]; | ||
ShellJS.popd(); | ||
@@ -536,4 +733,7 @@ | ||
var base = apkInName.substring(0, apkInName.length - ".apk".length); | ||
var apkOutName = base + "." + abi + ".apk"; | ||
var base = apkInName.substring(0, apkInName.length - apkInPattern.length); | ||
var apkOutName = base + "-" + | ||
this.application.manifest.appVersion + "-" + | ||
(release ? "release-unsigned" : "debug") + "." + | ||
abi + ".apk"; | ||
ShellJS.mv("bin" + Path.sep + apkInName, | ||
@@ -569,2 +769,3 @@ "bin" + Path.sep + apkOutName); | ||
var abiCodes = { | ||
"shared": 2, // use same as ARM, TODO check if correct. | ||
"armeabi-v7a": 2, | ||
@@ -723,31 +924,81 @@ // "arm64": 3, TODO check name | ||
/** | ||
* Update icons from the manifest. | ||
* Apply icon if none yet, or source has higher quality | ||
* @param {String} srcPath Icon to apply | ||
* @param {String} dstDir Path to destination directory | ||
* @param {String} iconFilename Destination icon filename without extension | ||
* @param {Function} callback Error callback | ||
* @returns {Boolean} True if applied, otherwise false. | ||
*/ | ||
AndroidPlatform.prototype.updateManifest = | ||
function(callback) { | ||
AndroidPlatform.prototype.applyIcon = | ||
function(srcPath, dstDir, iconFilename, callback) { | ||
var output = this.application.output; | ||
var manifest = new AndroidManifest(output, | ||
Path.join(this.platformPath, "AndroidManifest.xml")); | ||
// Different image types get different priorities. | ||
var score = { | ||
"png": 3, | ||
"jpeg": 2, | ||
"jpg": 2, | ||
"gif": 1 | ||
}; | ||
// Renaming package is not supported. | ||
if (manifest.package !== this.application.manifest.packageId) { | ||
callback("Renaming of package not supported (" + | ||
manifest.package + "/" + this.application.manifest.packageId + ")"); | ||
return; | ||
// extname() includes the ".", so strip it | ||
var srcExt = Path.extname(srcPath); | ||
if (srcExt && srcExt[0] === ".") | ||
srcExt = srcExt.substring(1); | ||
var srcScore = score[srcExt.toLowerCase()]; | ||
if (!srcScore) { | ||
output.warning("Image type not supported: " + srcPath); | ||
return false; | ||
} | ||
manifest.versionName = this.application.manifest.appVersion; | ||
manifest.applicationLabel = this.application.manifest.name; | ||
// Replace existing icon if we have a better one. | ||
var curPath = null; | ||
var curScore = -1; | ||
var ls = ShellJS.ls(Path.join(dstDir, iconFilename + "*")); | ||
if (ls.length > 1) { | ||
output.warning("Unexpected extra files in " + dstDir); | ||
} | ||
if (ls.length > 0) { | ||
// Update icons | ||
// extname() includes the ".", so strip it | ||
curPath = ls[0]; | ||
var curExt = Path.extname(curPath); | ||
if (curExt && curExt[0] === ".") | ||
curExt = curExt.substring(1); | ||
curScore = +score[curExt.toLowerCase()]; | ||
} | ||
if (srcScore >= curScore) { | ||
if (curPath) { | ||
// We have found a better quality icon | ||
ShellJS.rm(curPath); | ||
} | ||
var dstPath = Path.join(dstDir, iconFilename + Path.extname(srcPath)); | ||
ShellJS.cp("-f", srcPath, dstPath); | ||
} | ||
return true; | ||
}; | ||
/** | ||
* Update launcher icons. | ||
* @param {AndroidManifest} androidManifest | ||
* @param {Function} callback Error callback | ||
*/ | ||
AndroidPlatform.prototype.updateIcons = | ||
function(androidManifest, callback) { | ||
var output = this.application.output; | ||
// See http://iconhandbook.co.uk/reference/chart/android/ | ||
var sizes = { | ||
"ldpi": 47, | ||
"mdpi": 71, | ||
"hdpi": 95, | ||
"xhdpi": 143, | ||
"xxhdpi": 191, | ||
"xxxhdpi": 511, // whatever, this is default for even bigger icons. | ||
"ldpi": 36, | ||
"mdpi": 48, | ||
"hdpi": 72, | ||
"xhdpi": 96, | ||
"xxhdpi": 144, | ||
"xxxhdpi": 192, | ||
match: function(size) { | ||
@@ -760,11 +1011,17 @@ | ||
// Match size as per categories above. | ||
for (var prop in this) { | ||
if (this[prop] >= size) { | ||
// Start from the biggest size, and pick the first | ||
// one where the icon is bigger or same. | ||
var keys = Object.keys(this); | ||
for (var k = keys.length - 1; k >= 0; k--) { | ||
var prop = keys[k]; | ||
if (size >= this[prop]) { | ||
return prop; | ||
} | ||
} | ||
return "xxxhdpi"; | ||
// Default to smallest size when below 36. | ||
return "ldpi"; | ||
} | ||
}; | ||
var nUpdated = 0; | ||
var iconFilename = "crosswalk_icon"; | ||
@@ -791,17 +1048,109 @@ | ||
var dstPath = Path.join(this.platformPath, "res", "mipmap-" + density); | ||
var dst = Path.join(dstPath, iconFilename + Path.extname(icon.src)); | ||
ShellJS.mkdir(dstPath); | ||
ShellJS.cp(src, dst); | ||
var ret = this.applyIcon(src, dstPath, iconFilename, callback); | ||
if (ret) | ||
nUpdated++; | ||
} | ||
} | ||
manifest.applicationIcon = "@mipmap/" + iconFilename; | ||
if (nUpdated > 0) { | ||
androidManifest.applicationIcon = "@mipmap/" + iconFilename; | ||
} else { | ||
output.warning("No usable icons found in manifest.json"); | ||
output.warning("Using builtin default icon"); | ||
} else { | ||
output.warning("No icons found in manifest.json"); | ||
// Fall back to the default icon | ||
manifest.applicationIcon = "@drawable/crosswalk"; | ||
androidManifest.applicationIcon = "@drawable/crosswalk"; | ||
// Make sure the icon is present. | ||
ShellJS.cp(Path.join(__dirname, "..", "..", "app-template", "icon.png"), | ||
Path.join(this.platformPath, "res", "drawable-hdpi", "crosswalk.png")); | ||
} | ||
return nUpdated; | ||
}; | ||
/** | ||
* Configure crosswalk runtime. | ||
*/ | ||
AndroidPlatform.prototype.updateEngine = | ||
function() { | ||
var output = this.application.output; | ||
// Write command-line params file. | ||
var path = Path.join(this.platformPath, "assets", "xwalk-command-line"); | ||
if (this.application.manifest.commandLine) { | ||
// Write file. | ||
output.info("Writing command-line parameters file"); | ||
var commandLine = "xwalk " + this.application.manifest.commandLine; | ||
FS.writeFileSync(path, commandLine); | ||
} else { | ||
// Delete file to make sure there's not a stale one | ||
ShellJS.rm(path); | ||
} | ||
}; | ||
/** | ||
* Update android manifest. | ||
* @param {Function} callback Error callback | ||
*/ | ||
AndroidPlatform.prototype.updateManifest = | ||
function(callback) { | ||
var output = this.application.output; | ||
var manifest = new AndroidManifest(output, | ||
Path.join(this.platformPath, "AndroidManifest.xml")); | ||
// Renaming package is not supported. | ||
if (manifest.package !== this.application.manifest.packageId) { | ||
callback("Renaming of package not supported (" + | ||
manifest.package + "/" + this.application.manifest.packageId + ")"); | ||
return; | ||
} | ||
manifest.versionName = this.application.manifest.appVersion; | ||
manifest.applicationLabel = this.application.manifest.name; | ||
// Update icons | ||
this.updateIcons(manifest, callback); | ||
// Update orientation | ||
switch (this.application.manifest.orientation) { | ||
case "any": | ||
manifest.screenOrientation = "unspecified"; | ||
break; | ||
case "natural": | ||
manifest.screenOrientation = "sensor"; | ||
break; | ||
case "landscape": | ||
manifest.screenOrientation = "sensorLandscape"; | ||
break; | ||
case "portrait": | ||
manifest.screenOrientation = "sensorPortrait"; | ||
break; | ||
case "portrait-primary": | ||
manifest.screenOrientation = "portrait"; | ||
break; | ||
case "portrait-secondary": | ||
manifest.screenOrientation = "reversePortrait"; | ||
break; | ||
case "landscape-primary": | ||
manifest.screenOrientation = "landscape"; | ||
break; | ||
case "landscape-secondary": | ||
manifest.screenOrientation = "reverseLandscape"; | ||
break; | ||
default: | ||
output.warning("Unsupported orientation value in web manifest: " + this.application.manifest.orientation); | ||
} | ||
// Update permissions | ||
manifest.permissions = this.application.manifest.androidPermissions; | ||
}; | ||
/** | ||
* Update java activity file for build config. | ||
@@ -854,2 +1203,208 @@ * @param {Boolean} release True if release build, false if debug | ||
/** | ||
* Convert assets to webp format | ||
*/ | ||
AndroidPlatform.prototype.updateWebApp = | ||
function() { | ||
var output = this.application.output; | ||
// Always copy over the app tree to the android project | ||
var wwwPath = Path.join(this.platformPath, "assets", "www"); | ||
ShellJS.rm("-rf", wwwPath + Path.sep + "*"); | ||
output.info("Copying app to " + wwwPath); | ||
ShellJS.cp("-rf", this.appPath + Path.sep + "*", wwwPath); | ||
var params = this.application.manifest.androidWebp; | ||
if (!params) { | ||
// No webp conversion needed. | ||
return; | ||
} | ||
output.info("Converting image assets to webp format (" + params + ")"); | ||
// Check for conversion tool | ||
var cwebp = ShellJS.which("cwebp"); | ||
output.info("Checking for cwebp ... " + cwebp); | ||
if (!cwebp) { | ||
output.warning("Webp conversion tool not found, install from http://downloads.webmproject.org/releases/webp"); | ||
output.warning("Webp conversion failed, packaging unconverted assets"); | ||
return; | ||
} | ||
// Quality parameters | ||
var jpegQuality = 80; | ||
var pngQuality = 80; | ||
var pngAlphaQuality = 80; | ||
var argsList = []; | ||
if (typeof params === "string") | ||
argsList = params.split(/[ ,]+/); | ||
if (argsList && argsList.length > 0) | ||
jpegQuality = argsList[0]; | ||
if (argsList && argsList.length > 1) | ||
pngQuality = argsList[1]; | ||
if (argsList && argsList.length > 2) | ||
pngAlphaQuality = argsList[2]; | ||
// Directory traversal function | ||
function walk(dir) { | ||
var results = []; | ||
var list = FS.readdirSync(dir); | ||
list.forEach(function(file) { | ||
file = dir + "/" + file; | ||
var stat = FS.statSync(file); | ||
if (stat && stat.isDirectory()) | ||
results = results.concat(walk(file)); | ||
else | ||
results.push(file); | ||
}); | ||
return results; | ||
} | ||
// Do conversion | ||
var fileList = walk(wwwPath); | ||
var progress = output.createInfiniteProgress("Converting images to webp"); | ||
for (var i in fileList) { | ||
if (FS.lstatSync(fileList[i]).isFile()) { | ||
var filePath = fileList[i]; | ||
var tmpFilePath = filePath + ".webp"; | ||
var ext = Path.extname(filePath); | ||
progress.update(filePath); | ||
if (".jpeg" == ext || ".jpg" == ext) { | ||
execSync(cwebp + | ||
" " + filePath + | ||
" -q " + jpegQuality + | ||
" -o " + tmpFilePath); | ||
ShellJS.mv("-f", tmpFilePath, filePath); | ||
} else if (".png" == ext) { | ||
execSync(cwebp + | ||
" " + filePath + | ||
" -q " + pngQuality + | ||
" -alpha_q " + pngAlphaQuality + | ||
" -o " + tmpFilePath); | ||
ShellJS.mv("-f", tmpFilePath, filePath); | ||
} | ||
} | ||
} | ||
progress.done(); | ||
var logOutput = this.logOutput; | ||
var execSyncImpl; | ||
function execSync(cmd) { | ||
// On first run, work out which implementation to use. | ||
if (typeof execSyncImpl === "undefined") { | ||
if (ChildProcess.execSync) { | ||
// Nodejs >= 0.12 | ||
execSyncImpl = ChildProcess.execSync; | ||
} else { | ||
// Try to use npm module. | ||
try { | ||
// Exec-sync does throw even though it works, so let's use this hack, | ||
// it's just for nodejs 0.10 compat anyway. | ||
execSyncImpl = function(cmd) { try { return require("exec-sync")(cmd); } catch (e) {} return null; }; | ||
} catch (e) { | ||
output.error("NPM module 'exec-sync' not found"); | ||
output.error("Please install this package manually when on nodejs < 0.12"); | ||
execSyncImpl = null; | ||
} | ||
} | ||
} | ||
if (execSyncImpl !== null) { | ||
var ret = execSyncImpl(cmd); | ||
if (ret) { | ||
logOutput.write(cmd + "\n" + ret + "\n"); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Import extensions. | ||
* The directory of one external extension should be like: | ||
* myextension/ | ||
* myextension.jar | ||
* myextension.js | ||
* myextension.json | ||
* That means the name of the internal files should be the same as the | ||
* directory name. | ||
* For .jar files, they'll be copied to libs/ and then | ||
* built into classes.dex in the APK. | ||
* For .js files, they'll be copied into assets/xwalk-extensions/. | ||
* For .json files, the'll be merged into one file called | ||
* extensions-config.json and copied into assets/. | ||
*/ | ||
AndroidPlatform.prototype.importExtensions = | ||
function() { | ||
var output = this.application.output; | ||
var extensionsConfig = []; | ||
var extensionsPerms = []; | ||
this.application.manifest.extensions.forEach(function (extPath) { | ||
// Test integrity | ||
if (!ShellJS.test("-d", extPath)) { | ||
output.warning("Skipping invalid extension dir " + extPath); | ||
return; | ||
} | ||
var extName = Path.basename(extPath); | ||
var jarPath = Path.join(extPath, extName + ".jar"); | ||
if (!ShellJS.test("-f", jarPath)) { | ||
output.warning("Skipping extension, file not found " + jarPath); | ||
return; | ||
} | ||
var jsPath = Path.join(extPath, extName + ".js"); | ||
if (!ShellJS.test("-f", jsPath)) { | ||
output.warning("Skipping extension, file not found " + jsPath); | ||
return; | ||
} | ||
var jsonPath = Path.join(extPath, extName + ".json"); | ||
if (!ShellJS.test("-f", jsonPath)) { | ||
output.warning("Skipping extension, file not found " + jsonPath); | ||
return; | ||
} | ||
// Copy | ||
ShellJS.cp("-f", jarPath, Path.join(this.platformPath, "libs")); | ||
var jsDstPath = Path.join(this.platformPath, "assets", "xwalk-extensions"); | ||
ShellJS.mkdir(jsDstPath); | ||
ShellJS.cp("-f", jsPath, jsDstPath); | ||
// Accumulate config | ||
var jsonBuf = FS.readFileSync(jsonPath, {"encoding": "utf8"}); | ||
var configJson = JSON.parse(jsonBuf); | ||
configJson.jsapi = [ "xwalk-extensions", configJson.jsapi ].join("/"); | ||
extensionsConfig.push(configJson); | ||
// Accumulate permissions | ||
for (var i = 0; configJson.permissions && i < configJson.permissions.length; i++) { | ||
var perm = configJson.permissions[i]; | ||
// Support both android.permission.FOO namespaced and plain | ||
// version by using last component. | ||
perm = perm.split(".").pop(); | ||
extensionsPerms.push(perm); | ||
} | ||
}.bind(this)); | ||
// Write config | ||
if (extensionsConfig.length > 0) { | ||
var configJsonPath = Path.join(this.platformPath, "assets", "extensions-config.json"); | ||
FS.writeFileSync(configJsonPath, FormatJson.plain(extensionsConfig)); | ||
} | ||
// Add permissions to manifest, so they end up in AndroidManifest.xml later | ||
extensionsPerms.forEach(function (perm) { | ||
var perms = this.application.manifest.androidPermissions; | ||
if (perms.indexOf(perm) < 0) { | ||
perms.push(perm); | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* Implements {@link PlatformBase.build} | ||
@@ -865,7 +1420,15 @@ */ | ||
// Embedded or shared build? | ||
if (ShellJS.test("-d", Path.join(this.platformPath, "xwalk_shared_library"))) { | ||
this._shared = true; | ||
} | ||
this.updateEngine(); | ||
this.importExtensions(); | ||
this.updateManifest(callback); | ||
this.updateJavaActivity(configId === "release"); | ||
this.updateWebApp(this.application.manifest.androidWebp); | ||
var closure = { | ||
abis: ["armeabi-v7a", "x86"], // TODO export option | ||
abis: this._shared ? ["shared"] : ["armeabi-v7a", "x86"], // TODO export option | ||
abiIndex : 0, | ||
@@ -887,4 +1450,8 @@ release: configId == "release", // TODO verify above | ||
output.highlight(" + " + closure.apks[i]); | ||
output.write(" + " + closure.apks[i] + "\n"); | ||
} | ||
if (configId === "release") { | ||
output.highlight(" Sign APKs before publishing: " + | ||
"https://developer.android.com/tools/publishing/app-signing.html#signing-manually"); | ||
} | ||
} | ||
@@ -891,0 +1458,0 @@ callback(errormsg); |
@@ -167,8 +167,10 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var child = ChildProcess.execFile(this._scriptPath, args, {}, | ||
function(errmsg, stdlog, errlog) { | ||
function(error, stdlog, errlog) { | ||
errlog = this.filterErrorLog(errlog); | ||
if (errlog && !errmsg) { | ||
// Pass back errlog output as error message. | ||
errmsg = errlog; | ||
errmsg = this.filterErrorLog(errlog); | ||
if (error && error.message) { | ||
errmsg += error.message; | ||
indicator.done("error"); | ||
} else { | ||
indicator.done(); | ||
} | ||
@@ -185,3 +187,2 @@ | ||
indicator.done(); | ||
callback(path, stdlog, errmsg); | ||
@@ -266,3 +267,4 @@ return; | ||
} | ||
SDKNotFoundError.prototype = Error.prototype; | ||
SDKNotFoundError.prototype = Object.create(Error.prototype); | ||
SDKNotFoundError.prototype.constructor = SDKNotFoundError; | ||
@@ -269,0 +271,0 @@ AndroidSDK.prototype.SDKNotFoundError = SDKNotFoundError; |
@@ -14,2 +14,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
* @param {String} path Path to android app's main java activity file | ||
* @constructor | ||
*/ | ||
@@ -208,3 +209,3 @@ function JavaActivity(output, path) { | ||
found = false; | ||
while (lines[i] !== " }") { | ||
while (lines[i] !== " super.onCreate(savedInstanceState);") { | ||
outBuf.push(lines[i]); | ||
@@ -211,0 +212,0 @@ i++; |
@@ -13,2 +13,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
* @param {String} path Path to manifest.json | ||
* @constructor | ||
*/ | ||
@@ -15,0 +16,0 @@ function XmlTheme(output, path) { |
@@ -134,3 +134,57 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
test.done(); | ||
}, | ||
permissions: function(test) { | ||
test.expect(4); | ||
var manifest; | ||
var path = createManifest(); | ||
manifest = new AndroidManifest(_output, path); | ||
manifest.permissions = ["CAMERA", "INTERNET"]; | ||
manifest = new AndroidManifest(_output, path); | ||
var permissions = manifest.permissions; | ||
test.equal(permissions instanceof Array, true); | ||
test.equal(permissions.length === 2, true); | ||
test.equal(permissions.indexOf("CAMERA") > -1, true); | ||
test.equal(permissions.indexOf("INTERNET") > -1, true); | ||
ShellJS.rm("-f", path); | ||
test.done(); | ||
}, | ||
screenOrientation: function(test) { | ||
test.expect(18); | ||
var manifest; | ||
var path = createManifest(); | ||
manifest = new AndroidManifest(_output, path); | ||
// Default | ||
test.equal(manifest.screenOrientation, "unspecified"); | ||
var values = ["unspecified", "behind", | ||
"landscape", "portrait", | ||
"reverseLandscape", "reversePortrait", | ||
"sensorLandscape", "sensorPortrait", | ||
"userLandscape", "userPortrait", | ||
"sensor", "fullSensor", "nosensor", | ||
"user", "fullUser", "locked"]; | ||
values.forEach(function (value) { | ||
// Test write, then read and compare. | ||
manifest.screenOrientation = value; | ||
test.equal(manifest.screenOrientation, value); | ||
}); | ||
manifest.screenOrientation = "unspecified"; | ||
// Test bogus value | ||
manifest.screenOrientation = "foo"; | ||
test.equal(manifest.screenOrientation, "unspecified"); | ||
ShellJS.rm("-f", path); | ||
test.done(); | ||
} | ||
}; |
{ | ||
"name": "crosswalk-app-tools", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"description": "An APK packager for the Crosswalk Project -- http://crosswalk-project.org", | ||
"author": "Robert Staudinger <robert.staudinger@intel.com>", | ||
"license": "Apache V2", | ||
"license": "Apache-2.0", | ||
"engines" : { | ||
"node" : ">=0.12" | ||
}, | ||
"bin": { | ||
"crosswalk-app": "./src/crosswalk-app" | ||
"crosswalk-app": "./src/crosswalk-app", | ||
"crosswalk-pkg": "./src/crosswalk-pkg" | ||
}, | ||
@@ -25,3 +29,6 @@ "scripts": { | ||
"mustache": "~0.8.2", | ||
"node-uuid": "~1.4.3", | ||
"readdir": "~0.0.13", | ||
"shelljs": "~0.3.0", | ||
"xmlbuilder": "~2.6.4", | ||
"xmldom": "~0.1.19" | ||
@@ -33,5 +40,5 @@ }, | ||
"grunt-contrib-nodeunit": "~0.4.1", | ||
"grunt-jsdoc": "~0.4.2", | ||
"grunt-jsdoc": "~0.6.7", | ||
"grunt-release": "~0.9.0", | ||
"jsdoc": "https://github.com/jsdoc3/jsdoc/archive/v3.2.2.tar.gz", | ||
"jsdoc": "~3.3.2", | ||
"nodeunit": "~0.9.0" | ||
@@ -59,4 +66,5 @@ }, | ||
"test", | ||
"test-util" | ||
"test-util", | ||
"windows" | ||
] | ||
} |
111
README.md
Crosswalk-app-tools | ||
=================== | ||
Command line tools to create and package Crosswalk applications. The license for this project is Apache License | ||
Version 2.0, please refer to the LICENSE-APACHE-V2 included with the package. | ||
Crosswalk-app-tools is our forthcoming packaging tool for creating Crosswalk applications. We are inviting early adopters to build their web applications using crosswalk-app-tools, and provide feedback for future improvements. | ||
@@ -12,75 +9,91 @@ | ||
Crosswalk-app-tools is cross-platform by virtue of being based on Node.js. We are supporting Microsoft Windows, Apple OS X and Linux (testing is mostly done on Fedora and Ubuntu distributions). | ||
The tools are cross-platform by virtue of being based on Node.js. We are supporting Microsoft Windows, Apple OS X and Linux as host operating systems. | ||
The following components are required | ||
1. Android SDK with 5.0 (target-21) installed | ||
2. Java JDK and Apache Ant | ||
3. Node.js and NPM | ||
1. Node.js and NPM | ||
2. Android SDK with 5.0 (target-21) or later installed, plus Java JDK and Apache Ant for creating Android APK packages | ||
In order to get the tools available from the command-line easily, global npm installation is recommended. | ||
Microsoft Windows: `npm install -g crosswalk-app-tools` | ||
Apple OS X and Linux: `sudo npm install -g crosswalk-app-tools` | ||
The best way to check if a machine has all the required dependencies is to create and build a plain empty Android app | ||
on the system. If this does not work, then building Crosswalk apps will not succeed either. | ||
on the system. If this does not work, then building Crosswalk apps will not succeed either. App-tools provides a command for doing this: | ||
``` | ||
android create project -a MainActivity -k com.example.foo -p com.example.foo -t android-21 | ||
cd com.example.foo | ||
ant debug | ||
crosswalk-app check android | ||
``` | ||
In order to get the `crosswalk-app` script available everywhere, global npm installation is required. | ||
``` | ||
Microsoft Windows: npm install -g crosswalk-app-tools | ||
Apple OS X and Linux: sudo npm install -g crosswalk-app-tools | ||
``` | ||
Two executables are provided, `crosswalk-app` implements low level helper commands, `crosswalk-pkg` is the main tool for creating packages. | ||
### Usage | ||
``` | ||
Crosswalk Project Application Packaging Tool | ||
Crosswalk Project Packaging Tool -- https://crosswalk-project.org | ||
Usage: crosswalk-pkg <options> <path> | ||
crosswalk-app create <package-id> Create project <package-id> | ||
--platforms=<target> Optional, e.g. "windows" | ||
<options> | ||
-c --crosswalk=<version-spec>: Runtime version | ||
-h --help: Print usage information | ||
-p --platforms=<android|windows>: Target platform | ||
-r --release=true: Build release packages | ||
-v --version: Print tool version | ||
crosswalk-app build [release|debug] [<dir>] Build project to create packages | ||
Defaults to "debug" when not given | ||
Tries to build in current dir by default | ||
<path> | ||
Path to directory that contains a web app | ||
crosswalk-app update <channel>|<version> Update Crosswalk to latest in named | ||
channel, or specific version | ||
<version-spec> | ||
* Channel name, i.e. "stable". "beta", or "canary" | ||
* Version number, e.g. 14.43.343.25 | ||
* Path to release, e.g. $HOME/Downloads/crosswalk-14.43.343.25.zip | ||
crosswalk-app platforms List available target platforms | ||
Environment variables | ||
CROSSWALK_APP_TOOLS_CACHE_DIR=<path>: Keep downloaded files in this dir | ||
``` | ||
### Example: Creating and packaging an application | ||
crosswalk-app help Display usage information | ||
To get started, all you need is a web manifest, and an html file. The web manifest holds name and settings for your application. A minimal manifest.json looks like this: | ||
``` | ||
{ | ||
"name": "My first Crosswalk application", | ||
"start_url": "index.html", | ||
"xwalk_package_id": "com.example.foo" | ||
} | ||
``` | ||
crosswalk-app version Display version information | ||
Then add an index.html in the same directory: | ||
``` | ||
<html> | ||
<head> | ||
<title>My first Crosswalk application</title> | ||
</head> | ||
<body>This is my first Crosswalk application</body> | ||
</html> | ||
``` | ||
Options for platform 'android' | ||
For command 'create' | ||
--android-crosswalk Channel name (stable/beta/canary) | ||
or version number (w.x.y.z) | ||
Environment variables for platform 'android' | ||
CROSSWALK_APP_TOOLS_CACHE_DIR Keep downloaded files in this dir | ||
Finally, time to create the apk package: | ||
``` | ||
#### Example: Create App | ||
`crosswalk-app create com.example.foo`: This sets up a skeleton project in directory com.example.foo/, downloads and imports Crosswalk, and puts a sample "hello world" web app under com.example.foo/app/. | ||
crosswalk-pkg <path> | ||
``` | ||
This sets up a skeleton project, downloads and imports Crosswalk, and creates a package using the files above. | ||
#### Example: Build App | ||
`cd com.example.foo` and then `crosswalk-app build` builds packages. The APKs can be found in the current directory when done. | ||
#### Example: Update Crosswalk | ||
`crosswalk-app update stable` updates Crosswalk to the latest version available in the stable channel. | ||
### Next steps and limitations | ||
* Android release packages will have to be signed manually before they are published on Google's Play Store, as that functionality is not yet integrated. See https://developer.android.com/tools/publishing/app-signing.html#signing-manually for details. | ||
* We encourage everyone to use this app-tools and appreciate feedback, as we are looking to improve user friendliness and integration with Crosswalk in the coming releases. | ||
### Additional target platforms | ||
There is forthcoming support for additional target platforms. For iOS packaging, see | ||
https://github.com/crosswalk-project/crosswalk-app-tools-ios, Windows support is planned for the 0.8 release. | ||
### Limitations | ||
* Android release packages will have to be signed manually before they are published on Google's Play Store, as that functionality is not yet integrated. | ||
* This is alpha stage software and under continuous development. We encourage trying it and appreciate feedback, but use in a production environment is not supported at this point in time. | ||
### Run development versions from git | ||
1. Download: `npm install https://github.com/crosswalk-project/crosswalk-app-tools.git` | ||
4. The main script is `crosswalk-app-tools/src/crosswalk-pkg`. Set environment PATH or invoke with directory. | ||
### Run development versions from git | ||
1. Download: `git clone https://github.com/crosswalk-project/crosswalk-app-tools.git` | ||
3. Install dependencies: `cd crosswalk-app-tools`, then `npm install`, and `cd ..` | ||
4. The main script is `crosswalk-app-tools/src/crosswalk-app`. Set environment PATH or invoke with directory. | ||
### License | ||
The license for this project is the Apache License Version 2.0, please refer to the LICENSE-APACHE-V2 included with the package for details. |
@@ -12,5 +12,5 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var IllegalAccessException = require("./util/exceptions").IllegalAccessException; | ||
var InvalidPathException = require("./util/exceptions").InvalidPathException; | ||
var LogfileOutput = require("./LogfileOutput"); | ||
var Manifest = require("./Manifest"); | ||
var OutputIface = require("./OutputIface"); | ||
var OutputTee = require("./OutputTee"); | ||
@@ -26,3 +26,3 @@ var TerminalOutput = require("./TerminalOutput"); | ||
* @param {String} [packageId] Package ID in com.example.foo format, or null | ||
* @throws {InvalidPathException} If packageId not passed and current working dir not a project. | ||
* @throws {Error} If packageId not passed and current working dir not a project. | ||
* @protected | ||
@@ -32,9 +32,13 @@ */ | ||
var output = TerminalOutput.getInstance(); | ||
// cwd must be absolute and exist. | ||
if (!cwd || | ||
Path.resolve(cwd) != Path.normalize(cwd)) { | ||
throw new InvalidPathException("Path not absolute: " + cwd); | ||
output.error("Path not absolute: " + cwd); | ||
throw new Error("Path not absolute: " + cwd); | ||
} | ||
if (!ShellJS.test("-d", cwd)) { | ||
throw new InvalidPathException("Path does not exist: " + cwd); | ||
output.error("Path does not exist: " + cwd); | ||
throw new Error("Path does not exist: " + cwd); | ||
} | ||
@@ -50,3 +54,4 @@ | ||
if (ShellJS.test("-d", rootPath)) { | ||
throw new InvalidPathException("Failed to create project, path already exists: " + rootPath); | ||
output.error("Failed to create project, path already exists: " + rootPath); | ||
throw new Error("Failed to create project, path already exists: " + rootPath); | ||
} | ||
@@ -62,6 +67,7 @@ | ||
// Get packageId from manifest | ||
var manifest = new Manifest(this._output, Path.join(cwd, "app", "manifest.json")); | ||
var manifest = new Manifest(TerminalOutput.getInstance(), Path.join(cwd, "app", "manifest.json")); | ||
this._packageId = manifest.packageId; | ||
if (!this._packageId) { | ||
throw new InvalidPathException("Path does not seem to be a project toplevel: " + cwd); | ||
output.error("Path does not seem to be a project toplevel: " + cwd); | ||
throw new Error("Path does not seem to be a project toplevel: " + cwd); | ||
} | ||
@@ -74,15 +80,20 @@ | ||
if (!ShellJS.test("-d", this._rootPath)) { | ||
throw new InvalidPathException("Failed to load, invalid path: " + this._rootPath); | ||
output.error("Failed to load, invalid path: " + this._rootPath); | ||
throw new Error("Failed to load, invalid path: " + this._rootPath); | ||
} | ||
if (!ShellJS.test("-d", this._appPath)) { | ||
throw new InvalidPathException("Failed to load, invalid path: " + this._appPath); | ||
output.error("Failed to load, invalid path: " + this._appPath); | ||
throw new Error("Failed to load, invalid path: " + this._appPath); | ||
} | ||
if (!ShellJS.test("-d", this._logPath)) { | ||
throw new InvalidPathException("Failed to load, invalid path: " + this._logPath); | ||
output.error("Failed to load, invalid path: " + this._logPath); | ||
throw new Error("Failed to load, invalid path: " + this._logPath); | ||
} | ||
if (!ShellJS.test("-d", this._pkgPath)) { | ||
throw new InvalidPathException("Failed to load, invalid path: " + this._pkgPath); | ||
output.error("Failed to load, invalid path: " + this._pkgPath); | ||
throw new Error("Failed to load, invalid path: " + this._pkgPath); | ||
} | ||
if (!ShellJS.test("-d", this._prjPath)) { | ||
throw new InvalidPathException("Failed to load, invalid path: " + this._prjPath); | ||
output.error("Failed to load, invalid path: " + this._prjPath); | ||
throw new Error("Failed to load, invalid path: " + this._prjPath); | ||
} | ||
@@ -99,5 +110,9 @@ | ||
this._manifest = new Manifest(this._output, Path.join(this._appPath, "manifest.json")); | ||
this.loadManifest(Path.join(this._appPath, "manifest.json")); | ||
} | ||
/** | ||
* Initialize paths. | ||
* @param {String} rootPath Root path inside the project | ||
*/ | ||
function initMembers(rootPath) { | ||
@@ -122,2 +137,16 @@ | ||
/** | ||
* Load web manifest. | ||
* @param {String} path Path to web manifest file | ||
*/ | ||
Application.prototype.loadManifest = | ||
function(path) { | ||
if (!ShellJS.test("-f", path)) { | ||
throw new Error("File not found: ", path); | ||
} | ||
this._manifest = new Manifest(this._output, path); | ||
}; | ||
/** | ||
* Package identifier in reverse host format, i.e. com.example.foo. | ||
@@ -272,3 +301,7 @@ * @member {String} packageId | ||
set: function(output) { | ||
throw new IllegalAccessException("Attempting to write read-only property Application.output"); | ||
if (output instanceof OutputIface) { | ||
this._output = output; | ||
} else { | ||
throw new IllegalAccessException("Application.output must implement OutputIface"); | ||
} | ||
} | ||
@@ -275,0 +308,0 @@ }); |
@@ -8,2 +8,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var Minimist = require("minimist"); | ||
var ShellJS = require("shelljs"); | ||
@@ -38,2 +39,5 @@ /** | ||
"\n" + | ||
" crosswalk-app check [<platforms>] Check host setup\n" + | ||
" Check all platforms if none given\n" + | ||
"\n" + | ||
" crosswalk-app create <package-id> Create project <package-id>\n" + | ||
@@ -46,4 +50,5 @@ " --platforms=<target> Optional, e.g. \"windows\"\n" + | ||
"\n" + | ||
" crosswalk-app update <channel>|<version> Update Crosswalk to latest in named\n" + | ||
" crosswalk-app update [<version>] [<dir>] Update Crosswalk to latest in named\n" + | ||
" channel, or specific version\n" + | ||
" Version is \"stable\" when not given" + | ||
"\n" + | ||
@@ -67,2 +72,4 @@ " crosswalk-app platforms List available target platforms\n" + | ||
switch (cmd) { | ||
case "check": | ||
return cmd; | ||
case "create": | ||
@@ -73,3 +80,3 @@ var packageId = this.createGetPackageId(); | ||
var version = this.updateGetVersion(); | ||
if (version === false) { | ||
if (!version) { | ||
// Error: version could not be parsed. | ||
@@ -114,3 +121,3 @@ return null; | ||
if (["create", "update", "refresh", "build", "platforms"].indexOf(command) > -1) { | ||
if (["check", "create", "update", "refresh", "build", "platforms"].indexOf(command) > -1) { | ||
return command; | ||
@@ -123,2 +130,16 @@ } | ||
/** | ||
* Get platforms to check the host configuration for. | ||
* @returns {String[]} Array of platform IDs or empty array to check all available platforms. | ||
*/ | ||
CommandParser.prototype.checkGetPlatforms = | ||
function() { | ||
// Command goes like this | ||
// node crosswalk-app create platforms* | ||
// So we take everything from the third index | ||
var platforms = this._argv.slice(3); | ||
return platforms; | ||
}; | ||
/** | ||
* Get package name when command is "create". | ||
@@ -148,28 +169,53 @@ * @returns {String} Package name as per Android conventions or null. | ||
var errormsg = "Version must be channel 'stable', 'beta', 'canary', or format ab.cd.ef.gh"; | ||
// argv is filled like this: | ||
// node crosswalk-app update [version] [dir] | ||
// So argv[3] is either version or dir. | ||
if (this._argv.length < 4) { | ||
return null; | ||
return "stable"; | ||
} | ||
var version = this._argv[3]; | ||
// Recognise channel name for version | ||
if (["beta", "canary", "stable"].indexOf(version) > -1) { | ||
if (CommandParser.validateVersion(version, null)) { | ||
return version; | ||
} | ||
var match = version.match("[0-9\\.]*"); | ||
if (match[0] != version) { | ||
this._output.error(errormsg); | ||
return false; | ||
return null; | ||
}; | ||
// FIXME require node 0.12 and use Path.isAbsolute | ||
CommandParser.prototype.isAbsolute = | ||
function(path) { | ||
if (Path.sep === "/" && | ||
path[0] === Path.sep) { | ||
return true; | ||
} | ||
var parts = version.split('.'); | ||
if (parts.length != 4) { | ||
this._output.error(errormsg); | ||
return false; | ||
// Windows | ||
return path.match(/^[a-zA-z]:/) !== null; | ||
}; | ||
/** | ||
* Get dir when command is "update". Defaults to current dir. | ||
*/ | ||
CommandParser.prototype.updateGetDir = | ||
function() { | ||
// argv is filled like this: | ||
// node crosswalk-app update [version] [dir] | ||
// Dir can only be specified if version is given, though, | ||
// no guessing around here. | ||
if (this._argv.length < 5) { | ||
// Dir not given. | ||
return process.cwd(); | ||
} | ||
return version; | ||
var path = this._argv[4]; | ||
if (this.isAbsolute(path)) { | ||
return Path.resolve(Path.normalize(path)); | ||
} | ||
return Path.resolve(Path.normalize(Path.join(process.cwd(), path))); | ||
}; | ||
@@ -277,2 +323,35 @@ | ||
/** | ||
* Validate crosswalk version. | ||
* @param {String} version Version string | ||
* @param {OutputIface} [output] | ||
* @returns {Boolean} true if valid, otherwise false. | ||
*/ | ||
CommandParser.validateVersion = | ||
function(version, output) { | ||
var errormsg = "Version must be channel 'stable', 'beta', 'canary', or format ab.cd.ef.gh"; | ||
// Recognise channel name for version | ||
if (["beta", "canary", "stable"].indexOf(version) > -1) { | ||
return true; | ||
} | ||
var match = version.match("[0-9\\.]*"); | ||
if (match[0] != version) { | ||
if (output) | ||
output.error(errormsg); | ||
return false; | ||
} | ||
var parts = version.split('.'); | ||
if (parts.length != 4) { | ||
if (output) | ||
output.error(errormsg); | ||
return false; | ||
} | ||
return true; | ||
}; | ||
module.exports = CommandParser; |
@@ -15,5 +15,18 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
this._label = label; | ||
this._active = false; | ||
} | ||
/** | ||
* Whether the indicator has already been activated (first update() call) | ||
* @member {Boolean} isActive | ||
* @instance | ||
* @memberOf FiniteProgress | ||
*/ | ||
Object.defineProperty(FiniteProgress.prototype, "isActive", { | ||
get: function() { | ||
return this._active; | ||
} | ||
}); | ||
/** | ||
* Update progress indicator. | ||
@@ -25,2 +38,4 @@ * @param {Number} progress Progress value between 0.0 and 1.0 | ||
this._active = true; | ||
// Clamp | ||
@@ -63,4 +78,7 @@ progress = progress < 0 ? 0 : | ||
this._output.write(" " + message + "\n"); | ||
this._output.endProgress(); | ||
this._active = false; | ||
}; | ||
module.exports = FiniteProgress; |
@@ -15,5 +15,18 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
this._label = label; | ||
this._active = false; | ||
} | ||
/** | ||
* Whether the indicator has already been activated (first update() call) | ||
* @member {Boolean} isActive | ||
* @instance | ||
* @memberOf InfiniteProgress | ||
*/ | ||
Object.defineProperty(InfiniteProgress.prototype, "isActive", { | ||
get: function() { | ||
return this._active; | ||
} | ||
}); | ||
/** | ||
* Update progress indicator. | ||
@@ -25,2 +38,4 @@ * @param {String} tag Tag for current activity | ||
this._active = true; | ||
// Clear line | ||
@@ -54,4 +69,7 @@ this._output.write('\033[2K'); | ||
this._output.write(line); | ||
this._output.endProgress(); | ||
this._active = false; | ||
}; | ||
module.exports = InfiniteProgress; |
@@ -36,6 +36,5 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
LogfileOutput.prototype = Object.create(OutputIface.prototype); | ||
LogfileOutput.prototype.constructor = LogfileOutput; | ||
// FIXME I have no idea why this breaks, something about the singleton maybe? | ||
// LogfileOutput.prototype = OutputIface.prototype; | ||
LogfileOutput.prototype.error = | ||
@@ -42,0 +41,0 @@ function(message) { |
157
src/Main.js
@@ -15,6 +15,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var TerminalOutput = require("./TerminalOutput"); | ||
var util = require("./util/index.js"); | ||
var MAIN_EXIT_CODE_OK = 0; | ||
var MAIN_EXIT_CODE_ERROR = 127; | ||
/** | ||
@@ -42,4 +40,8 @@ * Callback signature for toplevel operations. | ||
} | ||
Main.prototype = Application.prototype; | ||
Main.prototype = Object.create(Application.prototype); | ||
Main.prototype.constructor = Main; | ||
Main.EXIT_CODE_OK = 0; | ||
Main.EXIT_CODE_ERROR = 127; | ||
/* TODO move to android project | ||
@@ -90,2 +92,65 @@ function workingDirectoryIsProject() { | ||
/** | ||
* Have platform implementations check the host setup. | ||
* @param {String[]} platformIds Platforms to check, null or empty array checks all. | ||
* @param {OutputIface} output Output to write to | ||
* @param {Main~mainOperationCb} callback Callback function | ||
* @static | ||
*/ | ||
Main.prototype.check = | ||
function(platformIds, output, callback) { | ||
var mgr = new PlatformsManager(output); | ||
var platforms = []; | ||
if (platformIds && platformIds.length > 0) { | ||
platformIds.forEach(function (platformId) { | ||
var errormsg = null; | ||
var platformInfo = mgr.load(platformId, | ||
function (errormsg_) { | ||
errormsg = errormsg_; | ||
}); | ||
if (platformInfo) { | ||
platforms.push(platformInfo); | ||
} else { | ||
output.error(errormsg); | ||
output.error("Failed to load platform '" + platformId + "'"); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
} | ||
}); | ||
} else { | ||
platforms = mgr.loadAll(); | ||
} | ||
if (!platforms || platforms.length === 0) { | ||
output.error("Failed to load platform modules"); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
} | ||
function checkPlatform(platformInfo, next) { | ||
if (platformInfo.Ctor.check) { | ||
output.info("Checking host setup for target " + platformInfo.platformId); | ||
platformInfo.Ctor.check(output, | ||
function(success) { | ||
next(); | ||
}); | ||
} else { | ||
output.warning("Skipping '" + platformInfo.platformId + "': 'check' not implemented"); | ||
next(); | ||
} | ||
} | ||
util.iterate(platforms, checkPlatform, | ||
function () { | ||
callback(Main.EXIT_CODE_OK); | ||
}); | ||
}; | ||
/** | ||
* Collect arguments | ||
@@ -126,3 +191,8 @@ */ | ||
// for new project. | ||
var platform = extraArgs.platforms; | ||
var platform = null; | ||
if (typeof extraArgs.platforms === "string") { | ||
platform = extraArgs.platforms; | ||
} else if (extraArgs.platforms instanceof Array) { | ||
platform = extraArgs.platforms[0]; | ||
} | ||
if (platform) { | ||
@@ -133,3 +203,3 @@ try { | ||
output.error("Invalid target platform '" + platform + "'"); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -143,3 +213,3 @@ } | ||
output.error("Could not find app template in " + templatePath); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -152,3 +222,3 @@ } | ||
if (!project) { | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -169,6 +239,6 @@ } | ||
output.info("Logfiles at " + this.logPath); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
} else { | ||
callback(MAIN_EXIT_CODE_OK); | ||
callback(Main.EXIT_CODE_OK); | ||
return; | ||
@@ -193,3 +263,3 @@ } | ||
if (!project) { | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -210,6 +280,6 @@ } | ||
output.info("Logfiles at " + this.logPath); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
} else { | ||
callback(MAIN_EXIT_CODE_OK); | ||
callback(Main.EXIT_CODE_OK); | ||
return; | ||
@@ -243,3 +313,3 @@ } | ||
if (!project) { | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -261,6 +331,6 @@ } | ||
output.info("Logfiles at " + this.logPath); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
} else { | ||
callback(MAIN_EXIT_CODE_OK); | ||
callback(Main.EXIT_CODE_OK); | ||
return; | ||
@@ -321,3 +391,3 @@ } | ||
if (Object.keys(platformInfo.envSpec).length > 0) { | ||
output.write("Environment variables for platform '" + platformInfo.platformId + "'\n\n"); | ||
output.write("\nEnvironment variables for platform '" + platformInfo.platformId + "'\n\n"); | ||
for (var env in platformInfo.envSpec) { | ||
@@ -357,2 +427,3 @@ output.write(" " + env + " " + platformInfo.envSpec[env] + "\n"); | ||
var app = new Main(); | ||
var rootDir = null; | ||
@@ -362,3 +433,3 @@ if (process.argv.length < 3) { | ||
app.printHelp(parser, output); | ||
callback(MAIN_EXIT_CODE_OK); | ||
callback(Main.EXIT_CODE_OK); | ||
return; | ||
@@ -371,3 +442,3 @@ } | ||
output.error("Unhandled command '" + process.argv[2] + "'"); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
return; | ||
@@ -378,14 +449,13 @@ } | ||
switch (cmd) { | ||
case "check": | ||
var platforms = parser.checkGetPlatforms(); | ||
app.check(platforms, output, callback); | ||
break; | ||
case "create": | ||
var packageId = parser.createGetPackageId(); | ||
try { | ||
// Chain up the constructor. | ||
Application.call(app, process.cwd(), packageId); | ||
app.create(packageId, extraArgs, callback); | ||
} catch (e) { | ||
output.error("Failed to initialize"); | ||
output.error("Ensure directory '" + packageId + "' does not already exist"); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
} | ||
// Chain up the constructor. | ||
Application.call(app, process.cwd(), packageId); | ||
app.create(packageId, extraArgs, callback); | ||
break; | ||
@@ -395,12 +465,7 @@ | ||
var version = parser.updateGetVersion(); | ||
rootDir = parser.updateGetDir(); | ||
try { | ||
// Chain up the constructor. | ||
Application.call(app, process.cwd(), null); | ||
app.update(version, extraArgs, callback); | ||
} catch (e) { | ||
output.error("Failed to initialize"); | ||
output.error("Ensure to invoke 'crosswalk-app-tools' from a toplevel project directory"); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
} | ||
// Chain up the constructor. | ||
Application.call(app, rootDir, null); | ||
app.update(version, extraArgs, callback); | ||
break; | ||
@@ -410,13 +475,7 @@ | ||
var type = parser.buildGetType(); | ||
var rootDir = parser.buildGetDir(); | ||
rootDir = parser.buildGetDir(); | ||
try { | ||
// Chain up the constructor. | ||
Application.call(app, rootDir, null); | ||
app.build(type, extraArgs, callback); | ||
} catch (e) { | ||
output.error("Failed to initialize"); | ||
output.error("Ensure to invoke 'crosswalk-app-tools' from a toplevel project directory"); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
} | ||
// Chain up the constructor. | ||
Application.call(app, rootDir, null); | ||
app.build(type, extraArgs, callback); | ||
break; | ||
@@ -438,3 +497,3 @@ | ||
output.error("Unhandled command " + cmd); | ||
callback(MAIN_EXIT_CODE_ERROR); | ||
callback(Main.EXIT_CODE_ERROR); | ||
} | ||
@@ -444,1 +503,3 @@ }; | ||
module.exports = new Main(); | ||
module.exports.EXIT_CODE_OK = Main.EXIT_CODE_OK; | ||
module.exports.EXIT_CODE_ERROR = Main.EXIT_CODE_ERROR; |
@@ -6,4 +6,6 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var FS = require("fs"); | ||
var Path = require("path"); | ||
var FormatJson = require("format-json"); | ||
var ShellJS = require("shelljs"); | ||
@@ -17,2 +19,3 @@ var CommandParser = require("./CommandParser"); | ||
* @param {String} path Path to manifest.json | ||
* @constructor | ||
*/ | ||
@@ -57,3 +60,3 @@ function Manifest(output, path) { | ||
output.error("Invalid app version '" + json.xwalk_app_version + "' in the manifest"); | ||
// TODO maybe exception | ||
throw new Error("Invalid app version '" + json.xwalk_app_version + "' in the manifest"); | ||
} | ||
@@ -77,2 +80,4 @@ | ||
var values; | ||
// Display | ||
@@ -82,3 +87,5 @@ this._display = "standalone"; | ||
if (["fullscreen", "standalone"].indexOf(json.display) > -1) { | ||
values = ["fullscreen", "standalone", "minimal-ui", "browser"]; | ||
if (values.indexOf(json.display) > -1) { | ||
// supported mode | ||
@@ -91,2 +98,23 @@ this._display = json.display; | ||
// Orientation | ||
this._orientation = "any"; | ||
if (json.orientation) { | ||
values = [ "any", | ||
"natural", | ||
"landscape", | ||
"portrait", | ||
"portrait-primary", | ||
"portrait-secondary", | ||
"landscape-primary", | ||
"landscape-secondary" ]; | ||
if (values.indexOf(json.orientation) > -1) { | ||
// supported mode | ||
this._orientation = json.orientation; | ||
} else { | ||
output.warning("Unsupported value '" + json.orientation + "' in manifest.json"); | ||
} | ||
} | ||
// Start URL | ||
@@ -106,2 +134,12 @@ // TODO check value | ||
// Command line params | ||
this._commandLine = null; | ||
if (json.xwalk_command_line) { | ||
if (typeof json.xwalk_command_line === "string") { | ||
this._commandLine = json.xwalk_command_line; | ||
} else { | ||
output.warning("Invalid command line '" + json.xwalk_command_line + "'"); | ||
} | ||
} | ||
// Package ID | ||
@@ -115,2 +153,20 @@ if (json.xwalk_package_id && | ||
// Extensions | ||
this._extensions = []; | ||
if (json.xwalk_extensions) { | ||
if (json.xwalk_extensions instanceof Array) { | ||
json.xwalk_extensions.forEach(function (path) { | ||
var absPath = Path.resolve(Path.dirname(this._path), path); | ||
absPath = Path.normalize(absPath); | ||
if (ShellJS.test("-e", absPath)) { | ||
this._extensions.push(absPath); | ||
} else { | ||
output.warning("Skipping extension because dir not found: " + absPath); | ||
} | ||
}.bind(this)); | ||
} else { | ||
output.warning("Invalid extensions " + json.xwalk_extensions); | ||
} | ||
} | ||
// Target platforms | ||
@@ -151,2 +207,29 @@ if (json.xwalk_target_platforms && | ||
// Android permissions | ||
this._androidPermissions = Manifest.ANDROID_DEFAULT_PERMISSIONS.concat([]); // clone array | ||
if (json.xwalk_android_permissions) { | ||
if (json.xwalk_android_permissions instanceof Array) { | ||
// Merge permissions to the default ones. | ||
json.xwalk_android_permissions.forEach(function (permission) { | ||
if (this._androidPermissions.indexOf(permission) < 0) { | ||
this._androidPermissions.push(permission); | ||
} | ||
}.bind(this)); | ||
} else { | ||
output.warning("Invalid android permissions '" + json.xwalk_android_permissions + "'"); | ||
} | ||
} | ||
// Android webp | ||
this._androidWebp = false; | ||
if (json.xwalk_android_webp) { | ||
if (typeof json.xwalk_android_webp === "string") { | ||
// TODO better check | ||
this._androidWebp = json.xwalk_android_webp; | ||
} else { | ||
output.warning("Invalid webp parameters '" + json.xwalk_android_webp + "'"); | ||
} | ||
} | ||
// Windows update ID | ||
@@ -186,12 +269,15 @@ // Optional field, only check if present. | ||
/** | ||
* Create manifest at project creation stage. | ||
* @param {OutputIface} output Output implementation | ||
* @param {String} path Path to manifest.json | ||
* Default permissions needed on android. | ||
*/ | ||
Manifest.ANDROID_DEFAULT_PERMISSIONS = [ "ACCESS_NETWORK_STATE", "ACCESS_WIFI_STATE", "INTERNET" ]; | ||
/** | ||
* Create default manifest data. | ||
* @param {String} packageId Unique package identifier com.example.foo | ||
* @returns {Manifest} Loaded manifest instance. | ||
* @returns {Object} Manifest JSON representation | ||
* @memberOf Manifest | ||
* @static | ||
*/ | ||
Manifest.create = | ||
function(path, packageId) { | ||
Manifest.createDefaultJson = | ||
function(packageId) { | ||
@@ -204,8 +290,2 @@ // Emulate old behaviour of using default backend, | ||
// Default icon | ||
var icon = { | ||
src: "icon.png", | ||
sizes: "72x72" | ||
}; | ||
// Create windows update id | ||
@@ -226,3 +306,3 @@ // Format is: 12345678-1234-1234-1234-111111111111 | ||
var buffer = FormatJson.plain({ | ||
return { | ||
// Standard fields | ||
@@ -232,6 +312,7 @@ "name": packageId, | ||
"display": "standalone", | ||
"icons": [ icon ], | ||
"orientation": "any", | ||
"start_url": "index.html", | ||
// Crosswalk fields | ||
"xwalk_app_version": "1", | ||
"xwalk_app_version": "0.1", | ||
"xwalk_command_line": "", | ||
"xwalk_package_id": packageId, | ||
@@ -242,6 +323,61 @@ "xwalk_target_platforms": platformInfo.platformId, | ||
"xwalk_android_keep_screen_on": false, | ||
// Set external storage by default, needed for shared mode/fallback. | ||
"xwalk_android_permissions": Manifest.ANDROID_DEFAULT_PERMISSIONS, | ||
// Windows fields | ||
"xwalk_windows_update_id": windowsUpdateId, | ||
"xwalk_windows_vendor": "(Vendor)" // optional, placeholder | ||
}); | ||
"xwalk_windows_update_id": windowsUpdateId | ||
}; | ||
}; | ||
/** | ||
* Create manifest at project creation stage. | ||
* @param {String} path Path to manifest.json | ||
* @param {String} packageId Unique package identifier com.example.foo | ||
* @memberOf Manifest | ||
* @static | ||
*/ | ||
Manifest.create = | ||
function(path, packageId) { | ||
var json = Manifest.createDefaultJson(packageId); | ||
// Default icon | ||
var icon = { | ||
src: "icon.png", | ||
sizes: "72x72" | ||
}; | ||
json.icons = [ icon ]; | ||
FS.writeFileSync(path, FormatJson.plain(json)); | ||
}; | ||
/** | ||
* Add missing default fields to manifest. | ||
* @param {String} path Path to manifest.json | ||
* @param {String} packageId Unique package identifier com.example.foo | ||
* @memberOf Manifest | ||
* @static | ||
*/ | ||
Manifest.addDefaults = | ||
function(output, path, packageId) { | ||
var buffer; | ||
var json = {}; | ||
if (ShellJS.test("-f", path)) { | ||
buffer = FS.readFileSync(path, {"encoding": "utf8"}); | ||
json = JSON.parse(buffer); | ||
} else { | ||
output.warning("File not found " + path); | ||
output.warning("Using default manifest.json"); | ||
} | ||
// Just a shallow assignment of missing fields. | ||
var defaultsJson = Manifest.createDefaultJson(packageId); | ||
for (var key in defaultsJson) { | ||
if (!json[key]) { | ||
json[key] = defaultsJson[key]; | ||
} | ||
} | ||
// Write back | ||
buffer = FormatJson.plain(json); | ||
FS.writeFileSync(path, buffer); | ||
@@ -355,2 +491,15 @@ }; | ||
/** | ||
* Orientation | ||
* @member {String} orientation | ||
* @instance | ||
* @memberOf Manifest | ||
* @see https://w3c.github.io/manifest/#orientation-member | ||
*/ | ||
Object.defineProperty(Manifest.prototype, "orientation", { | ||
get: function() { | ||
return this._orientation; | ||
} | ||
}); | ||
/** | ||
* Icons | ||
@@ -382,2 +531,14 @@ * @member {String} icons | ||
/** | ||
* Command line params | ||
* @member {String} commandLine | ||
* @instance | ||
* @memberOf Manifest | ||
*/ | ||
Object.defineProperty(Manifest.prototype, "commandLine", { | ||
get: function() { | ||
return this._commandLine; | ||
} | ||
}); | ||
/** | ||
* Package ID | ||
@@ -419,2 +580,38 @@ * @member {String} packageId | ||
/** | ||
* Android permissions. | ||
* @member {String} androidPermissions | ||
* @instance | ||
* @memberOf Manifest | ||
*/ | ||
Object.defineProperty(Manifest.prototype, "androidPermissions", { | ||
get: function() { | ||
return this._androidPermissions; | ||
} | ||
}); | ||
/** | ||
* Android webp conversion. | ||
* @member {String} androidWebp | ||
* @instance | ||
* @memberOf Manifest | ||
*/ | ||
Object.defineProperty(Manifest.prototype, "androidWebp", { | ||
get: function() { | ||
return this._androidWebp; | ||
} | ||
}); | ||
/** | ||
* Extensions. | ||
* @member {String} extensions | ||
* @instance | ||
* @memberOf Manifest | ||
*/ | ||
Object.defineProperty(Manifest.prototype, "extensions", { | ||
get: function() { | ||
return this._extensions; | ||
} | ||
}); | ||
/** | ||
* Build target platforms for the apps | ||
@@ -421,0 +618,0 @@ * @member {String} targetPlatforms |
@@ -22,3 +22,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
// OutputTee.prototype = OutputIface.prototype; | ||
OutputTee.prototype = Object.create(OutputIface.prototype); | ||
OutputTee.prototype.constructor = OutputTee; | ||
@@ -25,0 +26,0 @@ /** |
@@ -194,3 +194,6 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
ShellJS.mv('-f', packagePath, this.pkgPath); | ||
// ShellJS.mv() gives weird can not link error when moving | ||
// across file systems, so play save and copy/delete. | ||
ShellJS.cp(packagePath, this.pkgPath); | ||
ShellJS.rm("-f", packagePath); | ||
}; | ||
@@ -197,0 +200,0 @@ |
@@ -71,2 +71,14 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
/** | ||
* Module constructor. | ||
* @member {Function} Ctor | ||
* @instance | ||
* @memberOf PlatformInfo | ||
*/ | ||
Object.defineProperty(PlatformInfo.prototype, "Ctor", { | ||
get: function() { | ||
return this._Ctor; | ||
} | ||
}); | ||
/** | ||
* Instantiate platform backend. | ||
@@ -73,0 +85,0 @@ * @param {Application} application Application instance |
@@ -33,2 +33,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
function silentCb(errormsg) {} | ||
var output = this._output; | ||
@@ -41,3 +43,4 @@ | ||
platformInfo = this.load(platformId); | ||
// Silent error callback because we just try loading all. | ||
platformInfo = this.load(platformId, silentCb); | ||
if (platformInfo) { | ||
@@ -61,3 +64,3 @@ break; | ||
/** | ||
* Load default backend. | ||
* Load all installed backends. | ||
* @returns {PlatformInfo} Metadata object for loaded platform. | ||
@@ -68,2 +71,4 @@ */ | ||
function silentCb(errormsg) {} | ||
var output = this._output; | ||
@@ -75,3 +80,4 @@ | ||
platformInfo = this.load(platformId); | ||
// Silent error callback because we just try loading all. | ||
platformInfo = this.load(platformId, silentCb); | ||
if (platformInfo) { | ||
@@ -94,4 +100,3 @@ backends.push(platformInfo); | ||
if (!callback) | ||
callback = function(errormsg) {}; | ||
var output = this._output; | ||
@@ -108,3 +113,7 @@ var platformInfo = null; | ||
callback(e.message); | ||
if (callback) | ||
callback(e.message); | ||
else | ||
output.error(e.message); | ||
return null; | ||
} | ||
@@ -111,0 +120,0 @@ |
@@ -5,2 +5,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var Os = require("os"); | ||
var FiniteProgress = require("./FiniteProgress"); | ||
@@ -24,12 +26,19 @@ var InfiniteProgress = require("./InfiniteProgress"); | ||
this._progress = null; | ||
return TerminalOutput._instance; | ||
} | ||
TerminalOutput.prototype = Object.create(OutputIface.prototype); | ||
TerminalOutput.prototype.constructor = TerminalOutput; | ||
TerminalOutput.prototype = OutputIface.prototype; | ||
TerminalOutput.prototype.error = | ||
function(message) { | ||
if (!_config.getSilentConsole()) | ||
if (!_config.getSilentConsole()) { | ||
if (this._progress && | ||
this._progress.isActive) { | ||
console.log(""); | ||
} | ||
console.error("*** ERROR: " + message); | ||
} | ||
}; | ||
@@ -40,4 +49,9 @@ | ||
if (!_config.getSilentConsole()) | ||
if (!_config.getSilentConsole()) { | ||
if (this._progress && | ||
this._progress.isActive) { | ||
console.log(""); | ||
} | ||
console.error(" ** WARNING: " + message); | ||
} | ||
}; | ||
@@ -48,4 +62,9 @@ | ||
if (!_config.getSilentConsole()) | ||
if (!_config.getSilentConsole()) { | ||
if (this._progress && | ||
this._progress.isActive) { | ||
console.log(""); | ||
} | ||
console.log(" * " + message); | ||
} | ||
}; | ||
@@ -56,4 +75,9 @@ | ||
if (!_config.getSilentConsole()) | ||
if (!_config.getSilentConsole()) { | ||
if (this._progress && | ||
this._progress.isActive) { | ||
console.log(""); | ||
} | ||
console.log('\033[1m' + message + '\033[0m'); | ||
} | ||
}; | ||
@@ -85,4 +109,4 @@ | ||
var indicator = new FiniteProgress(this, label); | ||
return indicator; | ||
this._progress = new FiniteProgress(this, label); | ||
return this._progress; | ||
}; | ||
@@ -106,7 +130,18 @@ | ||
var indicator = new InfiniteProgress(this, label); | ||
return indicator; | ||
this._progress = new InfiniteProgress(this, label); | ||
return this._progress; | ||
}; | ||
/** | ||
* End progress display and switch back to line mode. | ||
* Only to be called from the progress indicator classes. | ||
* @private | ||
*/ | ||
TerminalOutput.prototype.endProgress = | ||
function() { | ||
this._progress = null; | ||
}; | ||
/** | ||
* Retrieve singleton instance. | ||
@@ -113,0 +148,0 @@ * @function getInstance |
@@ -19,3 +19,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
FileCreationFailed.prototype = Error.prototype; | ||
FileCreationFailed.prototype = Object.create(Error.prototype); | ||
FileCreationFailed.prototype.constructor = FileCreationFailed; | ||
@@ -32,3 +33,4 @@ /** | ||
} | ||
IllegalAccessException.prototype = Error.prototype; | ||
IllegalAccessException.prototype = Object.create(Error.prototype); | ||
IllegalAccessException.prototype.constructor = IllegalAccessException; | ||
@@ -45,3 +47,4 @@ /** | ||
} | ||
InvalidPathException.prototype = Error.prototype; | ||
InvalidPathException.prototype = Object.create(Error.prototype); | ||
InvalidPathException.prototype.constructor = InvalidPathException; | ||
@@ -48,0 +51,0 @@ |
@@ -9,2 +9,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var util = { | ||
/** {@link CrosswalkZip} */ | ||
CrosswalkZip: require("./CrosswalkZip"), | ||
/** {@link Downloader} */ | ||
@@ -17,5 +19,26 @@ Downloader: require("./Downloader"), | ||
/** {@link TemplateFile} */ | ||
TemplateFile: require("./TemplateFile") | ||
TemplateFile: require("./TemplateFile"), | ||
/** | ||
* Iterate an array with provisions for asynchronous processing of each item. | ||
* @param {Array} a Array to iterate over | ||
* @param {Function} callback Function(item, next) to be called for each item. | ||
* Parameter next when called continues iteration. | ||
* @param {Function} done Function to be called when done iterating. | ||
*/ | ||
iterate: function(a, callback, done) { | ||
var i = -1; | ||
function next() { | ||
i++; | ||
if (i < a.length) { | ||
callback(a[i], next); | ||
} else if (done) { | ||
done(); | ||
} | ||
} | ||
next(); | ||
} | ||
}; | ||
module.exports = util; |
@@ -11,3 +11,4 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
TestPlatform.prototype = PlatformBase.prototype; | ||
TestPlatform.prototype = Object.create(PlatformBase.prototype); | ||
TestPlatform.prototype.constructor = TestPlatform; | ||
@@ -14,0 +15,0 @@ TestPlatform.prototype.create = |
@@ -16,2 +16,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
var OutputTee = require("../src/OutputTee"); | ||
var TerminalOutput = require("../src/TerminalOutput"); | ||
var Util = require("../test-util/Util.js"); | ||
@@ -91,3 +92,3 @@ | ||
test.equal(manifest instanceof Manifest, true); | ||
test.equal(manifest.appVersion, "1"); | ||
test.equal(manifest.appVersion, "0.1"); | ||
Util.deleteTmpApplication(application); | ||
@@ -156,2 +157,15 @@ test.done(); | ||
setOutput2: function(test) { | ||
test.expect(1); | ||
var application = Util.createTmpApplication("com.example.foo"); | ||
application.output = TerminalOutput.getInstance(); | ||
// If we get here without exception, all is good. | ||
test.equal(true, true); | ||
Util.deleteTmpApplication(application); | ||
test.done(); | ||
}, | ||
logging: function(test) { | ||
@@ -158,0 +172,0 @@ |
@@ -123,3 +123,3 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
// No version, good test | ||
// No version, good test, default to "stable" | ||
var argv0 = ["node", "foo", "update"]; | ||
@@ -134,3 +134,3 @@ var cp0 = new CommandParser(_output, argv0); | ||
var version0 = cp0.updateGetVersion(); | ||
test.equal(version0, null); | ||
test.equal(version0, "stable"); | ||
@@ -156,3 +156,3 @@ // Good test | ||
var version2 = cp2.updateGetVersion(); | ||
test.equal(version2, false); | ||
test.equal(version2, null); | ||
@@ -162,2 +162,39 @@ test.done(); | ||
updateGetDir: function(test) { | ||
test.expect(8); | ||
// build inside project | ||
var argv0 = ["node", "foo", "update"]; | ||
var cp0 = new CommandParser(_output, argv0); | ||
test.equal(cp0.getCommand(), "update"); | ||
var cmd0 = cp0.getCommand(); | ||
test.equal(cmd0, argv0[2]); | ||
var version0 = cp0.updateGetVersion(); | ||
test.equal(version0, "stable"); | ||
var dir0 = cp0.updateGetDir(); | ||
test.equal(dir0, process.cwd()); | ||
// command-line with dir specified | ||
var argv1 = ["node", "foo", "update", "beta", "com.example.foo"]; | ||
var cp1 = new CommandParser(_output, argv1); | ||
test.equal(cp1.getCommand(), "update"); | ||
var cmd1 = cp1.getCommand(); | ||
test.equal(cmd1, argv1[2]); | ||
var version1 = cp1.updateGetVersion(); | ||
test.equal(version1, argv1[3]); | ||
var dir1 = cp1.updateGetDir(); | ||
test.equal(dir1, Path.join(process.cwd(), argv1[4])); | ||
test.done(); | ||
}, | ||
buildGetType: function(test) { | ||
@@ -164,0 +201,0 @@ |
@@ -34,3 +34,36 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
}, | ||
interruptedProgress: function(test) { | ||
test.expect(10); | ||
var progress = 0; | ||
var indicator = _output.createFiniteProgress("foo"); | ||
var interval = setInterval(callback, 200); | ||
function callback() { | ||
indicator.update(progress); | ||
progress += 0.1; | ||
if (progress > 0.19 && progress < 0.21) | ||
_output.info("info " + progress); | ||
else if (progress > 0.39 && progress < 0.41) | ||
_output.warning("warning " + progress); | ||
else if (progress > 0.59 && progress < 0.61) | ||
_output.error("error " + progress); | ||
else if (progress > 0.79 && progress < 0.81) | ||
_output.highlight("highlight " + progress); | ||
if (progress > 1) { | ||
clearInterval(interval); | ||
indicator.done("done"); | ||
test.done(); | ||
return; | ||
} | ||
test.equal(true, true); | ||
} | ||
} | ||
}; |
@@ -34,3 +34,43 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
} | ||
}, | ||
interruptedProgress: function(test) { | ||
test.expect(5); | ||
var tags = ["foo", "bar", "baz", "maman", "quux"]; | ||
var index = 0; | ||
var indicator = _output.createInfiniteProgress("foo"); | ||
var interval = setInterval(callback, 300); | ||
function callback() { | ||
indicator.update(tags[index]); | ||
switch (index) { | ||
case 0: | ||
_output.info("info " + index); | ||
break; | ||
case 1: | ||
_output.warning("warning " + index); | ||
break; | ||
case 2: | ||
_output.error("error " + index); | ||
break; | ||
case 3: | ||
_output.highlight("highlight " + index); | ||
break; | ||
} | ||
index++; | ||
test.equal(true, true); | ||
if (index >= tags.length) { | ||
indicator.done("done"); | ||
clearInterval(interval); | ||
test.done(); | ||
return; | ||
} | ||
} | ||
} | ||
}; |
@@ -34,6 +34,48 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
check0: function(test) { | ||
test.expect(1); | ||
// Good test. | ||
// Run check without platforms | ||
var tmpdir = Util.createTmpDir(); | ||
ShellJS.pushd(tmpdir); | ||
var app = require("../src/Main"); | ||
app.check([], TerminalOutput.getInstance(), function(errno) { | ||
test.equal(errno, 0); | ||
ShellJS.popd(); | ||
ShellJS.rm("-rf", tmpdir); | ||
test.done(); | ||
}); | ||
}, | ||
check1: function(test) { | ||
test.expect(1); | ||
// Good test. | ||
// Run check "android" | ||
var tmpdir = Util.createTmpDir(); | ||
ShellJS.pushd(tmpdir); | ||
var app = require("../src/Main"); | ||
app.check(["android"], TerminalOutput.getInstance(), function(errno) { | ||
test.equal(errno, 0); | ||
ShellJS.popd(); | ||
ShellJS.rm("-rf", tmpdir); | ||
test.done(); | ||
}); | ||
}, | ||
create: function(test) { | ||
test.expect(1); | ||
// Good test. | ||
@@ -40,0 +82,0 @@ var tmpdir = Util.createTmpDir(); |
@@ -65,25 +65,55 @@ // Copyright © 2014 Intel Corporation. All rights reserved. | ||
version = "2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
version = "2.2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "2.2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
version = "2.2.2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "2.2.2."; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
version = "3333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "3333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
version = "333.333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "333.333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
version = "33.333.333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
test.equal(false, version == consumeManifest(path).appVersion); | ||
try { | ||
version = "33.333.333"; | ||
path = produceManifest({"xwalk_app_version": version}); | ||
consumeManifest(path); | ||
} catch (e) { | ||
// Invalid version, just make sure it throws. | ||
test.equal(true, true); | ||
} | ||
@@ -135,5 +165,5 @@ test.done(); | ||
test.expect(3); | ||
test.expect(6); | ||
// Default to "false" | ||
// Default to "standalone" | ||
var path = produceManifest(); | ||
@@ -143,6 +173,9 @@ var manifest = consumeManifest(path); | ||
// Test reading "fullscreen" | ||
path = produceManifest({"display": "fullscreen"}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.display, "fullscreen"); | ||
// Test reading various values | ||
var values = ["fullscreen", "standalone", "minimal-ui", "browser"]; | ||
values.forEach(function (value) { | ||
path = produceManifest({"display": value}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.display, value); | ||
}); | ||
@@ -157,2 +190,35 @@ // Test reading bogus value "foo", default to "standalone" | ||
orientation: function(test) { | ||
test.expect(10); | ||
// Default to "any" | ||
var path = produceManifest(); | ||
var manifest = consumeManifest(path); | ||
test.equal(manifest.orientation, "any"); | ||
// Test reading various values | ||
var values = [ "any", | ||
"natural", | ||
"landscape", | ||
"portrait", | ||
"portrait-primary", | ||
"portrait-secondary", | ||
"landscape-primary", | ||
"landscape-secondary" | ||
]; | ||
values.forEach(function (value) { | ||
path = produceManifest({"orientation": value}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.orientation, value); | ||
}); | ||
// Test reading bogus value "foo", default to "any" | ||
path = produceManifest({"orientation": "foo"}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.orientation, "any"); | ||
test.done(); | ||
}, | ||
icons: function(test) { | ||
@@ -199,2 +265,19 @@ | ||
commandLine: function(test) { | ||
test.expect(2); | ||
// default | ||
var path = produceManifest(); | ||
var manifest = consumeManifest(path); | ||
test.equal(manifest.commandLine, null); | ||
// custom value | ||
path = produceManifest({"xwalk_command_line": "foo"}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.commandLine, "foo"); | ||
test.done(); | ||
}, | ||
packageId: function(test) { | ||
@@ -225,3 +308,3 @@ | ||
test.expect(1); | ||
test.expect(2); | ||
@@ -234,2 +317,9 @@ var path = produceManifest({"xwalk_target_platforms": "android"}); | ||
// write | ||
manifest.targetPlatforms = "windows"; | ||
// read back | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.targetPlatforms, "windows"); | ||
test.done(); | ||
@@ -272,2 +362,36 @@ }, | ||
androidPermissions: function(test) { | ||
test.expect(2); | ||
// Default, check INTERNET is set (also needs networks etc) | ||
var path = produceManifest(); | ||
var manifest = consumeManifest(path); | ||
test.equal(manifest.androidPermissions.indexOf("INTERNET") >= 0, true); | ||
// Test adding CAMERA | ||
path = produceManifest({"xwalk_android_permissions": [ "CAMERA" ]}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.androidPermissions.indexOf("CAMERA") >= 0, true); | ||
test.done(); | ||
}, | ||
androidWebp: function(test) { | ||
test.expect(2); | ||
// Default to "false" | ||
var path = produceManifest(); | ||
var manifest = consumeManifest(path); | ||
test.equal(!manifest.androidWebp, true); | ||
// Test reading "true" | ||
path = produceManifest({"xwalk_android_webp": "80 80 100"}); | ||
manifest = consumeManifest(path); | ||
test.equal(manifest.androidWebp, "80 80 100"); | ||
test.done(); | ||
}, | ||
windowsUpdateId: function(test) { | ||
@@ -289,10 +413,14 @@ | ||
test.expect(1); | ||
test.expect(2); | ||
var path3 = produceManifest(); | ||
var m3 = consumeManifest(path3); | ||
var path1 = produceManifest(); | ||
var m1 = consumeManifest(path1); | ||
test.equal(m1.windowsVendor, null); | ||
test.equal(typeof m3.windowsVendor === "string", true); | ||
var path2 = produceManifest({"xwalk_windows_vendor": "foo"}); | ||
var m2 = consumeManifest(path2); | ||
test.equal(m2.windowsVendor, "foo"); | ||
test.done(); | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
401777
79
8269
99
12
34
6
+ Addednode-uuid@~1.4.3
+ Addedreaddir@~0.0.13
+ Addedxmlbuilder@~2.6.4
+ Addedlodash@3.10.1(transitive)
+ Addednode-uuid@1.4.8(transitive)
+ Addedq@1.0.1(transitive)
+ Addedreaddir@0.0.13(transitive)
+ Addedxmlbuilder@2.6.5(transitive)