1+ ///<reference path="../.d.ts"/>
2+ "use strict" ;
3+ import path = require( "path" ) ;
4+ import shelljs = require( "shelljs" ) ;
5+ import semver = require( "semver" ) ;
6+ import constants = require( "./../constants" ) ;
7+
8+ export class PluginsService implements IPluginsService {
9+ private static INSTALL_COMMAND_NAME = "install" ;
10+ private static UNINSTALL_COMMAND_NAME = "uninstall" ;
11+
12+ constructor ( private $npm : INodePackageManager ,
13+ private $fs : IFileSystem ,
14+ private $projectData : IProjectData ,
15+ private $platformsData : IPlatformsData ,
16+ private $projectDataService : IProjectDataService ,
17+ private $childProcess : IChildProcess ,
18+ private $options : IOptions ,
19+ private $logger : ILogger ,
20+ private $errors : IErrors ) { }
21+
22+ public add ( plugin : string ) : IFuture < void > {
23+ return ( ( ) => {
24+ let pluginName = this . executeNpmCommand ( PluginsService . INSTALL_COMMAND_NAME , plugin ) . wait ( ) ;
25+ let nodeModuleData = this . getNodeModuleData ( pluginName ) ;
26+ if ( ! nodeModuleData . isPlugin ) {
27+ // We should remove already downloaded plugin and show an error message
28+ this . executeNpmCommand ( PluginsService . UNINSTALL_COMMAND_NAME , pluginName ) . wait ( ) ;
29+ this . $errors . failWithoutHelp ( `The specified plugin ${ plugin } is not a valid NativeScript plugin. Ensure that the plugin contains nativescript key in package.json file and try again.` ) ;
30+ }
31+
32+ this . prepare ( this . convertToPluginData ( nodeModuleData ) ) . wait ( ) ;
33+ this . $logger . out ( `Succsessfully installed plugin with name ${ nodeModuleData . name } .` ) ;
34+ } ) . future < void > ( ) ( ) ;
35+ }
36+
37+ public remove ( pluginName : string ) : IFuture < void > {
38+ return ( ( ) => {
39+ this . executeNpmCommand ( PluginsService . UNINSTALL_COMMAND_NAME , pluginName ) . wait ( ) ;
40+ let showMessage = true ;
41+ let action = ( modulesDestinationPath : string , platform : string ) => {
42+ shelljs . rm ( "-rf" , path . join ( modulesDestinationPath , pluginName ) ) ;
43+ this . $logger . out ( `Successfully removed plugin ${ pluginName } for ${ platform } platform` ) ;
44+ showMessage = false ;
45+ } ;
46+ this . executeForAllInstalledPlatforms ( action ) ;
47+
48+ if ( showMessage ) {
49+ this . $logger . out ( `Succsessfully removed plugin ${ pluginName } ` ) ;
50+ }
51+ } ) . future < void > ( ) ( ) ;
52+ }
53+
54+ public prepare ( pluginData : IPluginData ) : IFuture < void > {
55+ return ( ( ) => {
56+ let action = ( pluginDestinationPath : string , platform : string ) => {
57+ let skipExecution = false ;
58+ // Process .js files
59+ let installedFrameworkVersion = this . getInstalledFrameworkVersion ( platform ) . wait ( ) ;
60+ let pluginVersion = ( < any > pluginData . platformsData ) [ platform ] ;
61+ if ( semver . gt ( pluginVersion , installedFrameworkVersion ) ) {
62+ this . $logger . warn ( `Plugin ${ pluginData . name } with specified version ${ pluginVersion } for ${ platform } platform is not compatible for currently installed framework with version ${ installedFrameworkVersion } .` ) ;
63+ skipExecution = true ;
64+ }
65+
66+ if ( ! skipExecution ) {
67+ this . $fs . ensureDirectoryExists ( pluginDestinationPath ) . wait ( ) ;
68+ shelljs . cp ( "-R" , pluginData . fullPath , pluginDestinationPath ) ;
69+
70+ // TODO: Merge xmls - check if android.manifest or info.plist files exist and merge them
71+ let pluginPlatformsFolderPath = path . join ( pluginDestinationPath , pluginData . name , "platforms" ) ;
72+ if ( this . $fs . exists ( pluginPlatformsFolderPath ) . wait ( ) ) {
73+ shelljs . rm ( "-rf" , pluginPlatformsFolderPath ) ;
74+ }
75+
76+ // TODO: Add libraries
77+
78+ // Show message
79+ this . $logger . out ( `Successfully prepared plugin ${ pluginData . name } for ${ platform } platform` ) ;
80+ }
81+ } ;
82+
83+ this . executeForAllInstalledPlatforms ( action ) ;
84+ } ) . future < void > ( ) ( ) ;
85+ }
86+
87+ public getAllInstalledPlugins ( ) : IFuture < IPluginData [ ] > {
88+ return ( ( ) => {
89+ let nodeModules = this . $fs . readDirectory ( this . nodeModulesPath ) . wait ( ) ;
90+ let plugins : IPluginData [ ] = [ ] ;
91+ _ . each ( nodeModules , nodeModuleName => {
92+ var nodeModuleData = this . getNodeModuleData ( nodeModuleName ) ;
93+ if ( nodeModuleData . isPlugin ) {
94+ plugins . push ( this . convertToPluginData ( nodeModuleData ) ) ;
95+ }
96+ } ) ;
97+
98+ return plugins ;
99+ } ) . future < IPluginData [ ] > ( ) ( ) ;
100+ }
101+
102+ private executeNpmCommand ( npmCommandName : string , npmCommandArguments : string ) : IFuture < string > {
103+ return ( ( ) => {
104+ let command = this . composeNpmCommand ( npmCommandName , npmCommandArguments ) ;
105+ let result = this . $childProcess . exec ( command , { cwd : this . $projectData . projectDir } ) . wait ( ) ;
106+ return this . parseNpmCommandResult ( result ) ;
107+ } ) . future < string > ( ) ( ) ;
108+ }
109+
110+ private composeNpmCommand ( npmCommandName : string , npmCommandArguments : string ) : string {
111+ let command = `npm ${ npmCommandName } ${ npmCommandArguments } --save ` ;
112+ if ( this . $options . production ) {
113+ command += " --production " ;
114+ }
115+
116+ return command ;
117+ }
118+
119+ private parseNpmCommandResult ( npmCommandResult : string ) : string { // The npmCommandResult is in the following format: [<name>@<version node_modules/<name>]
120+ return npmCommandResult . split ( "@" ) [ 0 ] ; // returns plugin name
121+ }
122+
123+ private executeForAllInstalledPlatforms ( action : ( pluginDestinationPath : string , platform : string ) => void ) : void {
124+ let availablePlatforms = _ . keys ( this . $platformsData . availablePlatforms ) ;
125+ _ . each ( availablePlatforms , platform => {
126+ let isPlatformInstalled = this . $fs . exists ( path . join ( this . $projectData . platformsDir , platform . toLowerCase ( ) ) ) . wait ( ) ;
127+ if ( isPlatformInstalled ) {
128+ let platformData = this . $platformsData . getPlatformData ( platform . toLowerCase ( ) ) ;
129+ let pluginDestinationPath = path . join ( platformData . appDestinationDirectoryPath , constants . APP_FOLDER_NAME , "tns_modules" ) ;
130+ action ( pluginDestinationPath , platform . toLowerCase ( ) ) ;
131+ }
132+ } ) ;
133+ }
134+
135+ private getNodeModuleData ( moduleName : string ) : INodeModuleData {
136+ let fullNodeModulePath = path . join ( this . nodeModulesPath , moduleName ) ;
137+ let packageJsonFilePath = path . join ( fullNodeModulePath , "package.json" ) ;
138+ let data = require ( packageJsonFilePath ) ;
139+ let result = {
140+ name : data . name ,
141+ version : data . version ,
142+ isPlugin : data . nativescript ,
143+ fullPath : fullNodeModulePath ,
144+ } ;
145+
146+ return result ;
147+ }
148+
149+ private convertToPluginData ( nodeModuleData : INodeModuleData ) : IPluginData {
150+ let pluginData : any = _ . extend ( { } , nodeModuleData ) ;
151+ let data = < any > ( nodeModuleData . isPlugin ) ;
152+ if ( data ) {
153+ pluginData . platformsData = data . platforms ;
154+ }
155+
156+ return pluginData ;
157+ }
158+
159+ private get nodeModulesPath ( ) : string {
160+ return path . join ( this . $projectData . projectDir , "node_modules" ) ;
161+ }
162+
163+ private getInstalledFrameworkVersion ( platform : string ) : IFuture < string > {
164+ return ( ( ) => {
165+ let platformData = this . $platformsData . getPlatformData ( platform ) ;
166+ this . $projectDataService . initialize ( this . $projectData . projectDir ) ;
167+ let frameworkData = this . $projectDataService . getValue ( platformData . frameworkPackageName ) . wait ( ) ;
168+ return frameworkData . version ;
169+ } ) . future < string > ( ) ( ) ;
170+ }
171+ }
172+ $injector . register ( "pluginsService" , PluginsService ) ;
0 commit comments