pluginHandlers.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. *
  3. * Copyright 2013 Anis Kadri
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing,
  12. * software distributed under the License is distributed on an
  13. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. * KIND, either express or implied. See the License for the
  15. * specific language governing permissions and limitations
  16. * under the License.
  17. *
  18. */
  19. /* jshint unused: vars */
  20. var fs = require('fs');
  21. var path = require('path');
  22. var shell = require('shelljs');
  23. var events = require('cordova-common').events;
  24. var CordovaError = require('cordova-common').CordovaError;
  25. var handlers = {
  26. 'source-file': {
  27. install: function (obj, plugin, project, options) {
  28. if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
  29. if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
  30. var dest = path.join(obj.targetDir, path.basename(obj.src));
  31. if (options && options.android_studio === true) {
  32. dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
  33. }
  34. if (options && options.force) {
  35. copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
  36. } else {
  37. copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
  38. }
  39. },
  40. uninstall: function (obj, plugin, project, options) {
  41. var dest = path.join(obj.targetDir, path.basename(obj.src));
  42. if (options && options.android_studio === true) {
  43. dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
  44. }
  45. deleteJava(project.projectDir, dest);
  46. }
  47. },
  48. 'lib-file': {
  49. install: function (obj, plugin, project, options) {
  50. var dest = path.join('libs', path.basename(obj.src));
  51. if (options && options.android_studio === true) {
  52. dest = path.join('app/libs', path.basename(obj.src));
  53. }
  54. copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
  55. },
  56. uninstall: function (obj, plugin, project, options) {
  57. var dest = path.join('libs', path.basename(obj.src));
  58. if (options && options.android_studio === true) {
  59. dest = path.join('app/libs', path.basename(obj.src));
  60. }
  61. removeFile(project.projectDir, dest);
  62. }
  63. },
  64. 'resource-file': {
  65. install: function (obj, plugin, project, options) {
  66. copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
  67. },
  68. uninstall: function (obj, plugin, project, options) {
  69. removeFile(project.projectDir, path.normalize(obj.target));
  70. }
  71. },
  72. 'framework': {
  73. install: function (obj, plugin, project, options) {
  74. var src = obj.src;
  75. if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
  76. events.emit('verbose', 'Installing Android library: ' + src);
  77. var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
  78. var subDir;
  79. if (obj.custom) {
  80. var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
  81. copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
  82. subDir = path.resolve(project.projectDir, subRelativeDir);
  83. } else {
  84. obj.type = 'sys';
  85. subDir = src;
  86. }
  87. if (obj.type === 'gradleReference') {
  88. project.addGradleReference(parentDir, subDir);
  89. } else if (obj.type === 'sys') {
  90. project.addSystemLibrary(parentDir, subDir);
  91. } else {
  92. project.addSubProject(parentDir, subDir);
  93. }
  94. },
  95. uninstall: function (obj, plugin, project, options) {
  96. var src = obj.src;
  97. if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
  98. events.emit('verbose', 'Uninstalling Android library: ' + src);
  99. var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
  100. var subDir;
  101. if (obj.custom) {
  102. var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
  103. removeFile(project.projectDir, subRelativeDir);
  104. subDir = path.resolve(project.projectDir, subRelativeDir);
  105. // If it's the last framework in the plugin, remove the parent directory.
  106. var parDir = path.dirname(subDir);
  107. if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
  108. fs.rmdirSync(parDir);
  109. }
  110. } else {
  111. obj.type = 'sys';
  112. subDir = src;
  113. }
  114. if (obj.type === 'gradleReference') {
  115. project.removeGradleReference(parentDir, subDir);
  116. } else if (obj.type === 'sys') {
  117. project.removeSystemLibrary(parentDir, subDir);
  118. } else {
  119. project.removeSubProject(parentDir, subDir);
  120. }
  121. }
  122. },
  123. asset: {
  124. install: function (obj, plugin, project, options) {
  125. if (!obj.src) {
  126. throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
  127. }
  128. if (!obj.target) {
  129. throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
  130. }
  131. copyFile(plugin.dir, obj.src, project.www, obj.target);
  132. if (options && options.usePlatformWww) {
  133. // CB-11022 copy file to both directories if usePlatformWww is specified
  134. copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
  135. }
  136. },
  137. uninstall: function (obj, plugin, project, options) {
  138. var target = obj.target || obj.src;
  139. if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
  140. removeFileF(path.resolve(project.www, target));
  141. removeFileF(path.resolve(project.www, 'plugins', plugin.id));
  142. if (options && options.usePlatformWww) {
  143. // CB-11022 remove file from both directories if usePlatformWww is specified
  144. removeFileF(path.resolve(project.platformWww, target));
  145. removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
  146. }
  147. }
  148. },
  149. 'js-module': {
  150. install: function (obj, plugin, project, options) {
  151. // Copy the plugin's files into the www directory.
  152. var moduleSource = path.resolve(plugin.dir, obj.src);
  153. var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
  154. // Read in the file, prepend the cordova.define, and write it back out.
  155. var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
  156. if (moduleSource.match(/.*\.json$/)) {
  157. scriptContent = 'module.exports = ' + scriptContent;
  158. }
  159. scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
  160. var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
  161. shell.mkdir('-p', path.dirname(wwwDest));
  162. fs.writeFileSync(wwwDest, scriptContent, 'utf-8');
  163. if (options && options.usePlatformWww) {
  164. // CB-11022 copy file to both directories if usePlatformWww is specified
  165. var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
  166. shell.mkdir('-p', path.dirname(platformWwwDest));
  167. fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
  168. }
  169. },
  170. uninstall: function (obj, plugin, project, options) {
  171. var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
  172. removeFileAndParents(project.www, pluginRelativePath);
  173. if (options && options.usePlatformWww) {
  174. // CB-11022 remove file from both directories if usePlatformWww is specified
  175. removeFileAndParents(project.platformWww, pluginRelativePath);
  176. }
  177. }
  178. }
  179. };
  180. module.exports.getInstaller = function (type) {
  181. if (handlers[type] && handlers[type].install) {
  182. return handlers[type].install;
  183. }
  184. events.emit('verbose', '<' + type + '> is not supported for android plugins');
  185. };
  186. module.exports.getUninstaller = function (type) {
  187. if (handlers[type] && handlers[type].uninstall) {
  188. return handlers[type].uninstall;
  189. }
  190. events.emit('verbose', '<' + type + '> is not supported for android plugins');
  191. };
  192. function copyFile (plugin_dir, src, project_dir, dest, link) {
  193. src = path.resolve(plugin_dir, src);
  194. if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!');
  195. // check that src path is inside plugin directory
  196. var real_path = fs.realpathSync(src);
  197. var real_plugin_path = fs.realpathSync(plugin_dir);
  198. if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
  199. dest = path.resolve(project_dir, dest);
  200. // check that dest path is located in project directory
  201. if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
  202. shell.mkdir('-p', path.dirname(dest));
  203. if (link) {
  204. symlinkFileOrDirTree(src, dest);
  205. } else if (fs.statSync(src).isDirectory()) {
  206. // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
  207. shell.cp('-Rf', src + '/*', dest);
  208. } else {
  209. shell.cp('-f', src, dest);
  210. }
  211. }
  212. // Same as copy file but throws error if target exists
  213. function copyNewFile (plugin_dir, src, project_dir, dest, link) {
  214. var target_path = path.resolve(project_dir, dest);
  215. if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
  216. copyFile(plugin_dir, src, project_dir, dest, !!link);
  217. }
  218. function symlinkFileOrDirTree (src, dest) {
  219. if (fs.existsSync(dest)) {
  220. shell.rm('-Rf', dest);
  221. }
  222. if (fs.statSync(src).isDirectory()) {
  223. shell.mkdir('-p', dest);
  224. fs.readdirSync(src).forEach(function (entry) {
  225. symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
  226. });
  227. } else {
  228. fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
  229. }
  230. }
  231. // checks if file exists and then deletes. Error if doesn't exist
  232. function removeFile (project_dir, src) {
  233. var file = path.resolve(project_dir, src);
  234. shell.rm('-Rf', file);
  235. }
  236. // deletes file/directory without checking
  237. function removeFileF (file) {
  238. shell.rm('-Rf', file);
  239. }
  240. // Sometimes we want to remove some java, and prune any unnecessary empty directories
  241. function deleteJava (project_dir, destFile) {
  242. removeFileAndParents(project_dir, destFile, 'src');
  243. }
  244. function removeFileAndParents (baseDir, destFile, stopper) {
  245. stopper = stopper || '.';
  246. var file = path.resolve(baseDir, destFile);
  247. if (!fs.existsSync(file)) return;
  248. removeFileF(file);
  249. // check if directory is empty
  250. var curDir = path.dirname(file);
  251. while (curDir !== path.resolve(baseDir, stopper)) {
  252. if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
  253. fs.rmdirSync(curDir);
  254. curDir = path.resolve(curDir, '..');
  255. } else {
  256. // directory not empty...do nothing
  257. break;
  258. }
  259. }
  260. }
  261. function generateAttributeError (attribute, element, id) {
  262. return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
  263. }