UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

290 lines • 14.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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DoctorService = void 0; const os_1 = require("os"); const path = require("path"); const _ = require("lodash"); const helpers = require("../common/helpers"); const decorators_1 = require("../common/decorators"); const constants_1 = require("../constants"); const doctor_1 = require("@nativescript/doctor"); const yok_1 = require("../common/yok"); const color_1 = require("../color"); class DoctorService { get jsonFileSettingsPath() { return path.join(this.$settingsService.getProfileDir(), "doctor-cache.json"); } get $jsonFileSettingsService() { return this.$injector.resolve("jsonFileSettingsService", { jsonFileSettingsPath: this.jsonFileSettingsPath }); } constructor($analyticsService, $hostInfo, $logger, $childProcess, $injector, $projectDataService, $fs, $terminalSpinnerService, $versionsService, $settingsService) { this.$analyticsService = $analyticsService; this.$hostInfo = $hostInfo; this.$logger = $logger; this.$childProcess = $childProcess; this.$injector = $injector; this.$projectDataService = $projectDataService; this.$fs = $fs; this.$terminalSpinnerService = $terminalSpinnerService; this.$versionsService = $versionsService; this.$settingsService = $settingsService; } async printWarnings(configOptions) { configOptions = configOptions || {}; const getInfosData = { projectDir: configOptions.projectDir, androidRuntimeVersion: configOptions.runtimeVersion, platform: configOptions.platform, }; const infos = await this.$terminalSpinnerService.execute({ text: `Getting environment information ${os_1.EOL}`, }, () => this.getInfos({ forceCheck: configOptions.forceCheck }, getInfosData)); const warnings = infos.filter((info) => info.type === doctor_1.constants.WARNING_TYPE_NAME); const hasWarnings = warnings.length > 0; const hasAndroidWarnings = warnings.filter((warning) => _.includes(warning.platforms, doctor_1.constants.ANDROID_PLATFORM_NAME)).length > 0; if (hasAndroidWarnings) { this.printPackageManagerTip(); } if (!configOptions || configOptions.trackResult) { // TODO(Analytics): Consider sending this information to Google Analytics // await this.$analyticsService.track("DoctorEnvironmentSetup", hasWarnings ? "incorrect" : "correct"); } if (hasWarnings) { this.$logger.info("There seem to be issues with your configuration."); // cleanup the cache file as there seems to be issues with the current config // all projects need to be rechecked this.$fs.deleteFile(this.jsonFileSettingsPath); } else { this.$logger.info(color_1.color.bold("No issues were detected.")); await this.$jsonFileSettingsService.saveSetting(this.getKeyForConfiguration(getInfosData), infos); this.printInfosCore(infos); } try { await this.$versionsService.printVersionsInformation(configOptions.platform); } catch (err) { this.$logger.error("Cannot get the latest versions information from npm. Please try again later."); } // todo: check for deprecated imports from `tns-core-modules` this.checkForDeprecatedShortImportsInAppDir(configOptions.projectDir); await this.$injector .resolve("platformEnvironmentRequirements") .checkEnvironmentRequirements({ platform: configOptions.platform, projectDir: configOptions.projectDir, runtimeVersion: configOptions.runtimeVersion, options: configOptions.options, }); } async runSetupScript() { await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Run Setup Script" /* TrackActionNames.RunSetupScript */, additionalData: "Starting", }); if (this.$hostInfo.isLinux) { await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Run Setup Script" /* TrackActionNames.RunSetupScript */, additionalData: "Skipped as OS is Linux", }); return; } this.$logger.info("Running the setup script to try and automatically configure your environment."); if (this.$hostInfo.isDarwin) { await this.runSetupScriptCore(DoctorService.DarwinSetupScriptLocation, []); } if (this.$hostInfo.isWindows) { await this.runSetupScriptCore(DoctorService.WindowsSetupScriptExecutable, DoctorService.WindowsSetupScriptArguments); } await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Run Setup Script" /* TrackActionNames.RunSetupScript */, additionalData: "Finished", }); } async canExecuteLocalBuild(configuration) { await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Check Local Build Setup" /* TrackActionNames.CheckLocalBuildSetup */, additionalData: "Starting", }); const sysInfoConfig = { platform: configuration.platform, projectDir: configuration.projectDir, androidRuntimeVersion: configuration.runtimeVersion, }; const infos = await this.getInfos({ forceCheck: configuration && configuration.forceCheck }, sysInfoConfig); const warnings = this.filterInfosByType(infos, doctor_1.constants.WARNING_TYPE_NAME); const hasWarnings = warnings.length > 0; if (hasWarnings) { // cleanup the cache file as there seems to be issues with the current config // all projects need to be rechecked this.$fs.deleteFile(this.jsonFileSettingsPath); await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Check Local Build Setup" /* TrackActionNames.CheckLocalBuildSetup */, additionalData: `Warnings:${warnings.map((w) => w.message).join("__")}`, }); this.printInfosCore(infos); } else { infos.map((info) => this.$logger.trace(info.message)); await this.$jsonFileSettingsService.saveSetting(this.getKeyForConfiguration(sysInfoConfig), infos); } await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: "Check Local Build Setup" /* TrackActionNames.CheckLocalBuildSetup */, additionalData: `Finished: Is setup correct: ${!hasWarnings}`, }); return !hasWarnings; } checkForDeprecatedShortImportsInAppDir(projectDir) { if (projectDir) { try { const files = this.$projectDataService.getAppExecutableFiles(projectDir); const shortImports = this.getDeprecatedShortImportsInFiles(files, projectDir); if (shortImports.length) { this.$logger.printMarkdown("Detected short imports in your application. Please note that `short imports are deprecated` since NativeScript 5.2.0. More information can be found in this blogpost https://www.nativescript.org/blog/say-goodbye-to-short-imports-in-nativescript"); shortImports.forEach((shortImport) => { this.$logger.printMarkdown(`In file \`${shortImport.file}\` line \`${shortImport.line}\` is short import. Add \`tns-core-modules/\` in front of the required/imported module.`); }); } } catch (err) { this.$logger.trace(`Unable to validate if project has short imports. Error is`, err); } } } getDeprecatedShortImportsInFiles(files, projectDir) { const shortImportRegExp = this.getShortImportRegExp(projectDir); const shortImports = []; for (const file of files) { const fileContent = this.$fs.readText(file); const strippedComments = helpers.stripComments(fileContent); const linesToCheck = _.flatten(strippedComments.split(/\r?\n/).map((line) => line.split(";"))); const linesWithRequireStatements = linesToCheck.filter((line) => /\btns-core-modules\b/.exec(line) === null && (/\bimport\b/.exec(line) || /\brequire\b/.exec(line))); for (const line of linesWithRequireStatements) { const matches = line.match(shortImportRegExp); if (matches && matches.length) { shortImports.push({ file, line }); } } } return shortImports; } getShortImportRegExp(projectDir) { const pathToTnsCoreModules = path.join(projectDir, constants_1.NODE_MODULES_FOLDER_NAME, constants_1.TNS_CORE_MODULES_NAME); const coreModulesSubDirs = this.$fs .readDirectory(pathToTnsCoreModules) .filter((entry) => this.$fs .getFsStats(path.join(pathToTnsCoreModules, entry)) .isDirectory()); const stringRegularExpressionsPerDir = coreModulesSubDirs.map((c) => { // require("text"); // require("text/smth"); // require( "text/smth"); // require( "text/smth" ); // import * as text from "text"; // import { a } from "text"; // import {a } from "text/abc" const subDirPart = `[\"\']${c}[\"\'/]`; return `(\\brequire\\s*?\\(\\s*?${subDirPart})|(\\bimport\\b.*?from\\s*?${subDirPart})`; }); return new RegExp(stringRegularExpressionsPerDir.join("|"), "g"); } async runSetupScriptCore(executablePath, setupScriptArgs) { return this.$childProcess.spawnFromEvent(executablePath, setupScriptArgs, "close", { stdio: "inherit" }); } printPackageManagerTip() { if (this.$hostInfo.isWindows) { this.$logger.info("TIP: To avoid setting up the necessary environment variables, you can use the chocolatey package manager to install the Android SDK and its dependencies." + os_1.EOL); } else if (this.$hostInfo.isDarwin) { this.$logger.info("TIP: To avoid setting up the necessary environment variables, you can use the Homebrew package manager to install the Android SDK and its dependencies." + os_1.EOL); } } printInfosCore(infos) { if (!helpers.isInteractive()) { infos.map((info) => { let message = info.message; if (info.type === doctor_1.constants.WARNING_TYPE_NAME) { message = `WARNING: ${color_1.color.yellow(info.message)} ${os_1.EOL} ${info.additionalInformation} ${os_1.EOL}`; } this.$logger.info(message); }); } infos .filter((info) => info.type === doctor_1.constants.INFO_TYPE_NAME) .map((info) => { const spinner = this.$terminalSpinnerService.createSpinner(); spinner.text = info.message; spinner.succeed(); }); infos .filter((info) => info.type === doctor_1.constants.WARNING_TYPE_NAME) .map((info) => { const spinner = this.$terminalSpinnerService.createSpinner(); spinner.text = `${color_1.color.yellow(info.message)} ${os_1.EOL} ${info.additionalInformation} ${os_1.EOL}`; spinner.fail(); }); } filterInfosByType(infos, type) { return infos.filter((info) => info.type === type); } getKeyForConfiguration(sysInfoConfig) { const nativeScriptData = sysInfoConfig && sysInfoConfig.projectDir && JSON.stringify(this.$fs.readJson(path.join(sysInfoConfig.projectDir, "package.json")) .nativescript); const delimiter = "__"; const key = [ JSON.stringify(sysInfoConfig), process.env.ANDROID_HOME, process.env.JAVA_HOME, process.env["CommonProgramFiles(x86)"], process.env["CommonProgramFiles"], process.env.PROCESSOR_ARCHITEW6432, process.env.ProgramFiles, process.env["ProgramFiles(x86)"], nativeScriptData, ] .filter((a) => !!a) .join(delimiter); const data = helpers.getHash(key, { algorithm: "md5" }); return data; } async getInfos(cacheConfig, sysInfoConfig) { const key = this.getKeyForConfiguration(sysInfoConfig); const infosFromCache = cacheConfig.forceCheck ? null : await this.$jsonFileSettingsService.getSettingValue(key); this.$logger.trace(`getInfos cacheConfig options:`, cacheConfig, " current info from cache: ", infosFromCache); const infos = infosFromCache || (await doctor_1.doctor.getInfos(sysInfoConfig)); return infos; } } exports.DoctorService = DoctorService; DoctorService.DarwinSetupScriptLocation = path.join(__dirname, "..", "..", "setup", "mac-startup-shell-script.sh"); DoctorService.WindowsSetupScriptExecutable = "powershell.exe"; DoctorService.WindowsSetupScriptArguments = [ "start-process", "-FilePath", "PowerShell.exe", "-NoNewWindow", "-Wait", "-ArgumentList", "\"-NoProfile -ExecutionPolicy Bypass -Command iex ((new-object net.webclient).DownloadString('https://www.nativescript.org/setup/win'))\"", ]; __decorate([ (0, decorators_1.cache)() ], DoctorService.prototype, "jsonFileSettingsPath", null); __decorate([ (0, decorators_1.cache)() ], DoctorService.prototype, "$jsonFileSettingsService", null); yok_1.injector.register("doctorService", DoctorService); //# sourceMappingURL=doctor-service.js.map