GitHub PullRequestReviewCommentEvent at 2014-02-18 22:30:10
Repository: cordova-plugman
Actor: kamrik
Payload: {"comment":{"url":"https://api.github.com/repos/apache/cordova-plugman/pulls/comments/9845316","id":9845316,"diff_hunk":"@@ -52,404 +54,523 @@ var keep_these_frameworks = [\n     'AssetsLibrary.framework'\n ];\n \n-module.exports = {\n-    add_installed_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, vars, is_top_level) {\n-        checkPlatform(platform);\n-        var config = module.exports.get_platform_json(plugins_dir, platform);\n-        config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars, 'topLevel':is_top_level});\n-        module.exports.save_platform_json(config, plugins_dir, platform);\n-    },\n-    add_uninstalled_plugin_to_prepare_queue:function(plugins_dir, plugin, platform, is_top_level) {\n-        checkPlatform(platform);\n-\n-        var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, plugin, 'plugin.xml'));\n-        var config = module.exports.get_platform_json(plugins_dir, platform);\n-        config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id'], 'topLevel':is_top_level});\n-        module.exports.save_platform_json(config, plugins_dir, platform);\n-    },\n-    get_platform_json:function(plugins_dir, platform) {\n-        checkPlatform(platform);\n-\n-        var filepath = path.join(plugins_dir, platform + '.json');\n-        if (fs.existsSync(filepath)) {\n-            return JSON.parse(fs.readFileSync(filepath, 'utf-8'));\n-        } else {\n-            var config = {\n-                prepare_queue:{installed:[], uninstalled:[]},\n-                config_munge:{},\n-                installed_plugins:{},\n-                dependent_plugins:{}\n-            };\n-            fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8');\n-            return config;\n-        }\n-    },\n-    save_platform_json:function(config, plugins_dir, platform) {\n-        checkPlatform(platform);\n \n-        var filepath = path.join(plugins_dir, platform + '.json');\n-        fs.writeFileSync(filepath, JSON.stringify(config), 'utf-8');\n-    },\n-    generate_plugin_config_munge:function(plugin_dir, platform, project_dir, vars) {\n-        checkPlatform(platform);\n-\n-        vars = vars || {};\n-        var platform_handler = require('./../platforms')[platform];\n-        // Add PACKAGE_NAME variable into vars\n-        if (!vars['PACKAGE_NAME']) {\n-            vars['PACKAGE_NAME'] = platform_handler.package_name(project_dir);\n-        }\n+var package = module.exports = {};\n \n-        var munge = {};\n-        var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'));\n-\n-        var platformTag = plugin_xml.find('platform[@name=\"' + platform + '\"]');\n-        var changes = [];\n-        // add platform-agnostic config changes\n-        changes = changes.concat(plugin_xml.findall('config-file'));\n-        if (platformTag) {\n-            // add platform-specific config changes if they exist\n-            changes = changes.concat(platformTag.findall('config-file'));\n-\n-            // note down plugins-plist munges in special section of munge obj\n-            var plugins_plist = platformTag.findall('plugins-plist');\n-            plugins_plist.forEach(function(pl) {\n-                if (!munge['plugins-plist']) {\n-                    munge['plugins-plist'] = {};\n-                }\n-                var key = pl.attrib['key'];\n-                var value = pl.attrib['string'];\n-                if (!munge['plugins-plist'][key]) {\n-                    munge['plugins-plist'][key] = value;\n-                }\n-            });\n-            // note down pbxproj framework munges in special section of munge obj\n-            // CB-5238 this is only for systems frameworks\n-            var frameworks = platformTag.findall('framework');\n-            frameworks.forEach(function(f) {\n-                var custom = f.attrib['custom'];\n-                if(!custom) {\n-                  if (!munge['framework']) {\n-                      munge['framework'] = {};\n-                  }\n-                  var file = f.attrib['src'];\n-                  var weak = f.attrib['weak'];\n-                  weak = (weak == undefined || weak == null || weak != 'true' ? 'false' : 'true');\n-                  if (!munge['framework'][file]) {\n-                      munge['framework'][file] = {};\n-                  }\n-                  if (!munge['framework'][file][weak]) {\n-                      munge['framework'][file][weak] = 0;\n-                  }\n-                  munge['framework'][file][weak] += 1;\n-                }\n-            });\n-        }\n+package.PlatformMunger = PlatformMunger;\n \n-        changes.forEach(function(change) {\n-            var target = change.attrib['target'];\n-            var parent = change.attrib['parent'];\n-            if (!munge[target]) {\n-                munge[target] = {};\n-            }\n-            if (!munge[target][parent]) {\n-                munge[target][parent] = {};\n-            }\n-            var xmls = change.getchildren();\n-            xmls.forEach(function(xml) {\n-                // 1. stringify each xml\n-                var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});\n-                // interp vars\n-                vars && Object.keys(vars).forEach(function(key) {\n-                    var regExp = new RegExp(\"\\\\$\" + key, \"g\");\n-                    stringified = stringified.replace(regExp, vars[key]);\n-                });\n-                // 2. add into munge\n-                if (!munge[target][parent][stringified]) {\n-                    munge[target][parent][stringified] = 0;\n-                }\n-                munge[target][parent][stringified] += 1;\n-            });\n-        });\n-        return munge;\n-    },\n-\n-    remove_plugin_changes:function(platform, project_dir, plugins_dir, plugin_name, plugin_id, is_top_level, should_decrement) {\n-        var platform_config = module.exports.get_platform_json(plugins_dir, platform);\n-        var plugin_dir = path.join(plugins_dir, plugin_name);\n-        var plugin_vars = (is_top_level ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]);\n-\n-        // get config munge, aka how did this plugin change various config files\n-        var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, project_dir, plugin_vars);\n-        // global munge looks at all plugins' changes to config files\n-        var global_munge = platform_config.config_munge;\n-\n-        var plistObj, pbxproj;\n-        if (platform == 'ios') {\n-            if (global_munge['plugins-plist'] && config_munge['plugins-plist']) {\n-                var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'));\n-                if (plistfile.length > 0) {\n-                    plistfile = plistfile[0];\n-                    // determine if this is a binary or ascii plist and choose the parser\n-                    // this is temporary until binary support is added to node-plist\n-                    var pl = (isBinaryPlist(plistfile) ? bplist : plist);\n-                    plistObj = pl.parseFileSync(plistfile);\n+/******************************************************************************\n+Adapters to keep the current refactoring effort to within this file\n+******************************************************************************/\n+package.add_plugin_changes = function(platform, project_dir, plugins_dir, plugin_id, plugin_vars, is_top_level, should_increment, cache) {\n+    var munger = new PlatformMunger(platform, project_dir, plugins_dir);\n+    munger.add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment, cache);\n+    munger.save_all();\n+};\n+\n+package.remove_plugin_changes = function(platform, project_dir, plugins_dir, plugin_name, plugin_id, is_top_level, should_decrement) {\n+    // TODO: should_decrement parameter is never used, remove it here and wherever called\n+    var munger = new PlatformMunger(platform, project_dir, plugins_dir);\n+    munger.remove_plugin_changes(plugin_name, plugin_id, is_top_level);\n+    munger.save_all();\n+};\n+\n+package.process = function(plugins_dir, project_dir, platform) {\n+    var munger = new PlatformMunger(platform, project_dir, plugins_dir);\n+    munger.process();\n+    munger.save_all();\n+};\n+/******************************************************************************/\n+\n+\n+package.add_installed_plugin_to_prepare_queue = add_installed_plugin_to_prepare_queue;\n+function add_installed_plugin_to_prepare_queue(plugins_dir, plugin, platform, vars, is_top_level) {\n+    checkPlatform(platform);\n+    var config = module.exports.get_platform_json(plugins_dir, platform);\n+    config.prepare_queue.installed.push({'plugin':plugin, 'vars':vars, 'topLevel':is_top_level});\n+    module.exports.save_platform_json(config, plugins_dir, platform);\n+}\n+\n+package.add_uninstalled_plugin_to_prepare_queue = add_uninstalled_plugin_to_prepare_queue;\n+function add_uninstalled_plugin_to_prepare_queue(plugins_dir, plugin, platform, is_top_level) {\n+    checkPlatform(platform);\n+\n+    var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, plugin, 'plugin.xml'));\n+    var config = module.exports.get_platform_json(plugins_dir, platform);\n+    config.prepare_queue.uninstalled.push({'plugin':plugin, 'id':plugin_xml._root.attrib['id'], 'topLevel':is_top_level});\n+    module.exports.save_platform_json(config, plugins_dir, platform);\n+}\n+\n+\n+/******************************************************************************\n+* PlatformMunger class\n+*\n+* Can deal with config file of a single project.\n+* Parsed config files are cached in a ConfigKeeper object.\n+******************************************************************************/\n+function PlatformMunger(platform, project_dir, plugins_dir) {\n+    checkPlatform(platform);\n+    this.platform = platform;\n+    this.project_dir = project_dir;\n+    this.plugins_dir = plugins_dir;\n+    this.platform_handler = platforms[platform];\n+    this.config_keeper = new ConfigKeeper();\n+}\n+\n+// Write out all unsaved files.\n+PlatformMunger.prototype.save_all = PlatformMunger_save_all;\n+function PlatformMunger_save_all() {\n+    this.config_keeper.save_all();\n+}\n+\n+// Apply a munge object to a single config file.\n+// The remove parameter tells whether to add the change or remove it.\n+PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;\n+function PlatformMunger_apply_file_munge(file, munge, remove) {\n+    var self = this;\n+    var xml_child;\n+\n+    if ( file === 'framework' && self.platform === 'ios' ) {\n+        // ios pbxproj file\n+        var pbxproj = self.config_keeper.get(self.project_dir, self.platform, 'framework');\n+        for (var src in munge) {\n+            for (xml_child in munge[src]) {\n+                // Only add the framework if it's not a cordova-ios core framework\n+                if (keep_these_frameworks.indexOf(src) == -1) {\n+                    // xml_child in this case is whether the framework should use weak or not\n+                    if (remove) {\n+                        pbxproj.data.removeFramework(src);\n+                    } else {\n+                        pbxproj.data.addFramework(src, {weak: (xml_child === 'true')});\n+                    }\n+                    pbxproj.is_changed = true;\n                 }\n             }\n-            if (global_munge['framework'] && config_munge['framework']) {\n-                pbxproj = ios_parser.parseProjectFile(project_dir);\n-            }\n         }\n-\n-        // Traverse config munge and decrement global munge\n-        Object.keys(config_munge).forEach(function(file) {\n-            if (file == 'plugins-plist' && platform == 'ios') {\n-                // Handle plist files in ios\n-                if (global_munge[file]) {\n-                    Object.keys(config_munge[file]).forEach(function(key) {\n-                        if (global_munge[file][key] && plistObj) {\n-                            // TODO: REMOVE in 3.4 (https://issues.apache.org/jira/browse/CB-4456)\n-                            require('../../plugman').emit('warn', 'DEPRECATION WARNING: Plugin \"' + plugin_id + '\" uses <plugins-plist> element(s), which are now deprecated. Support will be removed in Cordova 3.4.');\n-                            delete plistObj.Plugins[key];\n-                            // TODO: don't write out on every change, do it once.\n-                            fs.writeFileSync(plistfile, plist.build(plistObj));\n-                            delete global_munge[file][key];\n-                        }\n-                    });\n+    } else {\n+        // all other types of files\n+        for (var selector in munge) {\n+            for (xml_child in munge[selector]) {\n+                // this xml child is new, graft it (only if config file exists)\n+                var config_file = self.config_keeper.get(self.project_dir, self.platform, file);\n+                if (config_file.exists) {\n+                    if (remove) config_file.prune_child(selector, xml_child);\n+                    else config_file.graft_child(selector, xml_child);\n                 }\n-            } else if (global_munge[file]) {\n-                // Handle arbitrary XML/pbxproj changes\n-                var is_framework = (platform == 'ios' && file == 'framework');\n-                Object.keys(config_munge[file]).forEach(function(selector) {\n-                    if (global_munge[file][selector]) {\n-                        Object.keys(config_munge[file][selector]).forEach(function(xml_child) {\n-                            if (global_munge[file][selector][xml_child]) {\n-                                if (should_decrement) {\n-                                    global_munge[file][selector][xml_child] -= 1;\n-                                }\n-                                if (global_munge[file][selector][xml_child] === 0) {\n-                                    if (is_framework) {\n-                                        // this is a .framework reference in ios files\n-                                        // We also need to keep some frameworks core to cordova-ios\n-                                        if (keep_these_frameworks.indexOf(selector) == -1) {\n-                                            pbxproj.xcode.removeFramework(selector); // in this case the 2nd-level key is the src attrib of <framework> els\n-                                            // TODO: dont write on every loop eh\n-                                            fs.writeFileSync(pbxproj.pbx, pbxproj.xcode.writeSync());\n-                                        }\n-                                    } else {\n-                                        // this xml child is no longer necessary, prune it\n-                                        // config.xml referenced in ios config changes refer to the project's config.xml, which we need to glob for.\n-                                        var filepath = resolveConfigFilePath(project_dir, platform, file);\n-                                        if (fs.existsSync(filepath)) {\n-\n-                                            // look at ext and do proper config change based on file type\n-                                            var ext = path.extname(filepath);\n-                                            // Windows8 uses an appxmanifest, and wp8 will likely use\n-                                            // the same in a future release\n-                                            // TODO: consider proper xml file detection, via <?xml version='1.0' encoding='utf-8'?>\n-                                            if (ext == '.xml' || ext == '.appxmanifest') {\n-                                                var xml_to_prune = [et.XML(xml_child)];\n-                                                var doc = xml_helpers.parseElementtreeSync(filepath);\n-                                                if (xml_helpers.pruneXML(doc, xml_to_prune, selector)) {\n-                                                    // were good, write out the file!\n-                                                    // TODO: don't write out on every change, do it once.\n-                                                    fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8');\n-                                                } else {\n-                                                    // uh oh\n-                                                    throw new Error('pruning xml at selector \"' + selector + '\" from \"' + filepath + '\" during config uninstall went bad :(');\n-                                                }\n-                                            } else {\n-                                                // plist file\n-                                                var pl = (isBinaryPlist(filepath) ? bplist : plist);\n-                                                var plistObj = pl.parseFileSync(filepath);\n-                                                if (plist_helpers.prunePLIST(plistObj, xml_child, selector)) {\n-                                                    fs.writeFileSync(filepath, plist.build(plistObj));\n-                                                } else {\n-                                                    throw new Error('grafting to plist \"' + filepath + '\" during config install went bad :(');\n-                                                }\n-                                            }\n-                                        }\n-                                    }\n-                                    delete global_munge[file][selector][xml_child];\n-                                }\n-                            }\n-                        });\n-                    }\n-                });\n             }\n-        });\n-        platform_config.config_munge = global_munge;\n+        }\n+    }\n+}\n \n-        // Remove from installed_plugins\n-        if (is_top_level) {\n-            delete platform_config.installed_plugins[plugin_id]\n-        } else {\n-            delete platform_config.dependent_plugins[plugin_id]\n+\n+PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;\n+function remove_plugin_changes(plugin_name, plugin_id, is_top_level) {\n+    var self = this;\n+    var platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);\n+    var plugin_dir = path.join(self.plugins_dir, plugin_name);\n+    var plugin_vars = (is_top_level ? platform_config.installed_plugins[plugin_id] : platform_config.dependent_plugins[plugin_id]);\n+\n+    // get config munge, aka how did this plugin change various config files\n+    var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars);\n+    // global munge looks at all plugins' changes to config files\n+    var global_munge = platform_config.config_munge;\n+    var munge = decrement_munge(global_munge, config_munge);\n+\n+    for (var file in munge) {\n+        if (file == 'plugins-plist' && self.platform == 'ios') {\n+            // TODO: remove this check and <plugins-plist> sections in spec/plugins/../plugin.xml files.\n+            events.emit(\n+                'warn',\n+                'WARNING: Plugin \"' + plugin_id + '\" uses <plugins-plist> element(s), ' +\n+                'which are no longer supported. Support has been removed as of Cordova 3.4.'\n+            );\n+            continue;\n         }\n+        self.apply_file_munge(file, munge[file], /* remove = */ true);\n+    }\n \n-        // save\n-        module.exports.save_platform_json(platform_config, plugins_dir, platform);\n-    },\n-    add_plugin_changes:function(platform, project_dir, plugins_dir, plugin_id, plugin_vars, is_top_level, should_increment, cache) {\n-        var platform_config = module.exports.get_platform_json(plugins_dir, platform);\n-        var plugin_dir = path.join(plugins_dir, plugin_id);\n-\n-        plugin_id = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'), 'utf-8')._root.attrib['id'];\n-\n-        // get config munge, aka how should this plugin change various config files\n-        var config_munge = module.exports.generate_plugin_config_munge(plugin_dir, platform, project_dir, plugin_vars);\n-        // global munge looks at all plugins' changes to config files\n-        var global_munge = platform_config.config_munge;\n-\n-        var plistObj;\n-        // Cache some slow stuff for reuse with multiple plugins.\n-        cache = cache || {};\n-        var pbxproj = cache.pbxproj;\n-\n-        if (platform == 'ios') {\n-            if (config_munge['plugins-plist']) {\n-                var plistfile = glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'));\n-                if (plistfile.length > 0) {\n-                    plistfile = plistfile[0];\n-                    // determine if this is a binary or ascii plist and choose the parser\n-                    // this is temporary until binary support is added to node-plist\n-                    var pl = (isBinaryPlist(plistfile) ? bplist : plist);\n-                    plistObj = pl.parseFileSync(plistfile);\n-                }\n-            }\n-            if (config_munge['framework']) {\n-                if (!pbxproj) {\n-                    // Note, parseProjectFile() is slow, ~250ms on MacBook Pro 2013.\n-                    cache.pbxproj = pbxproj = ios_parser.parseProjectFile(project_dir);\n-                }\n-            }\n+    // Remove from installed_plugins\n+    if (is_top_level) {\n+        delete platform_config.installed_plugins[plugin_id];\n+    } else {\n+        delete platform_config.dependent_plugins[plugin_id];\n+    }\n+\n+    // save\n+    module.exports.save_platform_json(platform_config, self.plugins_dir, self.platform);\n+}\n+\n+\n+PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;\n+function add_plugin_changes(plugin_id, plugin_vars, is_top_level, should_increment) {\n+    var self = this;\n+    var platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);\n+    var plugin_dir = path.join(self.plugins_dir, plugin_id);\n+\n+    plugin_id = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'), 'utf-8')._root.attrib['id'];\n+\n+    // get config munge, aka how should this plugin change various config files\n+    var config_munge = self.generate_plugin_config_munge(plugin_dir, plugin_vars);\n+    // global munge looks at all plugins' changes to config files\n+\n+    // TODO: The should_increment param is only used by cordova-cli and is going away soon.\n+    // If should_increment is set to false, avoid modifying the global_munge (use clone)\n+    // and apply the entire config_munge because it's already a proper subset of the global_munge.\n+    var munge, global_munge;\n+    if (should_increment) {\n+        global_munge = platform_config.config_munge;\n+        munge = increment_munge(global_munge, config_munge);\n+    } else {\n+        global_munge = clone_munge(platform_config.config_munge);\n+        munge = config_munge;\n+    }\n+\n+    for (var file in munge) {\n+        // TODO: remove this warning some time after 3.4 is out.\n+        if (file == 'plugins-plist' && self.platform == 'ios') {\n+            events.emit(\n+                'warn',\n+                'WARNING: Plugin \"' + plugin_id + '\" uses <plugins-plist> element(s), ' +\n+                'which are no longer supported. Support has been removed as of Cordova 3.4.'\n+            );\n+            continue;\n         }\n+        self.apply_file_munge(file, munge[file]);\n+    }\n \n-        // Traverse config munge and decrement global munge\n-        Object.keys(config_munge).forEach(function(file) {\n-            if (!global_munge[file]) {\n-                global_munge[file] = {};\n-            }\n-            var is_framework = (platform == 'ios' && file == 'framework');\n-            Object.keys(config_munge[file]).forEach(function(selector) {\n-                // Handle plist files on ios.\n-                if (file == 'plugins-plist' && platform == 'ios') {\n-                    var key = selector;\n-                    if (!global_munge[file][key] && plistObj) {\n-                        // TODO: REMOVE in 3.4 (https://issues.apache.org/jira/browse/CB-4456)\n-                        require('../../plugman').emit('warn', 'DEPRECATION WARNING: Plugin \"' + plugin_id + '\" uses <plugins-plist> element(s), which are now deprecated. Support will be removed in Cordova 3.4.');\n-                        // this key does not exist, so add it to plist\n-                        global_munge[file][key] = config_munge[file][key];\n-                        plistObj.Plugins[key] = config_munge[file][key];\n-                        // TODO: dont write on every loop eh\n-                        fs.writeFileSync(plistfile, plist.build(plistObj));\n-                    }\n-                } else {\n-                    // Handle arbitrary XML OR pbxproj framework stuff\n-                    if (!global_munge[file][selector]) {\n-                        global_munge[file][selector] = {};\n-                    }\n-                    Object.keys(config_munge[file][selector]).forEach(function(xml_child) {\n-                        if (!global_munge[file][selector][xml_child]) {\n-                            global_munge[file][selector][xml_child] = 0;\n-                        }\n-                        if (should_increment) {\n-                            global_munge[file][selector][xml_child] += 1;\n-                        }\n-                        if (global_munge[file][selector][xml_child] == 1) {\n-                            if (is_framework) {\n-                                var src = selector; // 2nd-level leaves are src path\n-                                // Only add the framework if it's not a cordova-ios core framework\n-                                if (keep_these_frameworks.indexOf(src) == -1) {\n-                                    // xml_child in this case is whether the framework should use weak or not\n-                                    var opt = {weak: (xml_child != 'true' ? false : true)};\n-                                    pbxproj.xcode.addFramework(src, opt);\n-                                    pbxproj.needs_write = true;\n-                                }\n-                            } else {\n-                                // this xml child is new, graft it (only if config file exists)\n-                                // config file may be in a place not exactly specified in the target\n-                                var filepath = resolveConfigFilePath(project_dir, platform, file);\n-\n-                                if (fs.existsSync(filepath)) {\n-\n-                                    // look at ext and do proper config change based on file type\n-                                    var ext = path.extname(filepath);\n-                                    // Windows8 uses an appxmanifest, and wp8 will likely use\n-                                    // the same in a future release\n-                                    // TODO: consider proper xml file detection, via <?xml version='1.0' encoding='utf-8'?>\n-                                    if (ext == '.xml' || ext == '.appxmanifest') {\n-                                        var xml_to_graft = [et.XML(xml_child)];\n-                                        // TODO: could parse the filepath once per unique target instead of on every change\n-                                        var doc = xml_helpers.parseElementtreeSync(filepath);\n-                                        if (xml_helpers.graftXML(doc, xml_to_graft, selector)) {\n-                                            // were good, write out the file!\n-                                            fs.writeFileSync(filepath, doc.write({indent: 4}), 'utf-8');\n-                                        } else {\n-                                            // uh oh\n-                                            throw new Error('grafting xml at selector \"' + selector + '\" from \"' + filepath + '\" during config install went bad :(');\n-                                        }\n-                                    } else {\n-                                        // plist file\n-                                        var pl = (isBinaryPlist(filepath) ? bplist : plist);\n-                                        // TODO: could parse the filepath once per unique target instead of on every change\n-                                        var plistObj = pl.parseFileSync(filepath);\n-                                        if (plist_helpers.graftPLIST(plistObj, xml_child, selector)) {\n-                                            var regExp = new RegExp(\"<string>[ \\t\\r\\n]+?</string>\", \"g\");\n-                                            fs.writeFileSync(filepath, plist.build(plistObj).replace(regExp, \"<string></string>\"));\n-                                        } else {\n-                                            throw new Error('grafting to plist \"' + filepath + '\" during config install went bad :(');\n-                                        }\n-                                    }\n-                                } else {\n-                                    // TODO: ignore if file doesnt exist?\n-                                }\n-                            }\n-                        }\n-                    });\n+    // Move to installed_plugins if it is a top-level plugin\n+    if (is_top_level) {\n+        platform_config.installed_plugins[plugin_id] = plugin_vars || {};\n+    } else {\n+        platform_config.dependent_plugins[plugin_id] = plugin_vars || {};\n+    }\n+\n+    // save\n+    module.exports.save_platform_json(platform_config, self.plugins_dir, self.platform);\n+}\n+\n+\n+// Load the global munge from platform json and apply all of it.\n+// Used by cordova prepare to re-generate some config file from platform\n+// defaults and the global munge.\n+PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;\n+function reapply_global_munge () {\n+    var self = this;\n+\n+    var platform_config = module.exports.get_platform_json(self.plugins_dir, self.platform);\n+    var global_munge = platform_config.config_munge;\n+    for (var file in global_munge) {\n+        // TODO: remove this warning some time after 3.4 is out.\n+        if (file == 'plugins-plist' && self.platform == 'ios') {\n+            events.emit(\n+                'warn',\n+                'WARNING: One of your plugins uses <plugins-plist> element(s), ' +\n+                'which are no longer supported. Support has been removed as of Cordova 3.4.'\n+            );\n+            continue;\n+        }\n+        // TODO: This is mostly file IO and can run in parallel since each file is independent.\n+        self.apply_file_munge(file, global_munge[file]);\n+    }\n+}\n+\n+\n+// generate_plugin_config_munge\n+// Generate the munge object from plugin.xml + vars\n+PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;\n+function generate_plugin_config_munge(plugin_dir, vars) {\n+    var self = this;\n+\n+    vars = vars || {};\n+    // Add PACKAGE_NAME variable into vars\n+    if (!vars['PACKAGE_NAME']) {\n+        vars['PACKAGE_NAME'] = self.platform_handler.package_name(self.project_dir);\n+    }\n+\n+    var munge = {};\n+    var plugin_xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'));","path":"src/util/config-changes.js","position":null,"original_position":606,"commit_id":"b116909e12713987b2a2c4eac7dbf52ca7dbbbe4","original_commit_id":"8434c17a0f5f672c39f00b19c8a784957f06b3e3","user":{"login":"kamrik","id":3289730,"avatar_url":"https://gravatar.com/avatar/abb03b79ed8712824503dd76e5adb7d9?d=https%3A%2F%2Fidenticons.github.com%2F76bc73e9740a26d1e93150f8bb20aa6f.png&r=x","gravatar_id":"abb03b79ed8712824503dd76e5adb7d9","url":"https://api.github.com/users/kamrik","html_url":"https://github.com/kamrik","followers_url":"https://api.github.com/users/kamrik/followers","following_url":"https://api.github.com/users/kamrik/following{/other_user}","gists_url":"https://api.github.com/users/kamrik/gists{/gist_id}","starred_url":"https://api.github.com/users/kamrik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kamrik/subscriptions","organizations_url":"https://api.github.com/users/kamrik/orgs","repos_url":"https://api.github.com/users/kamrik/repos","events_url":"https://api.github.com/users/kamrik/events{/privacy}","received_events_url":"https://api.github.com/users/kamrik/received_events","type":"User","site_admin":false},"body":"Done. Except in add_uninstalled_plugin_to_prepare_queue() which is not a method of PlatformMunger and should go away in some future refactoring round.","created_at":"2014-02-18T22:30:10Z","updated_at":"2014-02-18T22:30:10Z","html_url":"https://github.com/apache/cordova-plugman/pull/49#discussion_r9845316","pull_request_url":"https://api.github.com/repos/apache/cordova-plugman/pulls/49","_links":{"self":{"href":"https://api.github.com/repos/apache/cordova-plugman/pulls/comments/9845316"},"html":{"href":"https://github.com/apache/cordova-plugman/pull/49#discussion_r9845316"},"pull_request":{"href":"https://api.github.com/repos/apache/cordova-plugman/pulls/49"}}}}