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