Skip to content

Commit 799ef06

Browse files
committed
Make version checks smarter. Introduce check in the VS Code Marketplace for new version of the extension
1 parent 306a23a commit 799ef06

File tree

4 files changed

+226
-139
lines changed

4 files changed

+226
-139
lines changed

Diff for: nativescript/nativescript.ts

+152-73
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {exec, execSync, ChildProcess} from 'child_process';
22
import {EventEmitter} from 'events';
33
import * as path from 'path';
4+
import * as https from 'https';
45
import {Logger} from '../webkit/utilities';
56
import {WebKitConnection} from '../webkit/webKitConnection';
67
import {AndroidDebugConnection} from './android/androidDebugConnection';
@@ -72,9 +73,6 @@ export class IosProject extends NSProject {
7273
if (!this.isOSX()) {
7374
return Promise.reject('iOS platform is only supported on OS X.');
7475
}
75-
if(!CliInfo.isExisting()) {
76-
return Promise.reject(CliInfo.getMessage());
77-
}
7876

7977
// build command to execute
8078
let command: string = new CommandBuilder()
@@ -91,9 +89,6 @@ export class IosProject extends NSProject {
9189
if (!this.isOSX()) {
9290
return Promise.reject('iOS platform is supported only on OS X.');
9391
}
94-
if(!CliInfo.isExisting()) {
95-
return Promise.reject(CliInfo.getMessage());
96-
}
9792

9893
// build command to execute
9994
let command: string = new CommandBuilder()
@@ -111,10 +106,6 @@ export class IosProject extends NSProject {
111106
let readyToConnect: boolean = false;
112107

113108
return new Promise<string>((resolve, reject) => {
114-
if(!CliInfo.isCompatible()) {
115-
this.emit('TNS.outputMessage', 'WARNING: ' + CliInfo.getMessage(), 'error');
116-
}
117-
118109
// run NativeScript CLI command
119110
let child: ChildProcess = exec(command, { cwd: this.projectPath() });
120111

@@ -124,9 +115,6 @@ export class IosProject extends NSProject {
124115
if(!readyToConnect) {
125116
let matches: RegExpMatchArray = strData.match(socketPathPattern);
126117
if(matches && matches.length > 0) {
127-
if(!CliInfo.isCompatible()) {
128-
this.emit('TNS.outputMessage', 'WARNING: ' + CliInfo.getMessage(), 'error');
129-
}
130118
readyToConnect = true;
131119
resolve(matches[0].substr(socketPathPrefix.length));
132120
}
@@ -159,10 +147,6 @@ export class AndroidProject extends NSProject {
159147
}
160148

161149
public run(emulator: boolean): Promise<ChildProcess> {
162-
if(!CliInfo.isExisting()) {
163-
return Promise.reject(CliInfo.getMessage());
164-
}
165-
166150
// build command to execute
167151
let command: string = new CommandBuilder()
168152
.appendParam("run")
@@ -302,26 +286,9 @@ class CommandBuilder {
302286
}
303287
}
304288

305-
306-
export enum CliState {
307-
NotExisting,
308-
OlderThanSupported,
309-
NewerThanSupported,
310-
Compatible
311-
}
312-
313-
export module CliInfo {
314-
var installedCliVersion: number[];
315-
var extensionCliVersion: number[];
316-
let state: CliState;
317-
let message: string;
318-
319-
function compareByMinorVersions(v1: number[], v2: number[]) {
320-
return (v1[0] - v2[0] != 0) ? (v1[0] - v2[0]) : v1[1] - v2[1];
321-
}
322-
323-
function parseVersion(versionStr: string): number[] {
324-
if (versionStr == null) {
289+
class Version {
290+
public static parse(versionStr: string): number[] {
291+
if (versionStr === null) {
325292
return null;
326293
}
327294
let version: number[] = versionStr.split('.').map<number>((str, index, array) => parseInt(str));
@@ -331,58 +298,170 @@ export module CliInfo {
331298
return version;
332299
}
333300

334-
function versionToString(version: number[]) {
301+
public static stringify(version: number[]): string {
335302
return `${version[0]}.${version[1]}.${version[2]}`;
336303
}
337304

338-
export function getMessage() {
339-
return message;
305+
public static compareBySubminor(v1, v2): number {
306+
return (v1[0] - v2[0] != 0) ? (v1[0] - v2[0]) : (v1[1] - v2[1] != 0) ? v1[1] - v2[1] : v1[2] - v2[2];
340307
}
308+
}
341309

342-
export function getState() {
343-
return state;
310+
export class ExtensionVersionInfo {
311+
private static extensionVersion: number[] = null;
312+
private static minNativescriptCliVersion: number[] = null;
313+
private static extensionId: string = '8d837914-d8fa-45b5-965d-f76ebd6dbf5c';
314+
private static marketplaceQueryResult: Promise<any> = null;
315+
316+
private latestVersionMeta: any;
317+
private timestamp: number;
318+
319+
private static initVersionsFromPackageJson() {
320+
let packageJson = require('../../package.json');
321+
this.extensionVersion = Version.parse(packageJson.version);
322+
this.minNativescriptCliVersion = Version.parse(packageJson.minNativescriptCliVersion);
344323
}
345324

346-
export function isExisting() {
347-
return state != CliState.NotExisting;
325+
public static getExtensionVersion(): number[] {
326+
if (this.extensionVersion === null) {
327+
this.initVersionsFromPackageJson();
328+
}
329+
return this.extensionVersion;
348330
}
349331

350-
export function isCompatible() {
351-
return state == CliState.Compatible;
332+
public static getMinSupportedNativeScriptVersion(): number[] {
333+
if (this.minNativescriptCliVersion === null) {
334+
this.initVersionsFromPackageJson();
335+
}
336+
return this.minNativescriptCliVersion;
352337
}
353338

354-
function initialize() {
355-
// get the supported CLI version from package.json
356-
extensionCliVersion = parseVersion(require(path.resolve(__dirname, '../../package.json'))['nativescript-cli-version']);
357-
// get the currently installed CLI version
358-
let getVersionCommand: string = new CommandBuilder().appendParam('--version').build(); // build the command
359-
try {
360-
let versionStr: string = execSync(getVersionCommand).toString(); // execute it
361-
installedCliVersion = versionStr ? parseVersion(versionStr) : null; // parse the version string
362-
} catch(e) {
363-
installedCliVersion = null;
339+
public static getMarketplaceExtensionData(): Promise<any> {
340+
if (this.marketplaceQueryResult == null) {
341+
this.marketplaceQueryResult = new Promise<any>((resolve, reject) => {
342+
let postData: string = `{ filters: [{ criteria: [{ filterType: 4, value: "${ExtensionVersionInfo.extensionId}" }] }], flags: 262 }`;
343+
344+
let request = https.request({
345+
hostname: 'marketplace.visualstudio.com',
346+
path: '/_apis/public/gallery/extensionquery',
347+
method: 'POST',
348+
headers: {
349+
'Accept': 'application/json;api-version=2.2-preview.1',
350+
'Content-Type': 'application/json',
351+
'Transfer-Encoding': 'chunked',
352+
'Content-Length': Buffer.byteLength(postData)
353+
}
354+
}, response => {
355+
if (response.statusCode != 200) {
356+
reject(`Unable to download data from Visual Studio Marketplace. Status code: ${response.statusCode}`);
357+
return;
358+
}
359+
let body = '';
360+
response.on('data', chunk => {
361+
body += chunk;
362+
});
363+
response.on('end', () => {
364+
resolve(JSON.parse(body));
365+
});
366+
});
367+
368+
request.on('error', (e) => {
369+
reject(e);
370+
});
371+
372+
request.end(postData);
373+
});
364374
}
375+
return this.marketplaceQueryResult;
376+
}
365377

366-
// initialize the state of the CLI by comparing the installed CLI version and the extension CLI version
367-
if (installedCliVersion) {
368-
let compareResult: number = compareByMinorVersions(installedCliVersion, extensionCliVersion);
369-
if (compareResult < 0) {
370-
state = CliState.OlderThanSupported;
371-
message = `NativeScript extension is expected to work with NativeScript v${versionToString(extensionCliVersion)}, but currently NativeScript v${versionToString(installedCliVersion)} is installed. This may lead to not working features. Try to update NativeScript by executing 'npm install -g nativescript'.`;
372-
}
373-
else if (compareResult > 0) {
374-
state = CliState.NewerThanSupported;
375-
message = `NativeScript extension is expected to work with NativeScript v${versionToString(extensionCliVersion)}, but currently NativeScript v${versionToString(installedCliVersion)} is installed. This may lead to not working features. Try to update the extension by running 'Show Outdated Extensions' command.`
376-
}
377-
else {
378-
state = CliState.Compatible;
379-
message = null;
378+
public static createFromMarketplace(): Promise<ExtensionVersionInfo> {
379+
return this.getMarketplaceExtensionData()
380+
.then(marketplaceData => {
381+
let latestVersion = null;
382+
try {
383+
if (marketplaceData.results[0].extensions[0].extensionId == ExtensionVersionInfo.extensionId) {
384+
latestVersion = marketplaceData.results[0].extensions[0].versions[0];
385+
}
386+
} catch (e) { }
387+
return new ExtensionVersionInfo(latestVersion);
388+
});
389+
}
390+
391+
constructor(latestVersionMeta: any, timestamp?: number) {
392+
this.latestVersionMeta = latestVersionMeta;
393+
this.timestamp = timestamp || Date.now();
394+
}
395+
396+
public getLatestVersionMeta(): any {
397+
return this.latestVersionMeta;
398+
}
399+
400+
public isLatest(): boolean {
401+
if (!this.getLatestVersionMeta()) {
402+
return true;
403+
}
404+
return Version.compareBySubminor(ExtensionVersionInfo.getExtensionVersion(), Version.parse(this.getLatestVersionMeta().version)) >= 0;
405+
}
406+
407+
public getTimestamp(): number {
408+
return this.timestamp;
409+
}
410+
}
411+
412+
export enum CliState {
413+
NotExisting,
414+
OlderThanSupported,
415+
Compatible
416+
}
417+
418+
export class CliVersionInfo {
419+
private static installedCliVersion: number[] = null;
420+
421+
private _state: CliState;
422+
423+
public static getInstalledCliVersion(): number[] {
424+
if (this.installedCliVersion === null) {
425+
// get the currently installed CLI version
426+
let getVersionCommand: string = new CommandBuilder().appendParam('--version').build(); // tns --version
427+
try {
428+
let versionStr: string = execSync(getVersionCommand).toString().trim(); // execute it
429+
this.installedCliVersion = versionStr ? Version.parse(versionStr) : null; // parse the version string
430+
} catch(e) {
431+
this.installedCliVersion = null;
380432
}
381433
}
434+
435+
return this.installedCliVersion;
436+
}
437+
438+
constructor() {
439+
let installedCliVersion: number[] = CliVersionInfo.getInstalledCliVersion();
440+
if (installedCliVersion === null) {
441+
this._state = CliState.NotExisting;
442+
}
382443
else {
383-
state = CliState.NotExisting;
384-
message = `NativeScript not found, please run 'npm install –g nativescript@${versionToString(extensionCliVersion)}' to install it.`;
444+
let minSupportedCliVersion = ExtensionVersionInfo.getMinSupportedNativeScriptVersion();
445+
this._state = Version.compareBySubminor(installedCliVersion, minSupportedCliVersion) < 0 ? CliState.OlderThanSupported : CliState.Compatible;
446+
}
447+
}
448+
449+
public getState(): CliState {
450+
return this._state;
451+
}
452+
453+
public isCompatible(): boolean {
454+
return this._state === CliState.Compatible;
455+
}
456+
457+
public getErrorMessage(): string {
458+
switch (this._state) {
459+
case CliState.NotExisting:
460+
return `NativeScript CLI not found, please run 'npm install –g nativescript@${Version.stringify(ExtensionVersionInfo.getMinSupportedNativeScriptVersion())}' to install it.`;
461+
case CliState.OlderThanSupported:
462+
return `The existing NativeScript extension is compatible with NativeScript CLI v${Version.stringify(ExtensionVersionInfo.getMinSupportedNativeScriptVersion())} or greater. The currently installed NativeScript CLI is v${Version.stringify(CliVersionInfo.getInstalledCliVersion())}. You can update the NativeScript CLI by executing 'npm install -g nativescript'.`;
463+
default:
464+
return null;
385465
}
386466
}
387-
initialize();
388467
}

0 commit comments

Comments
 (0)