nativescript
Version:
Command-line interface for building NativeScript projects
290 lines • 14.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;
};
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
;