nativescript 8.5.1 → 8.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/base-package-manager.js +3 -1
- package/lib/commands/clean.js +200 -4
- package/lib/common/declarations.d.ts +7 -0
- package/lib/common/dispatchers.js +1 -1
- package/lib/common/file-system.js +25 -0
- package/lib/common/prompter.js +16 -8
- package/lib/common/services/commands-service.js +3 -1
- package/lib/common/services/hooks-service.js +11 -1
- package/lib/declarations.d.ts +3 -1
- package/lib/definitions/project.d.ts +39 -2
- package/lib/definitions/prompter.d.ts +8 -1
- package/lib/definitions/terminal-spinner-service.d.ts +5 -102
- package/lib/options.js +2 -0
- package/lib/services/initialize-service.js +7 -2
- package/lib/services/project-cleanup-service.js +46 -23
- package/lib/services/terminal-spinner-service.js +1 -1
- package/package.json +5 -6
|
@@ -88,7 +88,9 @@ class BasePackageManager {
|
|
|
88
88
|
getFlagsString(config, asArray) {
|
|
89
89
|
const array = [];
|
|
90
90
|
for (const flag in config) {
|
|
91
|
-
if (flag === "global" &&
|
|
91
|
+
if (flag === "global" &&
|
|
92
|
+
this.packageManager !== "yarn" &&
|
|
93
|
+
this.packageManager !== "yarn2") {
|
|
92
94
|
array.push(`--${flag}`);
|
|
93
95
|
array.push(`${config[flag]}`);
|
|
94
96
|
}
|
package/lib/commands/clean.js
CHANGED
|
@@ -12,16 +12,67 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.CleanCommand = void 0;
|
|
13
13
|
const yok_1 = require("../common/yok");
|
|
14
14
|
const constants = require("../constants");
|
|
15
|
+
const os = require("os");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const promises_1 = require("fs/promises");
|
|
18
|
+
const helpers_1 = require("../common/helpers");
|
|
19
|
+
const CLIPath = (0, path_1.resolve)(__dirname, "..", "..", "bin", "nativescript.js");
|
|
20
|
+
function bytesToHumanReadable(bytes) {
|
|
21
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
22
|
+
let unit = 0;
|
|
23
|
+
while (bytes >= 1024) {
|
|
24
|
+
bytes /= 1024;
|
|
25
|
+
unit++;
|
|
26
|
+
}
|
|
27
|
+
return `${bytes.toFixed(2)} ${units[unit]}`;
|
|
28
|
+
}
|
|
29
|
+
function promiseMap(values, mapper, concurrency = 10) {
|
|
30
|
+
let index = 0;
|
|
31
|
+
let pending = 0;
|
|
32
|
+
let done = false;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const next = () => {
|
|
35
|
+
done = index === values.length;
|
|
36
|
+
if (done && pending === 0) {
|
|
37
|
+
return resolve();
|
|
38
|
+
}
|
|
39
|
+
while (pending < concurrency && index < values.length) {
|
|
40
|
+
const value = values[index++];
|
|
41
|
+
pending++;
|
|
42
|
+
mapper(value)
|
|
43
|
+
.then(() => {
|
|
44
|
+
pending--;
|
|
45
|
+
next();
|
|
46
|
+
})
|
|
47
|
+
.catch();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
next();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
15
53
|
class CleanCommand {
|
|
16
|
-
constructor($projectCleanupService, $projectConfigService, $terminalSpinnerService) {
|
|
54
|
+
constructor($projectCleanupService, $projectConfigService, $terminalSpinnerService, $projectService, $prompter, $logger, $options, $childProcess) {
|
|
17
55
|
this.$projectCleanupService = $projectCleanupService;
|
|
18
56
|
this.$projectConfigService = $projectConfigService;
|
|
19
57
|
this.$terminalSpinnerService = $terminalSpinnerService;
|
|
58
|
+
this.$projectService = $projectService;
|
|
59
|
+
this.$prompter = $prompter;
|
|
60
|
+
this.$logger = $logger;
|
|
61
|
+
this.$options = $options;
|
|
62
|
+
this.$childProcess = $childProcess;
|
|
20
63
|
this.allowedParameters = [];
|
|
21
64
|
}
|
|
22
65
|
execute(args) {
|
|
66
|
+
var _a, _b;
|
|
23
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
const
|
|
68
|
+
const isDryRun = (_a = this.$options.dryRun) !== null && _a !== void 0 ? _a : false;
|
|
69
|
+
const isJSON = (_b = this.$options.json) !== null && _b !== void 0 ? _b : false;
|
|
70
|
+
const spinner = this.$terminalSpinnerService.createSpinner({
|
|
71
|
+
isSilent: isJSON,
|
|
72
|
+
});
|
|
73
|
+
if (!this.$projectService.isValidNativeScriptProject()) {
|
|
74
|
+
return this.cleanMultipleProjects(spinner);
|
|
75
|
+
}
|
|
25
76
|
spinner.start("Cleaning project...\n");
|
|
26
77
|
let pathsToClean = [
|
|
27
78
|
constants.HOOKS_DIR_NAME,
|
|
@@ -41,8 +92,20 @@ class CleanCommand {
|
|
|
41
92
|
}
|
|
42
93
|
catch (err) {
|
|
43
94
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
95
|
+
const res = yield this.$projectCleanupService.clean(pathsToClean, {
|
|
96
|
+
dryRun: isDryRun,
|
|
97
|
+
silent: isJSON,
|
|
98
|
+
stats: isJSON,
|
|
99
|
+
});
|
|
100
|
+
if (res.stats && isJSON) {
|
|
101
|
+
console.log(JSON.stringify({
|
|
102
|
+
ok: res.ok,
|
|
103
|
+
dryRun: isDryRun,
|
|
104
|
+
stats: Object.fromEntries(res.stats.entries()),
|
|
105
|
+
}, null, 2));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (res.ok) {
|
|
46
109
|
spinner.succeed("Project successfully cleaned.");
|
|
47
110
|
}
|
|
48
111
|
else {
|
|
@@ -50,6 +113,139 @@ class CleanCommand {
|
|
|
50
113
|
}
|
|
51
114
|
});
|
|
52
115
|
}
|
|
116
|
+
cleanMultipleProjects(spinner) {
|
|
117
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
118
|
+
if (!(0, helpers_1.isInteractive)() || this.$options.json) {
|
|
119
|
+
this.$logger.warn("No project found in the current directory.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const shouldScan = yield this.$prompter.confirm("No project found in the current directory. Would you like to scan for all projects in sub-directories instead?");
|
|
123
|
+
if (!shouldScan) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
spinner.start("Scanning for projects... Please wait.");
|
|
127
|
+
const paths = yield this.getNSProjectPathsInDirectory();
|
|
128
|
+
spinner.succeed(`Found ${paths.length} projects.`);
|
|
129
|
+
let computed = 0;
|
|
130
|
+
const updateProgress = () => {
|
|
131
|
+
const current = `${computed}/${paths.length}`.grey;
|
|
132
|
+
spinner.start(`Gathering cleanable sizes. This may take a while... ${current}`);
|
|
133
|
+
};
|
|
134
|
+
updateProgress();
|
|
135
|
+
const projects = new Map();
|
|
136
|
+
yield promiseMap(paths, (p) => {
|
|
137
|
+
return this.$childProcess
|
|
138
|
+
.exec(`node ${CLIPath} clean --dry-run --json --disable-analytics`, {
|
|
139
|
+
cwd: p,
|
|
140
|
+
})
|
|
141
|
+
.then((res) => {
|
|
142
|
+
const paths = JSON.parse(res).stats;
|
|
143
|
+
return Object.values(paths).reduce((a, b) => a + b, 0);
|
|
144
|
+
})
|
|
145
|
+
.catch((err) => {
|
|
146
|
+
this.$logger.trace("Failed to get project size for %s, Error is:", p, err);
|
|
147
|
+
return -1;
|
|
148
|
+
})
|
|
149
|
+
.then((size) => {
|
|
150
|
+
if (size > 0 || size === -1) {
|
|
151
|
+
projects.set(p, size);
|
|
152
|
+
}
|
|
153
|
+
computed++;
|
|
154
|
+
updateProgress();
|
|
155
|
+
});
|
|
156
|
+
}, os.cpus().length);
|
|
157
|
+
spinner.clear();
|
|
158
|
+
spinner.stop();
|
|
159
|
+
this.$logger.clearScreen();
|
|
160
|
+
const totalSize = Array.from(projects.values())
|
|
161
|
+
.filter((s) => s > 0)
|
|
162
|
+
.reduce((a, b) => a + b, 0);
|
|
163
|
+
const pathsToClean = yield this.$prompter.promptForChoice(`Found ${projects.size} cleanable project(s) with a total size of: ${bytesToHumanReadable(totalSize).green}. Select projects to clean`, Array.from(projects.keys()).map((p) => {
|
|
164
|
+
const size = projects.get(p);
|
|
165
|
+
let description;
|
|
166
|
+
if (size === -1) {
|
|
167
|
+
description = " - could not get size";
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
description = ` - ${bytesToHumanReadable(size)}`;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
title: `${p}${description.grey}`,
|
|
174
|
+
value: p,
|
|
175
|
+
};
|
|
176
|
+
}), true, {
|
|
177
|
+
optionsPerPage: process.stdout.rows - 6,
|
|
178
|
+
});
|
|
179
|
+
this.$logger.clearScreen();
|
|
180
|
+
spinner.warn(`This will run "${`ns clean`.yellow}" in all the selected projects and ${"delete files from your system".red.bold}!`);
|
|
181
|
+
spinner.warn(`This action cannot be undone!`);
|
|
182
|
+
let confirmed = yield this.$prompter.confirm("Are you sure you want to clean the selected projects?");
|
|
183
|
+
if (!confirmed) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
spinner.info("Cleaning... This might take a while...");
|
|
187
|
+
let totalSizeCleaned = 0;
|
|
188
|
+
for (let i = 0; i < pathsToClean.length; i++) {
|
|
189
|
+
const currentPath = pathsToClean[i];
|
|
190
|
+
spinner.start(`Cleaning ${currentPath.cyan}... ${i + 1}/${pathsToClean.length}`);
|
|
191
|
+
const ok = yield this.$childProcess
|
|
192
|
+
.exec(`node ${CLIPath} clean ${this.$options.dryRun ? "--dry-run" : ""} --json --disable-analytics`, {
|
|
193
|
+
cwd: currentPath,
|
|
194
|
+
})
|
|
195
|
+
.then((res) => {
|
|
196
|
+
const cleanupRes = JSON.parse(res);
|
|
197
|
+
return cleanupRes.ok;
|
|
198
|
+
})
|
|
199
|
+
.catch((err) => {
|
|
200
|
+
this.$logger.trace('Failed to clean project "%s"', currentPath, err);
|
|
201
|
+
return false;
|
|
202
|
+
});
|
|
203
|
+
if (ok) {
|
|
204
|
+
const cleanedSize = projects.get(currentPath);
|
|
205
|
+
const cleanedSizeStr = `- ${bytesToHumanReadable(cleanedSize)}`.grey;
|
|
206
|
+
spinner.succeed(`Cleaned ${currentPath.cyan} ${cleanedSizeStr}`);
|
|
207
|
+
totalSizeCleaned += cleanedSize;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
spinner.fail(`Failed to clean ${currentPath.cyan} - skipped`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
spinner.clear();
|
|
214
|
+
spinner.stop();
|
|
215
|
+
spinner.succeed(`Done! We've just freed up ${bytesToHumanReadable(totalSizeCleaned).green}! Woohoo! 🎉`);
|
|
216
|
+
if (this.$options.dryRun) {
|
|
217
|
+
spinner.info('Note: the "--dry-run" flag was used, so no files were actually deleted.');
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
getNSProjectPathsInDirectory(dir = process.cwd()) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
let nsDirs = [];
|
|
224
|
+
const getFiles = (dir) => __awaiter(this, void 0, void 0, function* () {
|
|
225
|
+
if (dir.includes("node_modules")) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const dirents = yield (0, promises_1.readdir)(dir, { withFileTypes: true }).catch((err) => {
|
|
229
|
+
this.$logger.trace('Failed to read directory "%s". Error is:', dir, err);
|
|
230
|
+
return [];
|
|
231
|
+
});
|
|
232
|
+
const hasNSConfig = dirents.some((ent) => ent.name.includes("nativescript.config.ts") ||
|
|
233
|
+
ent.name.includes("nativescript.config.js"));
|
|
234
|
+
if (hasNSConfig) {
|
|
235
|
+
nsDirs.push(dir);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
yield Promise.all(dirents.map((dirent) => {
|
|
239
|
+
const res = (0, path_1.resolve)(dir, dirent.name);
|
|
240
|
+
if (dirent.isDirectory()) {
|
|
241
|
+
return getFiles(res);
|
|
242
|
+
}
|
|
243
|
+
}));
|
|
244
|
+
});
|
|
245
|
+
yield getFiles(dir);
|
|
246
|
+
return nsDirs;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
53
249
|
}
|
|
54
250
|
exports.CleanCommand = CleanCommand;
|
|
55
251
|
yok_1.injector.registerCommand("clean", CleanCommand);
|
|
@@ -338,6 +338,13 @@ interface IFileSystem {
|
|
|
338
338
|
*/
|
|
339
339
|
getFileSize(path: string): number;
|
|
340
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Returns the size of specified path (recurses into all sub-directories if the path is a directory).
|
|
343
|
+
* @param {string} path Path to file or directory.
|
|
344
|
+
* @returns {number} File size in bytes.
|
|
345
|
+
*/
|
|
346
|
+
getSize(path: string): number;
|
|
347
|
+
|
|
341
348
|
/**
|
|
342
349
|
* Change file timestamps of the file referenced by the supplied path.
|
|
343
350
|
* @param {string} path File path
|
|
@@ -40,7 +40,7 @@ class CommandDispatcher {
|
|
|
40
40
|
if (this.$options.version) {
|
|
41
41
|
return this.printVersion();
|
|
42
42
|
}
|
|
43
|
-
if (this.$logger.getLevel() === "TRACE") {
|
|
43
|
+
if (this.$logger.getLevel() === "TRACE" && !this.$options.json) {
|
|
44
44
|
this.$logger.trace("Collecting system information...");
|
|
45
45
|
const sysInfo = yield this.$sysInfo.getSysInfo({
|
|
46
46
|
pathToNativeScriptCliPackageJson: path.join(__dirname, "..", "..", "package.json"),
|
|
@@ -147,6 +147,31 @@ let FileSystem = FileSystem_1 = class FileSystem {
|
|
|
147
147
|
const stat = this.getFsStats(path);
|
|
148
148
|
return stat.size;
|
|
149
149
|
}
|
|
150
|
+
getSize(path) {
|
|
151
|
+
const dirSize = (dir, paths = new Map(), root = true) => {
|
|
152
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
153
|
+
files.map((file) => {
|
|
154
|
+
const path = (0, path_1.join)(dir, file.name);
|
|
155
|
+
if (file.isDirectory()) {
|
|
156
|
+
dirSize(path, paths, false);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (file.isFile()) {
|
|
160
|
+
const { size } = fs.statSync(path);
|
|
161
|
+
paths.set(path, size);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
if (root) {
|
|
165
|
+
return Array.from(paths.values()).reduce((sum, current) => sum + current, 0);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
try {
|
|
169
|
+
return dirSize(path);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
150
175
|
futureFromEvent(eventEmitter, event) {
|
|
151
176
|
return __awaiter(this, void 0, void 0, function* () {
|
|
152
177
|
return new Promise((resolve, reject) => {
|
package/lib/common/prompter.js
CHANGED
|
@@ -84,16 +84,24 @@ class Prompter {
|
|
|
84
84
|
return result.inputString;
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
promptForChoice(promptMessage, choices) {
|
|
87
|
+
promptForChoice(promptMessage, choices, multiple = false, options = {}) {
|
|
88
88
|
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
-
const schema = {
|
|
90
|
-
message: promptMessage,
|
|
91
|
-
type: "select",
|
|
92
|
-
name: "userAnswer",
|
|
93
|
-
choices,
|
|
94
|
-
};
|
|
89
|
+
const schema = Object.assign({ message: promptMessage, type: multiple ? "multiselect" : "select", name: "userAnswer", choices }, options);
|
|
95
90
|
const result = yield this.get([schema]);
|
|
96
|
-
|
|
91
|
+
const extractValue = (choice) => {
|
|
92
|
+
if (typeof choice === "object") {
|
|
93
|
+
return choice.value || choice;
|
|
94
|
+
}
|
|
95
|
+
if (typeof choice === "string") {
|
|
96
|
+
return choice;
|
|
97
|
+
}
|
|
98
|
+
if (typeof choice === "number") {
|
|
99
|
+
return choices[choice];
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return multiple
|
|
103
|
+
? result.userAnswer.map(extractValue)
|
|
104
|
+
: extractValue(result.userAnswer);
|
|
97
105
|
});
|
|
98
106
|
}
|
|
99
107
|
promptForDetailedChoice(promptMessage, choices) {
|
|
@@ -53,7 +53,9 @@ class CommandsService {
|
|
|
53
53
|
this.commands.push({ commandName, commandArguments });
|
|
54
54
|
const command = this.$injector.resolveCommand(commandName);
|
|
55
55
|
if (command) {
|
|
56
|
-
if (!this.$staticConfig.disableAnalytics &&
|
|
56
|
+
if (!this.$staticConfig.disableAnalytics &&
|
|
57
|
+
!command.disableAnalytics &&
|
|
58
|
+
!this.$options.disableAnalytics) {
|
|
57
59
|
const analyticsService = this.$injector.resolve("analyticsService");
|
|
58
60
|
yield analyticsService.checkConsent();
|
|
59
61
|
const beautifiedCommandName = this.beautifyCommandName(commandName).replace(/\|/g, " ");
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
2
8
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
9
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
10
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -16,6 +22,7 @@ const _ = require("lodash");
|
|
|
16
22
|
const helpers_1 = require("../helpers");
|
|
17
23
|
const constants_1 = require("../../constants");
|
|
18
24
|
const yok_1 = require("../yok");
|
|
25
|
+
const decorators_1 = require("../decorators");
|
|
19
26
|
class Hook {
|
|
20
27
|
constructor(name, fullPath) {
|
|
21
28
|
this.name = name;
|
|
@@ -298,6 +305,9 @@ class HooksService {
|
|
|
298
305
|
return invalidArguments;
|
|
299
306
|
}
|
|
300
307
|
}
|
|
301
|
-
exports.HooksService = HooksService;
|
|
302
308
|
HooksService.HOOKS_DIRECTORY_NAME = "hooks";
|
|
309
|
+
__decorate([
|
|
310
|
+
(0, decorators_1.cache)()
|
|
311
|
+
], HooksService.prototype, "initialize", null);
|
|
312
|
+
exports.HooksService = HooksService;
|
|
303
313
|
yok_1.injector.register("hooksService", HooksService);
|
package/lib/declarations.d.ts
CHANGED
|
@@ -642,6 +642,8 @@ interface IOptions
|
|
|
642
642
|
skipRefresh: boolean;
|
|
643
643
|
file: string;
|
|
644
644
|
analyticsClient: string;
|
|
645
|
+
analyticsLogFile: string;
|
|
646
|
+
disableAnalytics: boolean;
|
|
645
647
|
force: boolean;
|
|
646
648
|
sdk: string;
|
|
647
649
|
template: string;
|
|
@@ -684,13 +686,13 @@ interface IOptions
|
|
|
684
686
|
background: string;
|
|
685
687
|
hmr: boolean;
|
|
686
688
|
link: boolean;
|
|
687
|
-
analyticsLogFile: string;
|
|
688
689
|
performance: Object;
|
|
689
690
|
cleanupLogFile: string;
|
|
690
691
|
appleApplicationSpecificPassword: string;
|
|
691
692
|
appleSessionBase64: string;
|
|
692
693
|
markingMode: boolean;
|
|
693
694
|
git: boolean;
|
|
695
|
+
dryRun: boolean;
|
|
694
696
|
}
|
|
695
697
|
|
|
696
698
|
interface IEnvOptions {
|
|
@@ -315,13 +315,50 @@ interface IProjectCleanupService {
|
|
|
315
315
|
* Clean multiple paths
|
|
316
316
|
* @param {string[]} pathsToClean
|
|
317
317
|
*/
|
|
318
|
-
clean(
|
|
318
|
+
clean(
|
|
319
|
+
pathsToClean: string[],
|
|
320
|
+
options?: IProjectCleanupOptions
|
|
321
|
+
): Promise<IProjectCleanupResult>;
|
|
319
322
|
|
|
320
323
|
/**
|
|
321
324
|
* Clean a single path
|
|
322
325
|
* @param {string} pathToClean
|
|
323
326
|
*/
|
|
324
|
-
cleanPath(
|
|
327
|
+
cleanPath(
|
|
328
|
+
pathToClean: string,
|
|
329
|
+
options?: IProjectCleanupOptions
|
|
330
|
+
): Promise<IProjectCleanupResult>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
interface IProjectCleanupOptions {
|
|
334
|
+
/**
|
|
335
|
+
* When set, the cleanup service will calculate the size of the cleaned up files.
|
|
336
|
+
*/
|
|
337
|
+
stats?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* When set, the cleanup service will not delete any files.
|
|
340
|
+
*/
|
|
341
|
+
dryRun?: boolean;
|
|
342
|
+
/**
|
|
343
|
+
* When set, the cleanup service will not print any messages to the console.
|
|
344
|
+
*/
|
|
345
|
+
silent?: boolean;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
interface IProjectCleanupResult {
|
|
349
|
+
ok: boolean;
|
|
350
|
+
/**
|
|
351
|
+
* When stats is set to true, this will contain a map of the cleaned up paths and their size.
|
|
352
|
+
*/
|
|
353
|
+
stats?: Map<string, number>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface IProjectPathCleanupResult {
|
|
357
|
+
ok: boolean;
|
|
358
|
+
/**
|
|
359
|
+
* When stats is set to true, this will contain the size of the cleaned path in bytes.
|
|
360
|
+
*/
|
|
361
|
+
size?: number;
|
|
325
362
|
}
|
|
326
363
|
|
|
327
364
|
interface IBackup {
|
|
@@ -10,7 +10,14 @@ declare global {
|
|
|
10
10
|
get(schemas: IPrompterQuestion[]): Promise<any>;
|
|
11
11
|
getPassword(prompt: string, options?: IAllowEmpty): Promise<string>;
|
|
12
12
|
getString(prompt: string, options?: IPrompterOptions): Promise<string>;
|
|
13
|
-
promptForChoice(
|
|
13
|
+
promptForChoice(
|
|
14
|
+
promptMessage: string,
|
|
15
|
+
choices:
|
|
16
|
+
| string[]
|
|
17
|
+
| { title: string; description?: string; value?: string }[],
|
|
18
|
+
multiple: boolean = false,
|
|
19
|
+
options: any = {}
|
|
20
|
+
): Promise<string>;
|
|
14
21
|
promptForDetailedChoice(
|
|
15
22
|
promptMessage: string,
|
|
16
23
|
choices: { key: string; description: string }[]
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import type { Options, Ora } from "ora";
|
|
2
|
+
|
|
3
|
+
type ITerminalSpinnerOptions = Options;
|
|
4
|
+
type ITerminalSpinner = Ora;
|
|
5
|
+
|
|
1
6
|
interface ITerminalSpinnerService {
|
|
2
7
|
createSpinner(spinnerOptions?: ITerminalSpinnerOptions): ITerminalSpinner;
|
|
3
8
|
execute<T>(
|
|
@@ -5,105 +10,3 @@ interface ITerminalSpinnerService {
|
|
|
5
10
|
action: () => Promise<T>
|
|
6
11
|
): Promise<T>;
|
|
7
12
|
}
|
|
8
|
-
|
|
9
|
-
type SpinnerName =
|
|
10
|
-
| "dots"
|
|
11
|
-
| "dots2"
|
|
12
|
-
| "dots3"
|
|
13
|
-
| "dots4"
|
|
14
|
-
| "dots5"
|
|
15
|
-
| "dots6"
|
|
16
|
-
| "dots7"
|
|
17
|
-
| "dots8"
|
|
18
|
-
| "dots9"
|
|
19
|
-
| "dots10"
|
|
20
|
-
| "dots11"
|
|
21
|
-
| "dots12"
|
|
22
|
-
| "line"
|
|
23
|
-
| "line2"
|
|
24
|
-
| "pipe"
|
|
25
|
-
| "simpleDots"
|
|
26
|
-
| "simpleDotsScrolling"
|
|
27
|
-
| "star"
|
|
28
|
-
| "star2"
|
|
29
|
-
| "flip"
|
|
30
|
-
| "hamburger"
|
|
31
|
-
| "growVertical"
|
|
32
|
-
| "growHorizontal"
|
|
33
|
-
| "balloon"
|
|
34
|
-
| "balloon2"
|
|
35
|
-
| "noise"
|
|
36
|
-
| "bounce"
|
|
37
|
-
| "boxBounce"
|
|
38
|
-
| "boxBounce2"
|
|
39
|
-
| "triangle"
|
|
40
|
-
| "arc"
|
|
41
|
-
| "circle"
|
|
42
|
-
| "squareCorners"
|
|
43
|
-
| "circleQuarters"
|
|
44
|
-
| "circleHalves"
|
|
45
|
-
| "squish"
|
|
46
|
-
| "toggle"
|
|
47
|
-
| "toggle2"
|
|
48
|
-
| "toggle3"
|
|
49
|
-
| "toggle4"
|
|
50
|
-
| "toggle5"
|
|
51
|
-
| "toggle6"
|
|
52
|
-
| "toggle7"
|
|
53
|
-
| "toggle8"
|
|
54
|
-
| "toggle9"
|
|
55
|
-
| "toggle10"
|
|
56
|
-
| "toggle11"
|
|
57
|
-
| "toggle12"
|
|
58
|
-
| "toggle13"
|
|
59
|
-
| "arrow"
|
|
60
|
-
| "arrow2"
|
|
61
|
-
| "arrow3"
|
|
62
|
-
| "bouncingBar"
|
|
63
|
-
| "bouncingBall"
|
|
64
|
-
| "smiley"
|
|
65
|
-
| "monkey"
|
|
66
|
-
| "hearts"
|
|
67
|
-
| "clock"
|
|
68
|
-
| "earth"
|
|
69
|
-
| "moon"
|
|
70
|
-
| "runner"
|
|
71
|
-
| "pong"
|
|
72
|
-
| "shark"
|
|
73
|
-
| "dqpb";
|
|
74
|
-
|
|
75
|
-
interface Spinner {
|
|
76
|
-
interval?: number;
|
|
77
|
-
frames: string[];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface ITerminalSpinner {
|
|
81
|
-
text: string;
|
|
82
|
-
start(text?: string): ITerminalSpinner;
|
|
83
|
-
stop(): ITerminalSpinner;
|
|
84
|
-
succeed(text?: string): ITerminalSpinner;
|
|
85
|
-
fail(text?: string): ITerminalSpinner;
|
|
86
|
-
warn(text?: string): ITerminalSpinner;
|
|
87
|
-
info(text?: string): ITerminalSpinner;
|
|
88
|
-
clear(): ITerminalSpinner;
|
|
89
|
-
render(): ITerminalSpinner;
|
|
90
|
-
frame(): ITerminalSpinner;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface ITerminalSpinnerOptions {
|
|
94
|
-
text?: string;
|
|
95
|
-
spinner?: SpinnerName | Spinner;
|
|
96
|
-
color?:
|
|
97
|
-
| "black"
|
|
98
|
-
| "red"
|
|
99
|
-
| "green"
|
|
100
|
-
| "yellow"
|
|
101
|
-
| "blue"
|
|
102
|
-
| "magenta"
|
|
103
|
-
| "cyan"
|
|
104
|
-
| "white"
|
|
105
|
-
| "gray";
|
|
106
|
-
interval?: number;
|
|
107
|
-
stream?: NodeJS.WritableStream;
|
|
108
|
-
enabled?: boolean;
|
|
109
|
-
}
|
package/lib/options.js
CHANGED
|
@@ -177,6 +177,7 @@ class Options {
|
|
|
177
177
|
default: { type: "boolean", hasSensitiveValue: false },
|
|
178
178
|
count: { type: "number", hasSensitiveValue: false },
|
|
179
179
|
analyticsLogFile: { type: "string", hasSensitiveValue: true },
|
|
180
|
+
disableAnalytics: { type: "boolean", hasSensitiveValue: false },
|
|
180
181
|
cleanupLogFile: { type: "string", hasSensitiveValue: true },
|
|
181
182
|
hooks: {
|
|
182
183
|
type: "boolean",
|
|
@@ -205,6 +206,7 @@ class Options {
|
|
|
205
206
|
hasSensitiveValue: false,
|
|
206
207
|
default: true,
|
|
207
208
|
},
|
|
209
|
+
dryRun: { type: "boolean", hasSensitiveValue: false },
|
|
208
210
|
};
|
|
209
211
|
}
|
|
210
212
|
get optionNames() {
|
|
@@ -22,11 +22,11 @@ class InitializeService {
|
|
|
22
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
23
|
initOpts = initOpts || {};
|
|
24
24
|
const $logger = this.$injector.resolve("logger");
|
|
25
|
+
const $options = this.$injector.resolve("options");
|
|
25
26
|
if (initOpts.loggerOptions) {
|
|
26
27
|
$logger.initialize(initOpts.loggerOptions);
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
29
|
-
const $options = this.$injector.resolve("options");
|
|
30
30
|
const loggerLevel = $options.log &&
|
|
31
31
|
constants_1.LoggerLevel[$options.log.toUpperCase()];
|
|
32
32
|
$logger.initializeCliLogger({ level: loggerLevel });
|
|
@@ -42,7 +42,12 @@ class InitializeService {
|
|
|
42
42
|
initOpts.extensibilityOptions.pathToExtensions;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
if ($options.json) {
|
|
46
|
+
$logger.trace("Skipping system warnings for --json commands");
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
yield this.showWarnings($logger);
|
|
50
|
+
}
|
|
46
51
|
});
|
|
47
52
|
}
|
|
48
53
|
showWarnings($logger) {
|
|
@@ -19,58 +19,81 @@ class ProjectCleanupService {
|
|
|
19
19
|
this.$projectHelper = $projectHelper;
|
|
20
20
|
this.$terminalSpinnerService = $terminalSpinnerService;
|
|
21
21
|
}
|
|
22
|
-
clean(pathsToClean) {
|
|
22
|
+
clean(pathsToClean, options) {
|
|
23
23
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
this.spinner = this.$terminalSpinnerService.createSpinner(
|
|
24
|
+
this.spinner = this.$terminalSpinnerService.createSpinner({
|
|
25
|
+
isSilent: options.silent,
|
|
26
|
+
});
|
|
27
|
+
let stats = options.stats ? new Map() : false;
|
|
25
28
|
let success = true;
|
|
26
29
|
for (const pathToClean of pathsToClean) {
|
|
27
|
-
const
|
|
30
|
+
const cleanRes = yield this.cleanPath(pathToClean, options).catch((error) => {
|
|
28
31
|
this.$logger.trace(`Encountered error while cleaning. Error is: ${error.message}.`, error);
|
|
29
|
-
return false;
|
|
32
|
+
return { ok: false };
|
|
30
33
|
});
|
|
31
|
-
|
|
34
|
+
if (stats && "size" in cleanRes) {
|
|
35
|
+
stats.set(pathToClean, cleanRes.size);
|
|
36
|
+
}
|
|
37
|
+
success = success && cleanRes.ok;
|
|
38
|
+
}
|
|
39
|
+
if (!(options === null || options === void 0 ? void 0 : options.silent)) {
|
|
40
|
+
console.log();
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
if (stats) {
|
|
43
|
+
return { ok: success, stats };
|
|
44
|
+
}
|
|
45
|
+
return { ok: success };
|
|
35
46
|
});
|
|
36
47
|
}
|
|
37
|
-
cleanPath(pathToClean) {
|
|
48
|
+
cleanPath(pathToClean, options) {
|
|
49
|
+
var _a;
|
|
38
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
const dryRun = (_a = options === null || options === void 0 ? void 0 : options.dryRun) !== null && _a !== void 0 ? _a : false;
|
|
52
|
+
const logPrefix = dryRun ? "(dry run) ".grey : "";
|
|
39
53
|
this.spinner.clear();
|
|
40
|
-
let success = true;
|
|
41
54
|
let fileType;
|
|
42
55
|
if (!pathToClean || pathToClean.trim().length === 0) {
|
|
43
|
-
this.$logger.trace(
|
|
44
|
-
return
|
|
56
|
+
this.$logger.trace(`${logPrefix}cleanPath called with no pathToClean.`);
|
|
57
|
+
return { ok: true };
|
|
45
58
|
}
|
|
46
59
|
const filePath = path.resolve(this.$projectHelper.projectDir, pathToClean);
|
|
47
60
|
const displayPath = `${path.relative(this.$projectHelper.projectDir, filePath)}`.yellow;
|
|
48
|
-
this.$logger.trace(
|
|
61
|
+
this.$logger.trace(`${logPrefix}Trying to clean '${filePath}'`);
|
|
49
62
|
if (this.$fs.exists(filePath)) {
|
|
50
63
|
const stat = this.$fs.getFsStats(filePath);
|
|
64
|
+
let size = 0;
|
|
65
|
+
if (options === null || options === void 0 ? void 0 : options.stats) {
|
|
66
|
+
size = this.$fs.getSize(filePath);
|
|
67
|
+
}
|
|
51
68
|
if (stat.isDirectory()) {
|
|
52
|
-
this.$logger.trace(
|
|
53
|
-
this.$fs.deleteDirectorySafe(filePath);
|
|
69
|
+
this.$logger.trace(`${logPrefix}Path '${filePath}' is a directory, deleting.`);
|
|
70
|
+
!dryRun && this.$fs.deleteDirectorySafe(filePath);
|
|
54
71
|
fileType = "directory";
|
|
55
72
|
}
|
|
56
73
|
else {
|
|
57
|
-
this.$logger.trace(
|
|
58
|
-
this.$fs.deleteFile(filePath);
|
|
74
|
+
this.$logger.trace(`${logPrefix}Path '${filePath}' is a file, deleting.`);
|
|
75
|
+
!dryRun && this.$fs.deleteFile(filePath);
|
|
59
76
|
fileType = "file";
|
|
60
77
|
}
|
|
61
|
-
success = !this.$fs.exists(filePath);
|
|
78
|
+
const success = dryRun || !this.$fs.exists(filePath);
|
|
62
79
|
if (success) {
|
|
63
|
-
this.spinner.succeed(
|
|
80
|
+
this.spinner.succeed(`${logPrefix}Cleaned ${fileType} ${displayPath}`);
|
|
64
81
|
}
|
|
65
82
|
else {
|
|
66
83
|
const message = `Failed to Clean ${fileType}`.red;
|
|
67
|
-
this.spinner.fail(`${message} ${displayPath}`);
|
|
84
|
+
this.spinner.fail(`${logPrefix}${message} ${displayPath}`);
|
|
85
|
+
}
|
|
86
|
+
if (options === null || options === void 0 ? void 0 : options.stats) {
|
|
87
|
+
return { ok: success, size };
|
|
68
88
|
}
|
|
69
|
-
return success;
|
|
89
|
+
return { ok: success };
|
|
90
|
+
}
|
|
91
|
+
this.$logger.trace(`${logPrefix}Path '${filePath}' not found, skipping.`);
|
|
92
|
+
this.spinner.info(`${logPrefix}Skipping ${displayPath} because it doesn't exist.`);
|
|
93
|
+
if (options === null || options === void 0 ? void 0 : options.stats) {
|
|
94
|
+
return { ok: true, size: 0 };
|
|
70
95
|
}
|
|
71
|
-
|
|
72
|
-
this.spinner.info(`Skipping ${displayPath} because it doesn't exist.`);
|
|
73
|
-
return success;
|
|
96
|
+
return { ok: true };
|
|
74
97
|
});
|
|
75
98
|
}
|
|
76
99
|
}
|
|
@@ -15,7 +15,7 @@ const yok_1 = require("../common/yok");
|
|
|
15
15
|
class TerminalSpinnerService {
|
|
16
16
|
createSpinner(spinnerOptions = {}) {
|
|
17
17
|
spinnerOptions.stream = spinnerOptions.stream || process.stdout;
|
|
18
|
-
return
|
|
18
|
+
return ora(spinnerOptions);
|
|
19
19
|
}
|
|
20
20
|
execute(spinnerOptions, action) {
|
|
21
21
|
return __awaiter(this, void 0, void 0, function* () {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nativescript",
|
|
3
3
|
"preferGlobal": true,
|
|
4
|
-
"version": "8.5.
|
|
4
|
+
"version": "8.5.2",
|
|
5
5
|
"author": "NativeScript <support@nativescript.org>",
|
|
6
6
|
"description": "Command-line interface for building NativeScript projects",
|
|
7
7
|
"bin": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"byline": "5.0.0",
|
|
66
66
|
"chalk": "4.1.0",
|
|
67
67
|
"chokidar": "3.5.1",
|
|
68
|
-
"cli-table": "https://github.com/telerik/cli-table/tarball/v0.3.1.2",
|
|
68
|
+
"cli-table": "https://github.com/telerik-boneyard/cli-table/tarball/v0.3.1.2",
|
|
69
69
|
"color": "3.1.2",
|
|
70
70
|
"colors": "1.4.0",
|
|
71
71
|
"convert-source-map": "1.7.0",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"nativescript-dev-xcode": "0.2.1",
|
|
91
91
|
"nativescript-preview-sdk": "0.4.2",
|
|
92
92
|
"open": "7.1.0",
|
|
93
|
-
"ora": "5.
|
|
93
|
+
"ora": "5.4.1",
|
|
94
94
|
"osenv": "0.1.5",
|
|
95
95
|
"pacote": "11.1.11",
|
|
96
96
|
"pako": "1.0.11",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"plist": "3.0.1",
|
|
99
99
|
"plist-merge-patch": "0.1.1",
|
|
100
100
|
"prettier": "2.1.1",
|
|
101
|
-
"prompts": "2.4.
|
|
101
|
+
"prompts": "2.4.2",
|
|
102
102
|
"proper-lockfile": "4.1.1",
|
|
103
103
|
"proxy-lib": "0.4.0",
|
|
104
104
|
"qr-image": "3.2.0",
|
|
@@ -137,10 +137,9 @@
|
|
|
137
137
|
"@types/lodash": "4.14.158",
|
|
138
138
|
"@types/minimatch": "^3.0.3",
|
|
139
139
|
"@types/node": "14.0.27",
|
|
140
|
-
"@types/ora": "3.2.0",
|
|
141
140
|
"@types/pacote": "^11.1.0",
|
|
142
141
|
"@types/prettier": "2.1.0",
|
|
143
|
-
"@types/prompts": "2.
|
|
142
|
+
"@types/prompts": "2.4.2",
|
|
144
143
|
"@types/proper-lockfile": "4.1.1",
|
|
145
144
|
"@types/qr-image": "3.2.3",
|
|
146
145
|
"@types/retry": "0.12.0",
|