Repository: cordova-lib
Updated Branches:
  refs/heads/master 9d8124bef -> 5737d74b2


CB-10822 Manage plugins/modules metadata using PlatformJson


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/57f2b3f2
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/57f2b3f2
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/57f2b3f2

Branch: refs/heads/master
Commit: 57f2b3f2a4e6129dab7f9e039823b339bf26db3a
Parents: 9d8124b
Author: Vladimir Kotikov <v-vlk...@microsoft.com>
Authored: Thu Mar 10 14:03:11 2016 +0300
Committer: Vladimir Kotikov <v-vlk...@microsoft.com>
Committed: Wed Mar 30 15:33:40 2016 +0300

----------------------------------------------------------------------
 cordova-common/package.json              |   5 +-
 cordova-common/spec/PlatformJson.spec.js | 160 ++++++++++++++++++++++++++
 cordova-common/src/PlatformJson.js       | 124 ++++++++++++++++++++
 3 files changed, 287 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/57f2b3f2/cordova-common/package.json
----------------------------------------------------------------------
diff --git a/cordova-common/package.json b/cordova-common/package.json
index 20c4916..6c5e1f2 100644
--- a/cordova-common/package.json
+++ b/cordova-common/package.json
@@ -33,14 +33,15 @@
     "plist": "^1.2.0",
     "q": "^1.4.1",
     "semver": "^5.0.1",
-    "shelljs": "^0.5.1",
+    "shelljs": "^0.5.3",
     "underscore": "^1.8.3",
     "unorm": "^1.3.3"
   },
   "devDependencies": {
     "istanbul": "^0.3.17",
     "jasmine-node": "^1.14.5",
-    "jshint": "^2.8.0"
+    "jshint": "^2.8.0",
+    "rewire": "^2.5.1"
   },
   "contributors": []
 }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/57f2b3f2/cordova-common/spec/PlatformJson.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/PlatformJson.spec.js 
b/cordova-common/spec/PlatformJson.spec.js
new file mode 100644
index 0000000..c9310a2
--- /dev/null
+++ b/cordova-common/spec/PlatformJson.spec.js
@@ -0,0 +1,160 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+var rewire = require('rewire');
+var PlatformJson = rewire('../src/PlatformJson');
+var ModuleMetadata = PlatformJson.__get__('ModuleMetadata');
+
+var FAKE_MODULE = {
+    name: 'fakeModule',
+    src: 'www/fakeModule.js',
+    clobbers: [{target: 'window.fakeClobber'}],
+    merges: [{target: 'window.fakeMerge'}],
+    runs: true
+};
+
+describe('PlatformJson class', function() {
+    it('should be constructable', function () {
+        expect(new PlatformJson()).toEqual(jasmine.any(PlatformJson));
+    });
+
+    describe('instance', function () {
+        var platformJson;
+        var fakePlugin;
+        
+        beforeEach(function () {
+            platformJson = new PlatformJson('/fake/path', 'android');
+            fakePlugin = jasmine.createSpyObj('fakePlugin', ['getJsModules']);
+            fakePlugin.id = 'fakeId';
+            fakePlugin.version = '1.0.0';
+            fakePlugin.getJsModules.andReturn([FAKE_MODULE]);
+        });
+        
+        describe('addPluginMetadata method', function () {
+            it('should not throw if root "modules" property is missing', 
function () {
+                expect(function () {
+                    platformJson.addPluginMetadata(fakePlugin);
+                }).not.toThrow();
+            });
+    
+            it('should add each module to "root.modules" array', function () {
+                platformJson.addPluginMetadata(fakePlugin);
+                expect(platformJson.root.modules.length).toBe(1);
+                
expect(platformJson.root.modules[0]).toEqual(jasmine.any(ModuleMetadata));
+            });
+            
+            it('shouldn\'t add module if there is already module with the same 
file added', function () {
+                platformJson.root.modules = [{
+                    name: 'fakePlugin2',
+                    file: 'plugins/fakeId/www/fakeModule.js'
+                }];
+                
+                platformJson.addPluginMetadata(fakePlugin);
+                expect(platformJson.root.modules.length).toBe(1);
+                expect(platformJson.root.modules[0].name).toBe('fakePlugin2');
+            });
+            
+            it('should add entry to plugin_metadata with corresponding 
version', function () {
+                platformJson.addPluginMetadata(fakePlugin);
+                
expect(platformJson.root.plugin_metadata[fakePlugin.id]).toBe(fakePlugin.version);
+            });
+        });
+        
+        describe('removePluginMetadata method', function () {
+            it('should not throw if root "modules" property is missing', 
function () {
+                expect(function () {
+                    platformJson.removePluginMetadata(fakePlugin);
+                }).not.toThrow();
+            });
+    
+            it('should remove plugin modules from "root.modules" array based 
on file path', function () {
+                
+                var pluginPaths = [
+                    'plugins/fakeId/www/fakeModule.js',
+                    'plugins/otherPlugin/www/module1.js',
+                    'plugins/otherPlugin/www/module1.js'
+                ];
+                
+                platformJson.root.modules = pluginPaths.map(function (p) { 
return {file: p}; });
+                platformJson.removePluginMetadata(fakePlugin);
+                var resultantPaths = platformJson.root.modules
+                    .map(function (p) { return p.file; })
+                    .filter(function (f) { return /fakeModule\.js$/.test(f); 
});
+                   
+                expect(resultantPaths.length).toBe(0);
+            });
+            
+            it('should remove entry from plugin_metadata with corresponding 
version', function () {
+                platformJson.root.plugin_metadata = {};
+                platformJson.root.plugin_metadata[fakePlugin.id] = 
fakePlugin.version;
+                platformJson.removePluginMetadata(fakePlugin);
+                
expect(platformJson.root.plugin_metadata[fakePlugin.id]).not.toBeDefined();
+            });
+        });
+        
+        describe('generateMetadata method', function () {
+            it('should generate text metadata containing list of installed 
modules', function () {
+                var meta = 
platformJson.addPluginMetadata(fakePlugin).generateMetadata();
+                expect(typeof meta).toBe('string');
+                expect(meta.indexOf(JSON.stringify(platformJson.root.modules, 
null, 4))).toBeGreaterThan(0);
+                // 
expect(meta).toMatch(JSON.stringify(platformJson.root.modules, null, 4));
+                
expect(meta).toMatch(JSON.stringify(platformJson.root.plugin_metadata, null, 
4));
+            });
+        });
+    });
+});
+
+describe('ModuleMetadata class', function () {
+    it('should be constructable', function () {
+        var meta;
+        expect(function name(params) {
+            meta = new ModuleMetadata('fakePlugin', {src: 
'www/fakeModule.js'});
+        }).not.toThrow();
+        expect(meta instanceof ModuleMetadata).toBeTruthy();
+    });
+    
+    it('should throw if either pluginId or jsModule argument isn\'t 
specified', function () {
+        expect(ModuleMetadata).toThrow();
+        expect(function () { new ModuleMetadata('fakePlugin', {}); 
}).toThrow();
+    });
+    
+    it('should guess module id either from name property of from module src', 
function () {
+        expect(new ModuleMetadata('fakePlugin', {name: 
'fakeModule'}).id).toMatch(/fakeModule$/);
+        expect(new ModuleMetadata('fakePlugin', {src: 
'www/fakeModule.js'}).id).toMatch(/fakeModule$/);
+    });
+    
+    it('should read "clobbers" property from module', function () {
+        expect(new ModuleMetadata('fakePlugin', {name: 
'fakeModule'}).clobbers).not.toBeDefined();
+        var metadata = new ModuleMetadata('fakePlugin', FAKE_MODULE);
+        expect(metadata.clobbers).toEqual(jasmine.any(Array));
+        expect(metadata.clobbers[0]).toBe(FAKE_MODULE.clobbers[0].target);
+    });
+    
+    it('should read "merges" property from module', function () {
+        expect(new ModuleMetadata('fakePlugin', {name: 
'fakeModule'}).merges).not.toBeDefined();
+        var metadata = new ModuleMetadata('fakePlugin', FAKE_MODULE);
+        expect(metadata.merges).toEqual(jasmine.any(Array));
+        expect(metadata.merges[0]).toBe(FAKE_MODULE.merges[0].target);
+    });
+    
+    it('should read "runs" property from module', function () {
+        expect(new ModuleMetadata('fakePlugin', {name: 
'fakeModule'}).runs).not.toBeDefined();
+        expect(new ModuleMetadata('fakePlugin', FAKE_MODULE).runs).toBe(true);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/57f2b3f2/cordova-common/src/PlatformJson.js
----------------------------------------------------------------------
diff --git a/cordova-common/src/PlatformJson.js 
b/cordova-common/src/PlatformJson.js
index 793e976..4e2b287 100644
--- a/cordova-common/src/PlatformJson.js
+++ b/cordova-common/src/PlatformJson.js
@@ -91,6 +91,38 @@ PlatformJson.prototype.addPlugin = function(pluginId, 
variables, isTopLevel) {
     return this;
 };
 
+/**
+ * @chaining
+ * Generates and adds metadata for provided plugin into associated 
<platform>.json file
+ *
+ * @param   {PluginInfo}  pluginInfo  A pluginInfo instance to add metadata 
from
+ * @returns {this} Current PlatformJson instance to allow calls chaining
+ */
+PlatformJson.prototype.addPluginMetadata = function (pluginInfo) {
+
+    var installedModules = this.root.modules || [];
+
+    var installedPaths = installedModules.map(function (installedModule) {
+        return installedModule.file;
+    });
+
+    var modulesToInstall = pluginInfo.getJsModules(this.platform)
+    .map(function (module) {
+        return new ModuleMetadata(pluginInfo.id, module);
+    })
+    .filter(function (metadata) {
+        // Filter out modules which are already added to metadata
+        return installedPaths.indexOf(metadata.file) === -1;
+    });
+
+    this.root.modules = installedModules.concat(modulesToInstall);
+
+    this.root.plugin_metadata = this.root.plugin_metadata || {};
+    this.root.plugin_metadata[pluginInfo.id] = pluginInfo.version;
+
+    return this;
+};
+
 PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
     var pluginsList = isTopLevel ?
         this.root.installed_plugins :
@@ -101,6 +133,35 @@ PlatformJson.prototype.removePlugin = function(pluginId, 
isTopLevel) {
     return this;
 };
 
+/**
+ * @chaining
+ * Removes metadata for provided plugin from associated file
+ *
+ * @param   {PluginInfo}  pluginInfo A PluginInfo instance to which modules' 
metadata
+ *   we need to remove
+ *
+ * @returns {this} Current PlatformJson instance to allow calls chaining
+ */
+PlatformJson.prototype.removePluginMetadata = function (pluginInfo) {
+    var modulesToRemove = pluginInfo.getJsModules(this.platform)
+    .map(function (jsModule) {
+        return  ['plugins', pluginInfo.id, jsModule.src].join('/');
+    });
+
+    var installedModules = this.root.modules || [];
+    this.root.modules = installedModules
+    .filter(function (installedModule) {
+        // Leave only those metadatas which 'file' is not in removed modules
+        return (modulesToRemove.indexOf(installedModule.file) === -1);
+    });
+
+    if (this.root.plugin_metadata) {
+        delete this.root.plugin_metadata[pluginInfo.id];
+    }
+
+    return this;
+};
+
 PlatformJson.prototype.addInstalledPluginToPrepareQueue = 
function(pluginDirName, vars, is_top_level) {
     this.root.prepare_queue.installed.push({'plugin':pluginDirName, 
'vars':vars, 'topLevel':is_top_level});
 };
@@ -125,6 +186,39 @@ PlatformJson.prototype.makeTopLevel = function(pluginId) {
     return this;
 };
 
+/**
+ * Generates a metadata for all installed plugins and js modules. The resultant
+ *   string is ready to be written to 'cordova_plugins.js'
+ *
+ * @returns {String} cordova_plugins.js contents
+ */
+PlatformJson.prototype.generateMetadata = function () {
+    return [
+        'cordova.define(\'cordova/plugin_list\', function(require, exports, 
module) {',
+        'module.exports = ' + JSON.stringify(this.root.modules, null, 4) + ';',
+        'module.exports.metadata = ',
+        '// TOP OF METADATA',
+        JSON.stringify(this.root.plugin_metadata, null, 4) + ';',
+        '// BOTTOM OF METADATA',
+        '});' // Close cordova.define.
+    ].join('\n');
+};
+
+/**
+ * @chaining
+ * Generates and then saves metadata to specified file. Doesn't check if file 
exists.
+ *
+ * @param {String} destination  File metadata will be written to
+ * @return {PlatformJson} PlatformJson instance
+ */
+PlatformJson.prototype.generateAndSaveMetadata = function (destination) {
+    var meta = this.generateMetadata();
+    shelljs.mkdir('-p', path.dirname(destination));
+    fs.writeFileSync(destination, meta, 'utf-8');
+
+    return this;
+};
+
 // convert a munge from the old format ([file][parent][xml] = count) to the 
current one
 function fix_munge(root) {
     root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]};
@@ -151,5 +245,35 @@ function fix_munge(root) {
     return root;
 }
 
+/**
+ * @constructor
+ * @class ModuleMetadata
+ *
+ * Creates a ModuleMetadata object that represents module entry in 
'cordova_plugins.js'
+ *   file at run time
+ *
+ * @param {String}  pluginId  Plugin id where this module installed from
+ * @param (JsModule|Object)  jsModule  A js-module entry from PluginInfo class 
to generate metadata for
+ */
+function ModuleMetadata (pluginId, jsModule) {
+
+    if (!pluginId) throw new TypeError('pluginId argument must be a valid 
plugin id');
+    if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule 
argument must contain src or/and name properties');
+
+    this.id  = pluginId + '.' + ( jsModule.name || 
jsModule.src.match(/([^\/]+)\.js/)[1] );
+    this.file = ['plugins', pluginId, jsModule.src].join('/');
+    this.pluginId = pluginId;
+
+    if (jsModule.clobbers && jsModule.clobbers.length > 0) {
+        this.clobbers = jsModule.clobbers.map(function(o) { return o.target; 
});
+    }
+    if (jsModule.merges && jsModule.merges.length > 0) {
+        this.merges = jsModule.merges.map(function(o) { return o.target; });
+    }
+    if (jsModule.runs) {
+        this.runs = true;
+    }
+}
+
 module.exports = PlatformJson;
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org
For additional commands, e-mail: commits-h...@cordova.apache.org

Reply via email to