nativescript
Version:
Command-line interface for building NativeScript projects
430 lines • 15.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var FileSystem_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileSystem = void 0;
const fs = require("fs");
const _ = require("lodash");
const path_1 = require("path");
const minimatch = require("minimatch");
const injector = require("./yok");
const crypto = require("crypto");
const shelljs = require("shelljs");
const helpers_1 = require("./helpers");
const constants_1 = require("../constants");
const os_1 = require("os");
const detectNewline = require("detect-newline");
const archiver_1 = require("archiver");
// TODO: Add .d.ts for mkdirp module (or use it from @types repo).
const mkdirp = require("mkdirp");
let FileSystem = FileSystem_1 = class FileSystem {
constructor($injector) {
this.$injector = $injector;
}
async zipFiles(zipFile, files, zipPathCallback) {
//we are resolving it here instead of in the constructor, because config has dependency on file system and config shouldn't require logger
const $logger = this.$injector.resolve("logger");
const zip = (0, archiver_1.create)("zip", {
zlib: {
level: 9,
},
});
const outFile = fs.createWriteStream(zipFile);
zip.pipe(outFile);
return new Promise((resolve, reject) => {
outFile.on("error", (err) => reject(err));
outFile.on("close", () => {
$logger.trace("zip: %d bytes written", zip.pointer());
resolve();
});
for (const file of files) {
let relativePath = zipPathCallback(file);
relativePath = relativePath.replace(/\\/g, "/");
$logger.trace("zipping as '%s' file '%s'", relativePath, file);
zip.append(fs.createReadStream(file), { name: relativePath });
}
zip.finalize();
});
}
utimes(path, atime, mtime) {
return fs.utimesSync(path, atime, mtime);
}
async unzip(zipFile, destinationDir, options, fileFilters) {
const shouldOverwriteFiles = !(options && options.overwriteExisitingFiles === false);
const isCaseSensitive = !(options && options.caseSensitive === false);
const $hostInfo = this.$injector.resolve("$hostInfo");
this.createDirectory(destinationDir);
let proc;
if ($hostInfo.isWindows) {
proc = (0, path_1.join)(__dirname, "resources/platform-tools/unzip/win32/unzip");
}
else if ($hostInfo.isDarwin) {
proc = "unzip"; // darwin unzip is info-zip
}
else if ($hostInfo.isLinux) {
proc = "unzip"; // linux unzip is info-zip
}
if (!isCaseSensitive) {
zipFile = this.findFileCaseInsensitive(zipFile);
}
const args = _.flatten([
"-b",
shouldOverwriteFiles ? "-o" : "-n",
isCaseSensitive ? [] : "-C",
zipFile,
fileFilters || [],
"-d",
destinationDir,
]);
const $childProcess = this.$injector.resolve("childProcess");
await $childProcess.spawnFromEvent(proc, args, "close", {
stdio: "ignore",
detached: true,
});
}
findFileCaseInsensitive(file) {
const dir = (0, path_1.dirname)(file);
const baseName = (0, path_1.basename)(file);
const entries = this.readDirectory(dir);
const match = minimatch.match(entries, baseName, {
nocase: true,
nonegate: true,
nonull: true,
})[0];
const result = (0, path_1.join)(dir, match);
return result;
}
exists(path) {
return fs.existsSync(path);
}
deleteFile(path) {
try {
fs.unlinkSync(path);
}
catch (err) {
if (err && err.code !== "ENOENT") {
// ignore "file doesn't exist" error
throw err;
}
}
}
deleteDirectory(directory) {
shelljs.rm("-rf", directory);
const err = shelljs.error();
if (err !== null) {
throw new Error(err);
}
}
deleteDirectorySafe(directory) {
try {
this.deleteDirectory(directory);
}
catch (e) {
return;
}
}
getFileSize(path) {
const stat = this.getFsStats(path);
return stat.size;
}
getSize(path) {
const dirSize = (dir, paths = new Map(), root = true) => {
const files = fs.readdirSync(dir, { withFileTypes: true });
files.map((file) => {
const path = (0, path_1.join)(dir, file.name);
if (file.isDirectory()) {
dirSize(path, paths, false);
return;
}
if (file.isFile()) {
const { size } = fs.statSync(path);
paths.set(path, size);
}
});
if (root) {
// console.log("root", paths);
return Array.from(paths.values()).reduce((sum, current) => sum + current, 0);
}
};
try {
return dirSize(path);
}
catch (err) {
return 0;
}
}
async futureFromEvent(eventEmitter, event) {
return new Promise((resolve, reject) => {
eventEmitter.once(event, function () {
const args = _.toArray(arguments);
if (event === "error") {
const err = args[0];
reject(err);
return;
}
switch (args.length) {
case 0:
resolve(undefined);
break;
case 1:
resolve(args[0]);
break;
default:
resolve(args);
break;
}
});
});
}
createDirectory(path) {
try {
mkdirp.sync(path);
}
catch (error) {
const $errors = this.$injector.resolve("errors");
let errorMessage = `Unable to create directory ${path}. \nError is: ${error}.`;
if (error.code === "EACCES") {
errorMessage += "\n\nYou may need to call the command with 'sudo'.";
}
$errors.fail(errorMessage);
}
}
readDirectory(path) {
return fs.readdirSync(path);
}
readFile(filename, options) {
return fs.readFileSync(filename, options);
}
readText(filename, options) {
options = options || { encoding: "utf8" };
if (_.isString(options)) {
options = { encoding: options };
}
if (!options.encoding) {
options.encoding = "utf8";
}
return this.readFile(filename, options);
}
readJson(filename, encoding) {
const data = this.readText(filename, encoding);
if (data) {
return (0, helpers_1.parseJson)(data);
}
return null;
}
writeFile(filename, data, encoding) {
this.createDirectory((0, path_1.dirname)(filename));
if (!data) {
// node 14 will no longer coerce unsupported input to strings anymore.
// clean any null or undefined data
data = "";
}
fs.writeFileSync(filename, data, { encoding: encoding });
}
appendFile(filename, data, encoding) {
fs.appendFileSync(filename, data, { encoding: encoding });
}
writeJson(filename, data, space, encoding) {
if (!space) {
space = this.getIndentationCharacter(filename);
}
let stringifiedData;
if ((0, path_1.basename)(filename) === constants_1.PACKAGE_JSON_FILE_NAME) {
let newline = os_1.EOL;
if (fs.existsSync(filename)) {
const existingFile = this.readText(filename);
newline = detectNewline(existingFile);
}
stringifiedData = JSON.stringify(data, null, space).concat(newline);
}
else {
stringifiedData = JSON.stringify(data, null, space);
}
return this.writeFile(filename, stringifiedData, encoding);
}
copyFile(sourceFileName, destinationFileName) {
if ((0, path_1.resolve)(sourceFileName) === (0, path_1.resolve)(destinationFileName)) {
return;
}
this.createDirectory((0, path_1.dirname)(destinationFileName));
// MobileApplication.app is resolved as a directory on Mac,
// therefore we need to copy it recursively as it's not a single file.
shelljs.cp("-rf", sourceFileName, destinationFileName);
const err = shelljs.error();
if (err) {
throw new Error(err);
}
}
createReadStream(path, options) {
return fs.createReadStream(path, options);
}
createWriteStream(path, options) {
return fs.createWriteStream(path, options);
}
chmod(path, mode) {
fs.chmodSync(path, mode);
}
getFsStats(path) {
return fs.statSync(path);
}
getLsStats(path) {
return fs.lstatSync(path);
}
getUniqueFileName(baseName) {
if (!this.exists(baseName)) {
return baseName;
}
const extension = (0, path_1.extname)(baseName);
const prefix = (0, path_1.basename)(baseName, extension);
for (let i = 2;; ++i) {
const numberedName = prefix + i + extension;
if (!this.exists(numberedName)) {
return numberedName;
}
}
}
isEmptyDir(directoryPath) {
const directoryContent = this.readDirectory(directoryPath);
return directoryContent.length === 0;
}
isRelativePath(p) {
const normal = (0, path_1.normalize)(p);
const absolute = (0, path_1.resolve)(p);
return normal !== absolute;
}
ensureDirectoryExists(directoryPath) {
if (!this.exists(directoryPath)) {
this.createDirectory(directoryPath);
}
}
rename(oldPath, newPath) {
fs.renameSync(oldPath, newPath);
}
renameIfExists(oldPath, newPath) {
try {
this.rename(oldPath, newPath);
return true;
}
catch (e) {
if (e.code === "ENOENT") {
return false;
}
throw e;
}
}
symlink(sourcePath, destinationPath, type) {
fs.symlinkSync(sourcePath, destinationPath, type);
}
async setCurrentUserAsOwner(path, owner) {
const $childProcess = this.$injector.resolve("childProcess");
if (!this.$injector.resolve("$hostInfo").isWindows) {
const chown = $childProcess.spawn("chown", ["-R", owner, path], {
stdio: "ignore",
detached: true,
});
await this.futureFromEvent(chown, "close");
}
// nothing to do on Windows, as chown does not work on this platform
}
// filterCallback: function(path: String, stat: fs.Stats): Boolean
enumerateFilesInDirectorySync(directoryPath, filterCallback, opts, foundFiles) {
foundFiles = foundFiles || [];
if (!this.exists(directoryPath)) {
const $logger = this.$injector.resolve("logger");
$logger.warn("Could not find folder: " + directoryPath);
return foundFiles;
}
const contents = this.readDirectory(directoryPath);
for (let i = 0; i < contents.length; ++i) {
const file = (0, path_1.join)(directoryPath, contents[i]);
let stat = null;
if (this.exists(file)) {
stat = this.getFsStats(file);
}
if (!stat || (filterCallback && !filterCallback(file, stat))) {
continue;
}
if (stat.isDirectory()) {
if (opts && opts.enumerateDirectories) {
foundFiles.push(file);
}
if (opts &&
opts.includeEmptyDirectories &&
this.readDirectory(file).length === 0) {
foundFiles.push(file);
}
this.enumerateFilesInDirectorySync(file, filterCallback, opts, foundFiles);
}
else {
foundFiles.push(file);
}
}
return foundFiles;
}
async getFileShasum(fileName, options) {
return new Promise((resolve, reject) => {
const algorithm = (options && options.algorithm) || "sha1";
const encoding = (options && options.encoding) || "hex";
const logger = this.$injector.resolve("$logger");
const shasumData = crypto.createHash(algorithm);
const fileStream = this.createReadStream(fileName);
fileStream.on("data", (data) => {
shasumData.update(data);
});
fileStream.on("end", () => {
const shasum = shasumData.digest(encoding);
logger.trace(`Shasum of file ${fileName} is ${shasum}`);
resolve(shasum);
});
fileStream.on("error", (err) => {
reject(err);
});
});
}
async readStdin() {
return new Promise((resolve, reject) => {
let buffer = "";
process.stdin.on("data", (data) => (buffer += data));
process.stdin.on("end", () => resolve(buffer));
});
}
rm(options, ...files) {
shelljs.rm(options, files);
}
deleteEmptyParents(directory) {
let parent = this.exists(directory) ? directory : (0, path_1.dirname)(directory);
while (this.isEmptyDir(parent)) {
this.deleteDirectory(parent);
parent = (0, path_1.dirname)(parent);
}
}
realpath(filePath) {
return fs.realpathSync(filePath);
}
getIndentationCharacter(filePath) {
if (!this.exists(filePath)) {
return FileSystem_1.DEFAULT_INDENTATION_CHARACTER;
}
const fileContent = this.readText(filePath).trim();
const matches = fileContent.match(FileSystem_1.JSON_OBJECT_REGEXP);
if (!matches || !matches[1]) {
return FileSystem_1.DEFAULT_INDENTATION_CHARACTER;
}
const indentation = matches[1];
return indentation[0] === " "
? indentation
: FileSystem_1.DEFAULT_INDENTATION_CHARACTER;
}
};
exports.FileSystem = FileSystem;
FileSystem.DEFAULT_INDENTATION_CHARACTER = "\t";
FileSystem.JSON_OBJECT_REGEXP = new RegExp(`{\\r*\\n*(\\W*)"`, "m");
exports.FileSystem = FileSystem = FileSystem_1 = __decorate([
injector.register("fs")
], FileSystem);
//# sourceMappingURL=file-system.js.map
;