UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

430 lines • 15.6 kB
"use strict"; 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