Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

pluginHandlers.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. Licensed to the Apache Software Foundation (ASF) under one
  3. or more contributor license agreements. See the NOTICE file
  4. distributed with this work for additional information
  5. regarding copyright ownership. The ASF licenses this file
  6. to you under the Apache License, Version 2.0 (the
  7. "License"); you may not use this file except in compliance
  8. with the License. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing,
  11. software distributed under the License is distributed on an
  12. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. KIND, either express or implied. See the License for the
  14. specific language governing permissions and limitations
  15. under the License.
  16. */
  17. 'use strict';
  18. const fs = require('fs-extra');
  19. const path = require('path');
  20. const util = require('util');
  21. const events = require('cordova-common').events;
  22. const CordovaError = require('cordova-common').CordovaError;
  23. // These frameworks are required by cordova-ios by default. We should never add/remove them.
  24. const keep_these_frameworks = [
  25. 'MobileCoreServices.framework',
  26. 'CoreGraphics.framework',
  27. 'AssetsLibrary.framework'
  28. ];
  29. const handlers = {
  30. 'source-file': {
  31. install: function (obj, plugin, project, options) {
  32. installHelper('source-file', obj, plugin.dir, project.projectDir, plugin.id, options, project);
  33. },
  34. uninstall: function (obj, plugin, project, options) {
  35. uninstallHelper('source-file', obj, project.projectDir, plugin.id, options, project);
  36. }
  37. },
  38. 'header-file': {
  39. install: function (obj, plugin, project, options) {
  40. installHelper('header-file', obj, plugin.dir, project.projectDir, plugin.id, options, project);
  41. },
  42. uninstall: function (obj, plugin, project, options) {
  43. uninstallHelper('header-file', obj, project.projectDir, plugin.id, options, project);
  44. }
  45. },
  46. 'resource-file': {
  47. install: function (obj, plugin, project, options) {
  48. const src = obj.src;
  49. let target = obj.target;
  50. const srcFile = path.resolve(plugin.dir, src);
  51. if (!target) {
  52. target = path.basename(src);
  53. }
  54. const destFile = path.resolve(project.resources_dir, target);
  55. if (!fs.existsSync(srcFile)) {
  56. throw new CordovaError(`Cannot find resource file "${srcFile}" for plugin ${plugin.id} in iOS platform`);
  57. }
  58. if (fs.existsSync(destFile)) {
  59. throw new CordovaError(`File already exists at destination "${destFile}" for resource file specified by plugin ${plugin.id} in iOS platform`);
  60. }
  61. project.xcode.addResourceFile(path.join('Resources', target));
  62. const link = !!(options && options.link);
  63. copyFile(plugin.dir, src, project.projectDir, destFile, link);
  64. },
  65. uninstall: function (obj, plugin, project, options) {
  66. const src = obj.src;
  67. let target = obj.target;
  68. if (!target) {
  69. target = path.basename(src);
  70. }
  71. const destFile = path.resolve(project.resources_dir, target);
  72. project.xcode.removeResourceFile(path.join('Resources', target));
  73. fs.removeSync(destFile);
  74. }
  75. },
  76. framework: { // CB-5238 custom frameworks only
  77. install: function (obj, plugin, project, options) {
  78. const src = obj.src;
  79. const custom = !!(obj.custom); // convert to boolean (if truthy/falsy)
  80. const embed = !!(obj.embed); // convert to boolean (if truthy/falsy)
  81. const link = !embed; // either link or embed can be true, but not both. the other has to be false
  82. if (!custom) {
  83. const keepFrameworks = keep_these_frameworks;
  84. if (keepFrameworks.indexOf(src) < 0) {
  85. if (obj.type === 'podspec') {
  86. // podspec handled in Api.js
  87. } else {
  88. project.frameworks[src] = project.frameworks[src] || 0;
  89. project.frameworks[src]++;
  90. const opt = { customFramework: false, embed: false, link: true, weak: obj.weak };
  91. events.emit('verbose', util.format('Adding non-custom framework to project... %s -> %s', src, JSON.stringify(opt)));
  92. project.xcode.addFramework(src, opt);
  93. events.emit('verbose', util.format('Non-custom framework added to project. %s -> %s', src, JSON.stringify(opt)));
  94. }
  95. }
  96. return;
  97. }
  98. const srcFile = path.resolve(plugin.dir, src);
  99. const targetDir = path.resolve(project.plugins_dir, plugin.id, path.basename(src));
  100. if (!fs.existsSync(srcFile)) throw new CordovaError(`Cannot find framework "${srcFile}" for plugin ${plugin.id} in iOS platform`);
  101. if (fs.existsSync(targetDir)) throw new CordovaError(`Framework "${targetDir}" for plugin ${plugin.id} already exists in iOS platform`);
  102. const symlink = !!(options && options.link);
  103. copyFile(plugin.dir, src, project.projectDir, targetDir, symlink); // frameworks are directories
  104. // CB-10773 translate back slashes to forward on win32
  105. const project_relative = fixPathSep(path.relative(project.projectDir, targetDir));
  106. // CB-11233 create Embed Frameworks Build Phase if does not exist
  107. const existsEmbedFrameworks = project.xcode.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Frameworks');
  108. if (!existsEmbedFrameworks && embed) {
  109. events.emit('verbose', '"Embed Frameworks" Build Phase (Embedded Binaries) does not exist, creating it.');
  110. project.xcode.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Embed Frameworks', null, 'frameworks');
  111. }
  112. const opt = { customFramework: true, embed, link, sign: true };
  113. events.emit('verbose', util.format('Adding custom framework to project... %s -> %s', src, JSON.stringify(opt)));
  114. project.xcode.addFramework(project_relative, opt);
  115. events.emit('verbose', util.format('Custom framework added to project. %s -> %s', src, JSON.stringify(opt)));
  116. },
  117. uninstall: function (obj, plugin, project, options) {
  118. const src = obj.src;
  119. if (!obj.custom) { // CB-9825 cocoapod integration for plugins
  120. const keepFrameworks = keep_these_frameworks;
  121. if (keepFrameworks.indexOf(src) < 0) {
  122. if (obj.type !== 'podspec') {
  123. // this should be refactored
  124. project.frameworks[src] = project.frameworks[src] || 1;
  125. project.frameworks[src]--;
  126. if (project.frameworks[src] < 1) {
  127. // Only remove non-custom framework from xcode project
  128. // if there is no references remains
  129. project.xcode.removeFramework(src);
  130. delete project.frameworks[src];
  131. }
  132. }
  133. }
  134. return;
  135. }
  136. const targetDir = fixPathSep(path.resolve(project.plugins_dir, plugin.id, path.basename(src)));
  137. const pbxFile = project.xcode.removeFramework(targetDir, { customFramework: true });
  138. if (pbxFile) {
  139. project.xcode.removeFromPbxEmbedFrameworksBuildPhase(pbxFile);
  140. }
  141. fs.removeSync(targetDir);
  142. }
  143. },
  144. 'lib-file': {
  145. install: function (obj, plugin, project, options) {
  146. events.emit('verbose', '<lib-file> install is not supported for iOS plugins');
  147. },
  148. uninstall: function (obj, plugin, project, options) {
  149. events.emit('verbose', '<lib-file> uninstall is not supported for iOS plugins');
  150. }
  151. },
  152. asset: {
  153. install: function (obj, plugin, project, options) {
  154. if (!obj.src) {
  155. throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
  156. }
  157. if (!obj.target) {
  158. throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
  159. }
  160. copyFile(plugin.dir, obj.src, project.www, obj.target);
  161. if (options && options.usePlatformWww) copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
  162. },
  163. uninstall: function (obj, plugin, project, options) {
  164. const target = obj.target;
  165. if (!target) {
  166. throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
  167. }
  168. removeFile(project.www, target);
  169. removeFileF(path.resolve(project.www, 'plugins', plugin.id));
  170. if (options && options.usePlatformWww) {
  171. removeFile(project.platformWww, target);
  172. removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
  173. }
  174. }
  175. },
  176. 'js-module': {
  177. install: function (obj, plugin, project, options) {
  178. // Copy the plugin's files into the www directory.
  179. const moduleSource = path.resolve(plugin.dir, obj.src);
  180. const moduleName = `${plugin.id}.${obj.name || path.basename(obj.src, path.extname(obj.src))}`;
  181. // Read in the file, prepend the cordova.define, and write it back out.
  182. let scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
  183. if (moduleSource.match(/.*\.json$/)) {
  184. scriptContent = `module.exports = ${scriptContent}`;
  185. }
  186. scriptContent = `cordova.define("${moduleName}", function(require, exports, module) {\n${scriptContent}\n});\n`;
  187. const moduleDestination = path.resolve(project.www, 'plugins', plugin.id, obj.src);
  188. fs.ensureDirSync(path.dirname(moduleDestination));
  189. fs.writeFileSync(moduleDestination, scriptContent, 'utf-8');
  190. if (options && options.usePlatformWww) {
  191. const platformWwwDestination = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
  192. fs.ensureDirSync(path.dirname(platformWwwDestination));
  193. fs.writeFileSync(platformWwwDestination, scriptContent, 'utf-8');
  194. }
  195. },
  196. uninstall: function (obj, plugin, project, options) {
  197. const pluginRelativePath = path.join('plugins', plugin.id, obj.src);
  198. removeFileAndParents(project.www, pluginRelativePath);
  199. if (options && options.usePlatformWww) removeFileAndParents(project.platformWww, pluginRelativePath);
  200. }
  201. }
  202. };
  203. module.exports.getInstaller = type => {
  204. if (handlers[type] && handlers[type].install) {
  205. return handlers[type].install;
  206. }
  207. events.emit('warn', `<${type}> is not supported for iOS plugins`);
  208. };
  209. module.exports.getUninstaller = type => {
  210. if (handlers[type] && handlers[type].uninstall) {
  211. return handlers[type].uninstall;
  212. }
  213. events.emit('warn', `<${type}> is not supported for iOS plugins`);
  214. };
  215. function installHelper (type, obj, plugin_dir, project_dir, plugin_id, options, project) {
  216. const srcFile = path.resolve(plugin_dir, obj.src);
  217. const targetDir = path.resolve(project.plugins_dir, plugin_id, obj.targetDir || '');
  218. const destFile = path.join(targetDir, path.basename(obj.src));
  219. let project_ref;
  220. const link = !!(options && options.link);
  221. if (link) {
  222. const trueSrc = fs.realpathSync(srcFile);
  223. // Create a symlink in the expected place, so that uninstall can use it.
  224. if (options && options.force) {
  225. copyFile(plugin_dir, trueSrc, project_dir, destFile, link);
  226. } else {
  227. copyNewFile(plugin_dir, trueSrc, project_dir, destFile, link);
  228. }
  229. // Xcode won't save changes to a file if there is a symlink involved.
  230. // Make the Xcode reference the file directly.
  231. // Note: Can't use path.join() here since it collapses 'Plugins/..', and xcode
  232. // library special-cases Plugins/ prefix.
  233. project_ref = `Plugins/${fixPathSep(path.relative(fs.realpathSync(project.plugins_dir), trueSrc))}`;
  234. } else {
  235. if (options && options.force) {
  236. copyFile(plugin_dir, srcFile, project_dir, destFile, link);
  237. } else {
  238. copyNewFile(plugin_dir, srcFile, project_dir, destFile, link);
  239. }
  240. project_ref = `Plugins/${fixPathSep(path.relative(project.plugins_dir, destFile))}`;
  241. }
  242. if (type === 'header-file') {
  243. project.xcode.addHeaderFile(project_ref);
  244. } else if (obj.framework) {
  245. const opt = { weak: obj.weak };
  246. const project_relative = path.join(path.basename(project.xcode_path), project_ref);
  247. project.xcode.addFramework(project_relative, opt);
  248. project.xcode.addToLibrarySearchPaths({ path: project_ref });
  249. } else {
  250. project.xcode.addSourceFile(project_ref, obj.compilerFlags ? { compilerFlags: obj.compilerFlags } : {});
  251. }
  252. }
  253. function uninstallHelper (type, obj, project_dir, plugin_id, options, project) {
  254. const targetDir = path.resolve(project.plugins_dir, plugin_id, obj.targetDir || '');
  255. const destFile = path.join(targetDir, path.basename(obj.src));
  256. let project_ref;
  257. const link = !!(options && options.link);
  258. if (link) {
  259. const trueSrc = fs.readlinkSync(destFile);
  260. project_ref = `Plugins/${fixPathSep(path.relative(fs.realpathSync(project.plugins_dir), trueSrc))}`;
  261. } else {
  262. project_ref = `Plugins/${fixPathSep(path.relative(project.plugins_dir, destFile))}`;
  263. }
  264. fs.removeSync(targetDir);
  265. if (type === 'header-file') {
  266. project.xcode.removeHeaderFile(project_ref);
  267. } else if (obj.framework) {
  268. const project_relative = path.join(path.basename(project.xcode_path), project_ref);
  269. project.xcode.removeFramework(project_relative);
  270. project.xcode.removeFromLibrarySearchPaths({ path: project_ref });
  271. } else {
  272. project.xcode.removeSourceFile(project_ref);
  273. }
  274. }
  275. const pathSepFix = new RegExp(path.sep.replace(/\\/, '\\\\'), 'g');
  276. function fixPathSep (file) {
  277. return file.replace(pathSepFix, '/');
  278. }
  279. function copyFile (plugin_dir, src, project_dir, dest, link) {
  280. src = path.resolve(plugin_dir, src);
  281. if (!fs.existsSync(src)) throw new CordovaError(`"${src}" not found!`);
  282. // check that src path is inside plugin directory
  283. const real_path = fs.realpathSync(src);
  284. const real_plugin_path = fs.realpathSync(plugin_dir);
  285. if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError(`File "${src}" is located outside the plugin directory "${plugin_dir}"`); }
  286. dest = path.resolve(project_dir, dest);
  287. // check that dest path is located in project directory
  288. if (dest.indexOf(project_dir) !== 0) { throw new CordovaError(`Destination "${dest}" for source file "${src}" is located outside the project`); }
  289. fs.ensureDirSync(path.dirname(dest));
  290. if (link) {
  291. linkFileOrDirTree(src, dest);
  292. } else {
  293. fs.copySync(src, dest);
  294. }
  295. }
  296. // Same as copy file but throws error if target exists
  297. function copyNewFile (plugin_dir, src, project_dir, dest, link) {
  298. const target_path = path.resolve(project_dir, dest);
  299. if (fs.existsSync(target_path)) { throw new CordovaError(`"${target_path}" already exists!`); }
  300. copyFile(plugin_dir, src, project_dir, dest, !!link);
  301. }
  302. function linkFileOrDirTree (src, dest) {
  303. if (fs.existsSync(dest)) {
  304. fs.removeSync(dest);
  305. }
  306. if (fs.statSync(src).isDirectory()) {
  307. fs.ensureDirSync(dest);
  308. fs.readdirSync(src).forEach(entry => {
  309. linkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
  310. });
  311. } else {
  312. fs.linkSync(src, dest);
  313. }
  314. }
  315. // checks if file exists and then deletes. Error if doesn't exist
  316. function removeFile (project_dir, src) {
  317. const file = path.resolve(project_dir, src);
  318. fs.removeSync(file);
  319. }
  320. // deletes file/directory without checking
  321. function removeFileF (file) {
  322. fs.removeSync(file);
  323. }
  324. function removeFileAndParents (baseDir, destFile, stopper) {
  325. stopper = stopper || '.';
  326. const file = path.resolve(baseDir, destFile);
  327. if (!fs.existsSync(file)) return;
  328. removeFileF(file);
  329. // check if directory is empty
  330. let curDir = path.dirname(file);
  331. while (curDir !== path.resolve(baseDir, stopper)) {
  332. if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
  333. fs.rmdirSync(curDir);
  334. curDir = path.resolve(curDir, '..');
  335. } else {
  336. // directory not empty...do nothing
  337. break;
  338. }
  339. }
  340. }
  341. function generateAttributeError (attribute, element, id) {
  342. return `Required attribute "${attribute}" not specified in <${element}> element from plugin: ${id}`;
  343. }