11import { exec , execSync , ChildProcess } from 'child_process' ;
22import { EventEmitter } from 'events' ;
33import * as path from 'path' ;
4+ import * as https from 'https' ;
45import { Logger } from '../webkit/utilities' ;
56import { WebKitConnection } from '../webkit/webKitConnection' ;
67import { 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