title: Cordova config.xml environment variables
body: <h1>The Scenario:</h1>
  
  <p>I'm building an <code>Ionic3</code> application, and my <code>config.xml</code> have some data that I want to be able to change according to my environment (for example, I want that my facebook app id have different values for development, staging and production).</p>
  
  <p>I achieved this creating a template <code>config.xml</code> (the file is <code>config.tpl.xml</code>) and a <code>before_prepare</code> <code>cordova</code> hook to replace the variables in the template with the correct values and save the generated content in <code>config.xml</code>.</p>
  
  <p>The <code>cordova</code> hook uses the <code>npm</code> package <a href="https://www.npmjs.com/package/es6-template-strings" rel="noreferrer"><code>es6-template-strings</code></a>:</p>
  
  <pre><code>npm install es6-template-strings --save-dev
  </code></pre>
  
  <p><em>The hook is:</em></p>
  
  <pre class="lang-js prettyprint-override"><code>#!/usr/bin/env node
  var fs = require('fs');
  var path = require('path');
  var compile = require('es6-template-strings/compile');
  var resolveToString = require('es6-template-strings/resolve-to-string');
  
  var ROOT_DIR = process.argv[2];
  var FILES = {
      SRC: "config.tpl.xml",
      DEST: "config.xml"
  };
  
  var env = process.env.NODE_ENV || 'dev';
  var envFile = 'src/environments/environment.' + env + '.json';
  
  var srcFileFull = path.join(ROOT_DIR, FILES.SRC);
  var destFileFull = path.join(ROOT_DIR, FILES.DEST);
  var configFileFull = path.join(ROOT_DIR, envFile);
  
  var templateData = fs.readFileSync(srcFileFull, 'utf8');
  
  var configData = fs.readFileSync(configFileFull, 'utf8');
  var config = JSON.parse(configData);
  
  var compiled = compile(templateData);
  var content = resolveToString(compiled, config);
  
  fs.writeFileSync(destFileFull, content);
  </code></pre>
  
  <p>I have files in the <code>src/environments/</code> directory for different environments, that are chosen based on the <code>NODE_ENV</code> value that is defined when I build <code>cordova</code>. For example, if <code>NODE_ENV=prod</code> (production), then it would use the file <code>environment.prod.json</code>:</p>
  
  <pre class="lang-json prettyprint-override"><code>{
      ...
      "FACEBOOK_APP_ID": "11111111",
      "FACEBOOK_APP_NAME": "My Facebook App Name",
      ...
      "PUSH_SENDER_ID": "22222222",
      ...
  }
  </code></pre>
  
  <p>When the hook is executed, this part in the <code>cordova.tpl.xml</code>:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;plugin name="cordova-plugin-facebook4" spec="~1.7.4"&gt;
      &lt;variable name="APP_ID" value="${FACEBOOK_APP_ID}" /&gt;
      &lt;variable name="APP_NAME" value="${FACEBOOK_APP_NAME}" /&gt;
  &lt;/plugin&gt;
  &lt;plugin name="phonegap-plugin-push" spec="~1.9.2"&gt;
      &lt;variable name="SENDER_ID" value="${PUSH_SENDER_ID}" /&gt;
  &lt;/plugin&gt;
  </code></pre>
  
  <p>becomes like:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;plugin name="cordova-plugin-facebook4" spec="~1.7.4"&gt;
      &lt;variable name="APP_ID" value="11111111" /&gt;
      &lt;variable name="APP_NAME" value="My Facebook App Name" /&gt;
  &lt;/plugin&gt;
  &lt;plugin name="phonegap-plugin-push" spec="~1.9.2"&gt;
      &lt;variable name="SENDER_ID" value="22222222" /&gt;
  &lt;/plugin&gt;
  </code></pre>
  
  <h1>The problem:</h1>
  
  <p>So far so good. The problem is that when some automatic changes are done to <code>config.xml</code> (like adding plugins), it is not reflected in <code>cordova.tpl.xml</code>, so I have to remember to make the changes manually.</p>
  
  <p>Although the way it is done now is still much better than having to add each environment variable like before (it was a maintenance nightmare and error prone), I still have to change the template every-time a add/change/remove a plugin (not so frequent, and easy to discover the problem when I forget it, but still far from the ideal).</p>
  
  <h1>My question:</h1>
  
  <p>I would like to know if there is a way avoid these manual changes, but keep using the environment variables like I'm doing now.</p>
  
  <p>It could be achieved making the automatic changes to <code>config.xml</code> to be done to <code>config.tpl.xml</code> instead (like when adding a plugin with <code>--save</code>), if possible, but I haven't found any <code>cordova</code> option about this.</p>
  
  <p>Or using the <code>config.xml</code> as the template and send it to another place with the variables defined (e.g. to <code>www/config.xml</code>), and use the <code>config.xml</code> in the other location to be used to build the app (not the <code>config.xml</code> in the root, i.e., the template). I would only change the <code>src</code> and <code>dest</code> files in my hook (to <code>config.xml</code> and <code>www/config.xml</code>, respectively). But I also haven't found a way to achieve this.</p>
  
  <p>Any thoughts about this?</p>
  
  <p><em>(It doesn't need to be an Ionic specific solution.)</em></p>
  
  <h1>Update (2017-10-13)</h1>
  
  <p>Based on <a href="https://stackoverflow.com/a/46698231/4850646">Bobby</a>'s answer, I achieved what I wanted both when installing and uninstalling a plugin. I've created 4 hooks: <code>after_plugin_add</code>, <code>after_plugin_rm</code>, <code>before_plugin_add</code>, <code>before_plugin_rm</code>.</p>
  
  <p>The <code>before</code> hooks copy the template (<code>config.tpl.xml</code>) into the <code>config.xml</code> file and the <code>after</code> hooks do the inverse.</p>
  
  <p>The <code>before_plugin_add</code> and <code>before_plugin_rm</code> hooks are as follows:</p>
  
  <pre class="lang-javascript prettyprint-override"><code>#!/usr/bin/env node
  var fs = require('fs');
  var path = require('path');
  
  var ROOT_DIR = process.argv[2];
  var FILES = {
      SRC: 'config.tpl.xml',
      DEST: 'config.xml'
  };
  
  var srcFileFull = path.join(ROOT_DIR, FILES.SRC);
  var destFileFull = path.join(ROOT_DIR, FILES.DEST);
  
  var templateData = fs.readFileSync(srcFileFull, 'utf8');
  fs.writeFileSync(destFileFull, templateData);
  </code></pre>
  
  <p>The <code>after_plugin_add</code> and <code>after_plugin_rm</code> hooks are almost identical, just swapping <code>FILES.SRC</code> and <code>FILES.DEST</code> values.</p>