nativescript
Version:
Command-line interface for building NativeScript projects
715 lines ⢠26.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.stripComments = stripComments;
exports.doesCurrentNpmCommandMatch = doesCurrentNpmCommandMatch;
exports.someWithRegExps = someWithRegExps;
exports.getCurrentNpmCommandArgv = getCurrentNpmCommandArgv;
exports.isInstallingNativeScriptGlobally = isInstallingNativeScriptGlobally;
exports.createRegExp = createRegExp;
exports.regExpEscape = regExpEscape;
exports.getShortPluginName = getShortPluginName;
exports.executeActionByChunks = executeActionByChunks;
exports.deferPromise = deferPromise;
exports.settlePromises = settlePromises;
exports.getPropertyName = getPropertyName;
exports.quoteString = quoteString;
exports.createGUID = createGUID;
exports.stringReplaceAll = stringReplaceAll;
exports.isRequestSuccessful = isRequestSuccessful;
exports.isResponseRedirect = isResponseRedirect;
exports.formatListOfNames = formatListOfNames;
exports.getRelativeToRootPath = getRelativeToRootPath;
exports.setIsInteractive = setIsInteractive;
exports.isInteractive = isInteractive;
exports.toBoolean = toBoolean;
exports.block = block;
exports.isNumberWithoutExponent = isNumberWithoutExponent;
exports.fromWindowsRelativePathToUnix = fromWindowsRelativePathToUnix;
exports.isNullOrWhitespace = isNullOrWhitespace;
exports.getCurrentEpochTime = getCurrentEpochTime;
exports.sleep = sleep;
exports.createTable = createTable;
exports.getMessageWithBorders = getMessageWithBorders;
exports.remove = remove;
exports.trimSymbol = trimSymbol;
exports.parseJson = parseJson;
exports.getFuturesResults = getFuturesResults;
exports.appendZeroesToVersion = appendZeroesToVersion;
exports.decorateMethod = decorateMethod;
exports.hook = hook;
exports.isPromise = isPromise;
exports.attachAwaitDetach = attachAwaitDetach;
exports.connectEventually = connectEventually;
exports.getHash = getHash;
exports.connectEventuallyUntilTimeout = connectEventuallyUntilTimeout;
exports.getProjectFilesConfig = getProjectFilesConfig;
exports.getPidFromiOSSimulatorLogs = getPidFromiOSSimulatorLogs;
exports.getValueFromNestedObject = getValueFromNestedObject;
exports.getWinRegPropertyValue = getWinRegPropertyValue;
exports.stringify = stringify;
exports.getFixedLengthDateString = getFixedLengthDateString;
exports.getFormattedDateComponent = getFormattedDateComponent;
exports.getFormattedMilliseconds = getFormattedMilliseconds;
exports.annotate = annotate;
exports.hasValidAndroidSigning = hasValidAndroidSigning;
const uuid_1 = require("uuid");
const os_1 = require("os");
const constants_1 = require("./constants");
const crypto = require("crypto");
const _ = require("lodash");
const Table = require("cli-table3");
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
function stripComments(content) {
const newContent = content.replace(STRIP_COMMENTS, "");
return newContent;
}
function doesCurrentNpmCommandMatch(patterns) {
const currentNpmCommandArgv = getCurrentNpmCommandArgv();
let result = false;
if (currentNpmCommandArgv.length) {
result = someWithRegExps(currentNpmCommandArgv, patterns);
}
return result;
}
/**
* Equivalent of lodash's some, but instead of lambda, just pass array of Regular Expressions.
* If any of them matches any of the given elements, true is returned.
* @param {string[]} array Elements to be checked.
* @param {RegExp[]} patterns Regular expressions to be tested
* @returns {boolean} True in case any element of the array matches any of the patterns. False otherwise.
*/
function someWithRegExps(array, patterns) {
return _.some(array, (item) => _.some(patterns, (pattern) => !!item.match(pattern)));
}
function getCurrentNpmCommandArgv() {
let result = [];
if (process.env && process.env.npm_config_argv) {
try {
const npmConfigArgv = JSON.parse(process.env.npm_config_argv);
result = npmConfigArgv.original || [];
}
catch (error) {
// ignore
}
}
return result;
}
function isInstallingNativeScriptGlobally() {
return (isInstallingNativeScriptGloballyWithNpm() ||
isInstallingNativeScriptGloballyWithYarn());
}
function isInstallingNativeScriptGloballyWithNpm() {
const isInstallCommand = doesCurrentNpmCommandMatch([/^install$/, /^i$/]);
const isGlobalCommand = doesCurrentNpmCommandMatch([/^--global$/, /^-g$/]);
const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
/^nativescript(@.*)?$/,
/nativescript-(.*)\.tgz?$/,
]);
return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
function isInstallingNativeScriptGloballyWithYarn() {
// yarn populates the same env used by npm - npm_config_argv, so check it for yarn specific command
const isInstallCommand = doesCurrentNpmCommandMatch([/^add$/]);
const isGlobalCommand = doesCurrentNpmCommandMatch([/^global$/]);
const hasNativeScriptPackage = doesCurrentNpmCommandMatch([
/^nativescript(@.*)?$/,
/nativescript-(.*)\.tgz?$/,
]);
return isInstallCommand && isGlobalCommand && hasNativeScriptPackage;
}
/**
* Creates regular expression from input string.
* The method replaces all occurences of RegExp special symbols in the input string with \<symbol>.
* @param {string} input The string from which a regular expression should be created.
* @param {string} opts RegExp options, for example "gm" - global and multiline.
* @returns {RegExp} The regular expression created from the input string.
*/
function createRegExp(input, opts) {
if (!input || !_.isString(input)) {
throw new Error("Input must be a string.");
}
const escapedSource = regExpEscape(input);
return new RegExp(escapedSource, opts);
}
/**
* Escapes all special symbols used in regex.
* @param {string} input The string in which to replace the special regexp symbols.
* @returns {string} A string in which all regex symbols are escaped.
*/
function regExpEscape(input) {
// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function getShortPluginName(pluginName) {
return sanitizePluginName(pluginName).replace(/[\-]/g, "_");
}
function sanitizePluginName(pluginName) {
// avoid long plugin names, exclude the npm module scope (@scope/nativescript-plugin) from the android plugin name
return pluginName.split("/").pop();
}
async function executeActionByChunks(initialData, chunkSize, elementAction) {
let arrayToChunk;
let action;
if (_.isArray(initialData)) {
arrayToChunk = initialData;
action = (element) => elementAction(element, initialData.indexOf(element));
}
else {
arrayToChunk = _.keys(initialData);
action = (key) => elementAction(initialData[key], key);
}
const chunks = _.chunk(arrayToChunk, chunkSize);
for (const chunk of chunks) {
await Promise.all(_.map(chunk, (element) => action(element)));
}
}
function deferPromise() {
let resolve;
let reject;
let isResolved = false;
let isRejected = false;
let promise;
let result;
promise = new Promise((innerResolve, innerReject) => {
resolve = (value) => {
isResolved = true;
result = value;
return innerResolve(value);
};
reject = (reason) => {
isRejected = true;
return innerReject(reason);
};
});
return {
promise,
resolve,
reject,
isResolved: () => isResolved,
isRejected: () => isRejected,
isPending: () => !isResolved && !isRejected,
getResult: () => result,
};
}
/**
* Executes all promises and does not stop in case any of them throws.
* Returns the results of all promises in array when all are successfully resolved.
* In case any of the promises is rejected, rejects the resulted promise with all accumulated errors.
* @param {Promise<T>[]} promises Promises to be resolved.
* @returns {Promise<T[]>} New promise which will be resolved with the results of all promises.
*/
function settlePromises(promises) {
return new Promise((resolve, reject) => {
let settledPromisesCount = 0;
const results = [];
const errors = [];
const length = promises.length;
if (!promises.length) {
resolve(null);
}
_.forEach(promises, (currentPromise, index) => {
currentPromise
.then((result) => {
results[index] = result;
})
.catch((err) => {
// Accumulate all errors.
errors.push(err);
})
.then(() => {
settledPromisesCount++;
if (settledPromisesCount === length) {
errors.length
? reject(new Error(`Multiple errors were thrown:${os_1.EOL}${errors
.map((e) => e.message || e)
.join(os_1.EOL)}`))
: resolve(results);
}
})
.catch();
});
});
}
function getPropertyName(func) {
if (func) {
const match = func
.toString()
.match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
if (match) {
return (match[1] || match[2]).trim();
}
}
return null;
}
function bashQuote(s) {
if (s[0] === "'" && s[s.length - 1] === "'") {
return s;
}
// replace ' with '"'"' and wrap in ''
return "'" + s.replace(/'/g, "'\"'\"'") + "'";
}
function cmdQuote(s) {
if (s[0] === '"' && s[s.length - 1] === '"') {
return s;
}
// replace " with \" and wrap in ""
return '"' + s.replace(/"/g, '\\"') + '"';
}
function quoteString(s) {
if (!s) {
return s;
}
return (0, os_1.platform)() === "win32" ? cmdQuote(s) : bashQuote(s);
}
function createGUID(useBraces) {
let output;
useBraces = useBraces === undefined ? true : useBraces;
if (useBraces) {
output = "{" + (0, uuid_1.v4)() + "}";
}
else {
output = (0, uuid_1.v4)();
}
return output;
}
function stringReplaceAll(inputString, find, replace) {
return inputString.split(find).join(replace);
}
function isRequestSuccessful(request) {
return request.statusCode >= 200 && request.statusCode < 300;
}
function isResponseRedirect(response) {
return _.includes([301, 302, 303, 307, 308], response.statusCode);
}
function formatListOfNames(names, conjunction) {
conjunction = conjunction === undefined ? "or" : conjunction;
if (names.length <= 1) {
return names[0];
}
else {
return (_.initial(names).join(", ") +
" " +
conjunction +
" " +
names[names.length - 1]);
}
}
function getRelativeToRootPath(rootPath, filePath) {
const relativeToRootPath = filePath.substr(rootPath.length);
return relativeToRootPath;
}
let customIsInteractive;
function setIsInteractive(override) {
customIsInteractive = override;
}
function isInteractive() {
if (customIsInteractive) {
return customIsInteractive();
}
const result = isRunningInTTY() && !isCIEnvironment();
return result;
}
/**
* Checks if current process is running in Text Terminal (TTY)
*/
function isRunningInTTY() {
return (process.stdout &&
process.stdout.isTTY &&
process.stdin &&
process.stdin.isTTY);
}
function isCIEnvironment() {
// The following CI environments set their own environment variables that we respect:
// travis: "CI",
// circleCI: "CI",
// jenkins: "JENKINS_HOME"
return !!(process.env && (process.env.CI || process.env.JENKINS_HOME));
}
function toBoolean(str) {
return !!(str && str.toString && str.toString().toLowerCase() === "true");
}
function block(operation) {
if (isInteractive()) {
process.stdin.setRawMode(false);
}
operation();
if (isInteractive()) {
process.stdin.setRawMode(true);
}
}
function isNumberWithoutExponent(n) {
const parsedNum = parseFloat(n);
return (!isNaN(parsedNum) &&
isFinite(n) &&
n.toString &&
n.toString() === parsedNum.toString());
}
function fromWindowsRelativePathToUnix(windowsRelativePath) {
return windowsRelativePath.replace(/\\/g, "/");
}
function isNullOrWhitespace(input) {
if (!input && input !== false) {
return true;
}
return _.isString(input) && input.replace(/\s/gi, "").length < 1;
}
function getCurrentEpochTime() {
const dateTime = new Date();
return dateTime.getTime();
}
async function sleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(async () => resolve(), ms);
});
}
function createTable(headers, data) {
const table = new Table({
head: headers,
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
});
_.forEach(data, (row) => table.push(row));
return table;
}
function getMessageWithBorders(message, spanLength = 3) {
if (!message) {
return "";
}
const longestRowLength = message.split("\n").sort((a, b) => {
return b.length - a.length;
})[0].length;
let border = "*".repeat(longestRowLength + 2 * spanLength); // * 2 for both sides
if (border.length % 2 === 0) {
border += "*"; // the * should always be an odd number in order to get * in each edge (we will remove the even *s below)
}
border = border.replace(/\*\*/g, "* "); // ***** => * * * in order to have similar padding to the side borders
const formatRow = function (row) {
return (_.padEnd("*", spanLength) +
_.padEnd(row, border.length - 2 * spanLength) +
_.padStart("*", spanLength) +
os_1.EOL);
};
const emptyRow = formatRow("");
const messageWithBorders = [];
messageWithBorders.push(os_1.EOL, border + os_1.EOL, emptyRow, ...message.split("\n").map((row) => formatRow(row.trim())), emptyRow, border + os_1.EOL, os_1.EOL);
return messageWithBorders.join("");
}
function remove(array, predicate, numberOfElements) {
numberOfElements = numberOfElements || 1;
const index = _.findIndex(array, predicate);
if (index === -1) {
return new Array();
}
return array.splice(index, numberOfElements);
}
function trimSymbol(str, symbol) {
while (str.charAt(0) === symbol) {
str = str.substr(1);
}
while (str.charAt(str.length - 1) === symbol) {
str = str.substr(0, str.length - 1);
}
return str;
}
function parseJson(data) {
// Replace BOM from the header of the file if it exists
return JSON.parse(data.replace(/^\uFEFF/, ""));
}
// TODO: Use generic for predicatе predicate: (element: T|T[]) when TypeScript support this.
async function getFuturesResults(promises, predicate) {
const results = await Promise.all(promises);
return _(results).filter(predicate).flatten().value();
}
/**
* Appends zeroes to a version string until it reaches a specified length.
* @param {string} version The version on which to append zeroes.
* @param requiredVersionLength The required length of the version string.
* @returns {string} Appended version string. In case input is null, undefined or empty string, it is returned immediately without appending anything.
*/
function appendZeroesToVersion(version, requiredVersionLength) {
if (version) {
const zeroesToAppend = requiredVersionLength - version.split(".").length;
for (let index = 0; index < zeroesToAppend; index++) {
version += ".0";
}
}
return version;
}
function decorateMethod(before, after) {
return (target, propertyKey, descriptor) => {
const sink = descriptor.value;
descriptor.value = async function (...args) {
let newMethods = null;
if (before) {
newMethods = await before(sink, this, args);
}
let hasBeenReplaced = false;
let result;
if (newMethods && newMethods.length) {
const replacementMethods = _.filter(newMethods, (f) => _.isFunction(f));
if (replacementMethods.length > 0) {
hasBeenReplaced = true;
const chainedReplacementMethod = _.reduce(replacementMethods, (prev, next) => next.bind(next, args, prev), sink.bind(this));
result = chainedReplacementMethod();
}
}
if (!hasBeenReplaced) {
result = sink.apply(this, args);
}
if (after) {
return await after(sink, this, result, args);
}
return result;
};
};
}
function hook(commandName) {
function getHooksService(self) {
let hooksService = self.$hooksService;
if (!hooksService) {
const injector = self.$injector;
if (!injector) {
throw Error("Type with hooks needs to have either $hooksService or $injector injected.");
}
hooksService = injector.resolve("hooksService");
}
return hooksService;
}
function prepareArguments(method, args, hooksService) {
annotate(method);
const argHash = {};
for (let i = 0; i < method.$inject.args.length; ++i) {
argHash[method.$inject.args[i]] = args[i];
}
argHash.$arguments = args;
const result = {};
result[hooksService.hookArgsName] = argHash;
return result;
}
return decorateMethod(async (method, self, args) => {
const hooksService = getHooksService(self);
return hooksService.executeBeforeHooks(commandName, prepareArguments(method, args, hooksService));
}, async (method, self, resultPromise, args) => {
const result = await resultPromise;
const hooksService = getHooksService(self);
await hooksService.executeAfterHooks(commandName, prepareArguments(method, args, hooksService));
return Promise.resolve(result);
});
}
function isPromise(candidateFuture) {
return !!(candidateFuture && typeof candidateFuture.then === "function");
}
async function attachAwaitDetach(eventName, eventEmitter, eventHandler, operation) {
eventEmitter.on(eventName, eventHandler);
try {
await operation;
}
finally {
eventEmitter.removeListener(eventName, eventHandler);
}
}
async function connectEventually(factory, handler) {
async function tryConnect() {
const tryConnectAfterTimeout = setTimeout.bind(undefined, tryConnect, 1000);
const socket = await factory();
socket.on("connect", () => {
socket.removeListener("error", tryConnectAfterTimeout);
handler(socket);
});
socket.on("error", tryConnectAfterTimeout);
}
await tryConnect();
}
function getHash(str, options) {
return crypto
.createHash((options && options.algorithm) || "sha256")
.update(str)
.digest((options && options.encoding) || "hex");
}
async function connectEventuallyUntilTimeout(factory, timeout) {
return new Promise(async (resolve, reject) => {
let lastKnownError;
let isResolved = false;
const connectionTimer = setTimeout(function () {
if (!isResolved) {
isResolved = true;
reject(lastKnownError || new Error(`Unable to connect for ${timeout}ms`));
}
}, timeout);
async function tryConnect() {
const tryConnectAfterTimeout = (error) => {
if (isResolved) {
return;
}
lastKnownError = error;
setTimeout(tryConnect, 1000);
};
try {
const socket = await factory();
socket.on("connect", () => {
socket.removeListener("error", tryConnectAfterTimeout);
isResolved = true;
clearTimeout(connectionTimer);
resolve(socket);
});
socket.on("error", tryConnectAfterTimeout);
}
catch (e) {
lastKnownError = e;
tryConnectAfterTimeout(e);
}
}
await tryConnect();
});
}
function getProjectFilesConfig(opts) {
const projectFilesConfig = {
configuration: opts.isReleaseBuild
? constants_1.Configurations.Release.toLowerCase()
: constants_1.Configurations.Debug.toLowerCase(),
};
return projectFilesConfig;
}
/**
* Tries to find the process id (PID) of the specified application identifier.
* This is specific implementation for iOS Simulator, where the running applications are real processes.
* Their PIDs are printed in a specific format in the the logs, once the application is started.
* @param {string} applicationIdentifier Application Identifier of the app for which we try to get the PID.
* @param {string} logLine Line that may contain the PID of the process.
* @returns {string} The PID of the searched application identifier in case it's found in the current line, null otherwise.
*/
function getPidFromiOSSimulatorLogs(applicationIdentifier, logLine) {
if (logLine) {
const pidRegExp = new RegExp(`${applicationIdentifier}:\\s?(\\d+)`);
const pidMatch = logLine.match(pidRegExp);
return pidMatch ? pidMatch[1] : null;
}
return null;
}
function getValueFromNestedObject(obj, key) {
function _getValueRecursive(_obj, _key) {
if (_.has(_obj, _key)) {
return [_obj];
}
const res = [];
_.forEach(_obj, (v, k) => {
if (typeof v === "object" &&
typeof k === "string" &&
!_.startsWith(k, "$") &&
!_.endsWith(k.toLowerCase(), "service") &&
(v = _getValueRecursive(v, _key)).length) {
res.push.apply(res, v);
}
});
return res;
}
return _.head(_getValueRecursive(obj, key));
}
function getWinRegPropertyValue(key, propertyName) {
return new Promise((resolve, reject) => {
const Winreg = require("winreg");
const regKey = new Winreg({
hive: Winreg.HKLM,
key: key,
});
regKey.get(propertyName, (err, value) => {
if (err) {
reject(err);
}
else {
resolve(value);
}
});
});
}
function stringify(value, replacer, space) {
return JSON.stringify(value, replacer, space || 2);
}
//2019-01-07 18:29:50.745
function getFixedLengthDateString() {
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = getFormattedDateComponent(currentDate.getMonth() + 1);
const day = getFormattedDateComponent(currentDate.getDate());
const hour = getFormattedDateComponent(currentDate.getHours());
const minutes = getFormattedDateComponent(currentDate.getMinutes());
const seconds = getFormattedDateComponent(currentDate.getSeconds());
const milliseconds = getFormattedMilliseconds(currentDate);
return `${[year, month, day].join("-")} ${[hour, minutes, seconds].join(":")}.${milliseconds}`;
}
function getFormattedDateComponent(component) {
const stringComponent = component.toString();
return stringComponent.length === 1 ? `0${stringComponent}` : stringComponent;
}
function getFormattedMilliseconds(date) {
let milliseconds = date.getMilliseconds().toString();
while (milliseconds.length < 3) {
milliseconds = `0${milliseconds}`;
}
return milliseconds;
}
//--- begin part copied from AngularJS
//The MIT License
//
//Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
const CLASS_NAME = /class\s+([A-Z].+?)(?:\s+.*?)?\{/;
const CONSTRUCTOR_ARGS = /constructor\s*([^\(]*)\(\s*([^\)]*)\)/m;
const FN_NAME_AND_ARGS = /^(?:function)?\s*([^\(]*)\(\s*([^\)]*)\)\s*(=>)?\s*[{_]/m;
const FN_ARG_SPLIT = /,/;
const FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
function annotate(fn) {
let $inject, fnText, argDecl;
if (typeof fn === "function") {
if (!($inject = fn.$inject) || $inject.name !== fn.name) {
$inject = { args: [], name: "" };
fnText = fn.toString().replace(STRIP_COMMENTS, "");
let nameMatch = fnText.match(CLASS_NAME);
if (nameMatch) {
argDecl = fnText.match(CONSTRUCTOR_ARGS);
}
else {
nameMatch = argDecl = fnText.match(FN_NAME_AND_ARGS);
}
$inject.name = nameMatch && nameMatch[1];
if (argDecl && fnText.length) {
argDecl[2].split(FN_ARG_SPLIT).forEach((arg) => {
arg.replace(FN_ARG, (all, underscore, name) => $inject.args.push(name));
});
}
fn.$inject = $inject;
}
}
return $inject;
}
/**
* Returns true if all Android signing options are provided, false otherwise.
* @param {IAndroidSigningData} signingData The signing data to be validated.
* @return {void}
*/
function hasValidAndroidSigning(signingData) {
const isValid = signingData &&
signingData.keyStorePath &&
signingData.keyStorePassword &&
signingData.keyStoreAlias &&
signingData.keyStoreAliasPassword;
return !!isValid;
}
//--- end part copied from AngularJS
//# sourceMappingURL=helpers.js.map
;