New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment proposal #762

Closed
danbucholtz opened this Issue Feb 17, 2017 · 120 comments

Comments

Projects
None yet
@danbucholtz
Contributor

danbucholtz commented Feb 17, 2017

I am thinking we could do this:

  1. Introduce a new flag that gets passed in like this:

ionic serve --env qa or ionic run android --env prod

My first thought was that it's value would default to dev for non-prod builds, and prod for prod builds. Developer's can pass in whatever they want, though.

  1. Based on the value that is passed in (or inferred), the config section of the package.json is read. It could look something like this:
...

"config": {
   "ionic_env" : {
       "dev" : {
          "keyToReplace" : "devValueToReplaceWith",
          "keyTwoToReplace" : "devValueTwoToReplaceWith"
       },
       "qa" : {
          "keyToReplace" : "qaValueToReplaceWith",
          "keyTwoToReplace" : "qaValueTwoToReplaceWith"
       }
   }
},
...

If the ionic_env data is not there, we would just move on in the build process. If it is present, we would then perform the text replacement.

  1. Any computed values, sync or async, could be replaced after the build is done. We could either leave this up to the user to add on to the npm script section, or we could provide a hook into the postprocess step. I prefer the latter as it's easier to document and and we can probably make it a 1/2 second faster or so if we do it in app-scripts.

Feedback is appreciated.

Thanks,
Dan

@fiznool

This comment has been minimized.

Show comment
Hide comment
@fiznool

fiznool Feb 17, 2017

Contributor

Looks interesting! I have a few questions.

Any computed values, sync or async, could be replaced after the build is done.

Could you expand on this? I didn't quite understand what you mean.

How would the app use these variables, what would the code look like?

One of the comments in another thread mentioned keeping sensitive values out of source control. Could there be a way of reading in a value from a process.env variable to satisfy this requirement?

Contributor

fiznool commented Feb 17, 2017

Looks interesting! I have a few questions.

Any computed values, sync or async, could be replaced after the build is done.

Could you expand on this? I didn't quite understand what you mean.

How would the app use these variables, what would the code look like?

One of the comments in another thread mentioned keeping sensitive values out of source control. Could there be a way of reading in a value from a process.env variable to satisfy this requirement?

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Feb 17, 2017

Contributor

@danbucholtz this looks great. Angular CLI has a similar implementation.

They have a --target which you can use to tell the CLI to either do a production build (AOT, minification, gzip, etc.) or a dev build. Then there's the --environment flag which we can use to choose the appropriate environment.

I'd disagree with the ionic_env working as proposed though. As you also pointed out, there might be a case when we need to compute some values, and that'd be difficult for some users to figure out. How about the following ionic_env:

"ionic_env": {
    "prod": "config/env.prod.js",
    "staging": "config/env.staging.js",
    "dev": "config/env.dev.js"
}

And each file would export the following:

const somePackage = require('somePackage');

module.exports = {
    SOME_VAR: somePackage.getSomeValueSync(),
    SOME_ASYNC_VAL: async function getSomeValueAsync() => {
        await asyncValue = somePackage.computeSomeValueAsync();
        return asyncValue;
    },
    VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL
};

Then @ionic/app-scripts would require the appropriate env based on the config from ionic_env and for each key it will:

  • check if the value is primitive and use that if so
  • or if it's a function it would invoke and
    • check if the return value is primitive and use it if so
    • if it's a Promise or an Observable, wait until it's resolved or emits and return that
  • then after it successfully computed each value, it would replace where necessary

Perhaps there could be a better approach, but I thing this would work in most scenarios.

Contributor

rolandjitsu commented Feb 17, 2017

@danbucholtz this looks great. Angular CLI has a similar implementation.

They have a --target which you can use to tell the CLI to either do a production build (AOT, minification, gzip, etc.) or a dev build. Then there's the --environment flag which we can use to choose the appropriate environment.

I'd disagree with the ionic_env working as proposed though. As you also pointed out, there might be a case when we need to compute some values, and that'd be difficult for some users to figure out. How about the following ionic_env:

"ionic_env": {
    "prod": "config/env.prod.js",
    "staging": "config/env.staging.js",
    "dev": "config/env.dev.js"
}

And each file would export the following:

const somePackage = require('somePackage');

module.exports = {
    SOME_VAR: somePackage.getSomeValueSync(),
    SOME_ASYNC_VAL: async function getSomeValueAsync() => {
        await asyncValue = somePackage.computeSomeValueAsync();
        return asyncValue;
    },
    VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL
};

Then @ionic/app-scripts would require the appropriate env based on the config from ionic_env and for each key it will:

  • check if the value is primitive and use that if so
  • or if it's a function it would invoke and
    • check if the return value is primitive and use it if so
    • if it's a Promise or an Observable, wait until it's resolved or emits and return that
  • then after it successfully computed each value, it would replace where necessary

Perhaps there could be a better approach, but I thing this would work in most scenarios.

@fiznool

This comment has been minimized.

Show comment
Hide comment
@fiznool

fiznool Feb 17, 2017

Contributor

Perhaps I'm missing something, but what sort of use cases would benefit from async computation of values?

Contributor

fiznool commented Feb 17, 2017

Perhaps I'm missing something, but what sort of use cases would benefit from async computation of values?

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Feb 17, 2017

Contributor

Reading a file for instance (that's also possible to do sync). Or perhaps you store some keys in an AWS bucket and you'd like to pull those keys when you compile the app. I could think of plenty of use cases.

Contributor

rolandjitsu commented Feb 17, 2017

Reading a file for instance (that's also possible to do sync). Or perhaps you store some keys in an AWS bucket and you'd like to pull those keys when you compile the app. I could think of plenty of use cases.

@fiznool

This comment has been minimized.

Show comment
Hide comment
@fiznool

fiznool Feb 17, 2017

Contributor

Right, I'm with you now. 👍

One approach I've seen in other places is to allow exporting a function as well as an object inside the configuration file. This could simplify the logic from the app-scripts end and allow you to build the config object dynamically, and only return once you are ready.

Something along these lines:

// sync example
const somePackage = require('somePackage');

module.exports = {
  SOME_VAR: somePackage.getSomeValueSync();
  VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL;
};
// async example
const somePackage = require('somePackage');

module.exports = function() {
  const config = {
    SOME_VAR: somePackage.getSomeValueSync();
    VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL;
  };
  
  return somePackage.computeSomeValueAsync().then(val => {
    config.SOME_ASYNC_VAL = val;
    return config;
  });
};

app-scripts would then perform the following

  • If the export is an object, assume it is the config object and use it.
  • If the export is a function, assume it will return a promise. Call it, wait for it to be resolved and use the resolved value as the config object.

This has the benefit that app-scripts doesn't need to enumerate all the keys in the config object (what about nested keys?) and provides the most flexibility in your config script (what if the fetching of a second async value depends upon another one being resolved first?)

Contributor

fiznool commented Feb 17, 2017

Right, I'm with you now. 👍

One approach I've seen in other places is to allow exporting a function as well as an object inside the configuration file. This could simplify the logic from the app-scripts end and allow you to build the config object dynamically, and only return once you are ready.

Something along these lines:

// sync example
const somePackage = require('somePackage');

module.exports = {
  SOME_VAR: somePackage.getSomeValueSync();
  VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL;
};
// async example
const somePackage = require('somePackage');

module.exports = function() {
  const config = {
    SOME_VAR: somePackage.getSomeValueSync();
    VALUE_FROM_PROCESS: process.env.SOME_ENV_VAR_VAL;
  };
  
  return somePackage.computeSomeValueAsync().then(val => {
    config.SOME_ASYNC_VAL = val;
    return config;
  });
};

app-scripts would then perform the following

  • If the export is an object, assume it is the config object and use it.
  • If the export is a function, assume it will return a promise. Call it, wait for it to be resolved and use the resolved value as the config object.

This has the benefit that app-scripts doesn't need to enumerate all the keys in the config object (what about nested keys?) and provides the most flexibility in your config script (what if the fetching of a second async value depends upon another one being resolved first?)

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Feb 17, 2017

Contributor

@fiznool true, that'd be much simpler and it would help if a value depends on another one. But the app scripts would still need to enum keys in order to replace, but that's just implementation detail.

Contributor

rolandjitsu commented Feb 17, 2017

@fiznool true, that'd be much simpler and it would help if a value depends on another one. But the app scripts would still need to enum keys in order to replace, but that's just implementation detail.

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Feb 17, 2017

Contributor

Though I'd actually argue that if we export an object, app scripts could potentially run async tasks in parallel so if we'd have more than one property that evaluates to a promise/observable, it could run all of them at once and complete when all are done (something like a Promise.all([ ... ])/Observable.merge(...)).

Yet, the user could do this as well, but if app scripts would do it, it would spare the user of adding extra implementation.

I guess there are many ways this can be tackled, but the simpler the better.

Contributor

rolandjitsu commented Feb 17, 2017

Though I'd actually argue that if we export an object, app scripts could potentially run async tasks in parallel so if we'd have more than one property that evaluates to a promise/observable, it could run all of them at once and complete when all are done (something like a Promise.all([ ... ])/Observable.merge(...)).

Yet, the user could do this as well, but if app scripts would do it, it would spare the user of adding extra implementation.

I guess there are many ways this can be tackled, but the simpler the better.

@fiznool

This comment has been minimized.

Show comment
Hide comment
@fiznool

fiznool Feb 17, 2017

Contributor
Contributor

fiznool commented Feb 17, 2017

@ehorodyski

This comment has been minimized.

Show comment
Hide comment
@ehorodyski

ehorodyski Feb 17, 2017

I like where @rolandjitsu is going, but instead of tying it down to a specific implementation, is there a way you can take the environment path being used from package.json in the ionic_env object, and then alias it, so it's import { <T> } from @ionic/config or import { <T> } from @ionic/env?

I don't know how the Angular library does aliasing so you can do import { HTTP } from '@angular/http', instead of import { HTTP } from 'angular/modules/wherever/it/lives/http', but that's how I'd implement it.

Give the users the ultimate control of building what they want to export from their environment/config files, but provide a way for those paths to be swapped out during building, and I think properly creating a module alias, which I don't know how to do, is the best approach.

ehorodyski commented Feb 17, 2017

I like where @rolandjitsu is going, but instead of tying it down to a specific implementation, is there a way you can take the environment path being used from package.json in the ionic_env object, and then alias it, so it's import { <T> } from @ionic/config or import { <T> } from @ionic/env?

I don't know how the Angular library does aliasing so you can do import { HTTP } from '@angular/http', instead of import { HTTP } from 'angular/modules/wherever/it/lives/http', but that's how I'd implement it.

Give the users the ultimate control of building what they want to export from their environment/config files, but provide a way for those paths to be swapped out during building, and I think properly creating a module alias, which I don't know how to do, is the best approach.

@danbucholtz

This comment has been minimized.

Show comment
Hide comment
@danbucholtz

danbucholtz Feb 17, 2017

Contributor

If we were to expose a hook and allow the user to load in async values, perhaps we could do something like this:

"config" : {
   "ionic_env" : {
      "dev" : "./scripts/dev.config.js",
      "qa" : "./scripts/qa.config.js",
      "prod" : "./scripts/prod.config.js"
      ... etc ...
   }
}

Those modules could then export a function that returns a Promise. That way it will always be async.

It could work like this:

module.exports = function(ionicEnvironment: string) {
   return readSomeAsyncValue().then((result: string) => {
      return {
         'keyOneToReplace' : 'valueOne',
         'keyTwoToReplace' : result
      }
   });
}

Something like this would work in the vast majority of use cases.

In an application's code, let's say you need to hit a different HTTP service for development and production.

You could write a service like this:

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest('$BACKEND_SERVICE_URL');
   }
}

Your implementation to replace it could look like this:

module.exports = function() {
   return Promise.resolve({
      '$BACKEND_SERVICE_URL' : 'localhost:8080/myService'
   });
}
Contributor

danbucholtz commented Feb 17, 2017

If we were to expose a hook and allow the user to load in async values, perhaps we could do something like this:

"config" : {
   "ionic_env" : {
      "dev" : "./scripts/dev.config.js",
      "qa" : "./scripts/qa.config.js",
      "prod" : "./scripts/prod.config.js"
      ... etc ...
   }
}

Those modules could then export a function that returns a Promise. That way it will always be async.

It could work like this:

module.exports = function(ionicEnvironment: string) {
   return readSomeAsyncValue().then((result: string) => {
      return {
         'keyOneToReplace' : 'valueOne',
         'keyTwoToReplace' : result
      }
   });
}

Something like this would work in the vast majority of use cases.

In an application's code, let's say you need to hit a different HTTP service for development and production.

You could write a service like this:

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest('$BACKEND_SERVICE_URL');
   }
}

Your implementation to replace it could look like this:

module.exports = function() {
   return Promise.resolve({
      '$BACKEND_SERVICE_URL' : 'localhost:8080/myService'
   });
}
@ehorodyski

This comment has been minimized.

Show comment
Hide comment
@ehorodyski

ehorodyski Feb 17, 2017

@danbucholtz - I think that is really over-complicated.

Take a look at Webpack's resolve aliasing. This could be done dynamically when starting ionic serve or the other scripts, depending on the env variable:

alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
Now, instead of using relative paths when importing like so:

import Utility from '../../utilities/utility';

you can use the alias:

import Utility from 'Utilities/utility';

The only implication to the user is that they'd need to store their config files outside the folder with their source code, as to not include all the files defined. Or maybe not, you can run a glob that excludes the other environment files not being used. Plus, there's no resolution during application running.

@danbucholtz - I think that is really over-complicated.

Take a look at Webpack's resolve aliasing. This could be done dynamically when starting ionic serve or the other scripts, depending on the env variable:

alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
Now, instead of using relative paths when importing like so:

import Utility from '../../utilities/utility';

you can use the alias:

import Utility from 'Utilities/utility';

The only implication to the user is that they'd need to store their config files outside the folder with their source code, as to not include all the files defined. Or maybe not, you can run a glob that excludes the other environment files not being used. Plus, there's no resolution during application running.

@josh-m-sharpe

This comment has been minimized.

Show comment
Hide comment
@josh-m-sharpe

josh-m-sharpe Feb 17, 2017

For us this isn't necessarily just about env=dev/staging/prod. For some context, we're coming to ionic(v2) from a vanilla cordova app and we use the same codebase to generate multiple different apps primarily distinguished by an 'org_id'. (The app passes this value in server calls to receive different content, amongst a bunch of other differences). We also currently inject the internal and external version numbers since they vary (inconsistently) by org_id.

So, in a nutshell, just passing in "environment" wouldn't solve this problem for us. Why not make this feature align better with unix best practices and simply make all of the current env available inside the application somehow?

for example:

ORG_ID=123 ENVIRONMENT=staging ionic serve

For what it's worth, cordova (or node?) is doing this already automatically. That is, you can access 'process' inside a cordova hook without having to require anything. From that, it's just process.env.ORG_ID == '123'

josh-m-sharpe commented Feb 17, 2017

For us this isn't necessarily just about env=dev/staging/prod. For some context, we're coming to ionic(v2) from a vanilla cordova app and we use the same codebase to generate multiple different apps primarily distinguished by an 'org_id'. (The app passes this value in server calls to receive different content, amongst a bunch of other differences). We also currently inject the internal and external version numbers since they vary (inconsistently) by org_id.

So, in a nutshell, just passing in "environment" wouldn't solve this problem for us. Why not make this feature align better with unix best practices and simply make all of the current env available inside the application somehow?

for example:

ORG_ID=123 ENVIRONMENT=staging ionic serve

For what it's worth, cordova (or node?) is doing this already automatically. That is, you can access 'process' inside a cordova hook without having to require anything. From that, it's just process.env.ORG_ID == '123'

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Feb 17, 2017

Contributor

@danbucholtz I agree, that'd be a reasonable solution. And I'm sure that it would cover most use cases.

@ehorodyski I think it's already possible to use aliasing if you use your own webpack config file (I cannot confirm it, never tried it). But I do see what you mean, though that would not work if you have async operations that need to run in order to compute the env values you want to have available in the app. It would also not work for the use case @josh-m-sharpe describes where you need some env vars that are not exposed by app scripts.

@josh-m-sharpe using the solution proposed would also solve what you're referring to. You'd just need to expose from process.env:

module.exports = function() {
    // process.env is an object and has all the env vars
    return Promise.resolve(process.env);
}

I think it's essential to have a more generic and universal solution, such as the one proposed by @danbucholtz in #762 (comment), that can cover most of the uses cases since the dev needs vary as we can see from the posts on this topic.

Contributor

rolandjitsu commented Feb 17, 2017

@danbucholtz I agree, that'd be a reasonable solution. And I'm sure that it would cover most use cases.

@ehorodyski I think it's already possible to use aliasing if you use your own webpack config file (I cannot confirm it, never tried it). But I do see what you mean, though that would not work if you have async operations that need to run in order to compute the env values you want to have available in the app. It would also not work for the use case @josh-m-sharpe describes where you need some env vars that are not exposed by app scripts.

@josh-m-sharpe using the solution proposed would also solve what you're referring to. You'd just need to expose from process.env:

module.exports = function() {
    // process.env is an object and has all the env vars
    return Promise.resolve(process.env);
}

I think it's essential to have a more generic and universal solution, such as the one proposed by @danbucholtz in #762 (comment), that can cover most of the uses cases since the dev needs vary as we can see from the posts on this topic.

@biesbjerg

This comment has been minimized.

Show comment
Hide comment
@biesbjerg

biesbjerg Feb 17, 2017

Am I the only one that really wants hinting of config keys, e.g. by defining a ConfigInterface? I have not seen it mentioned and I'm not sure how to do it elegantly, though...

Am I the only one that really wants hinting of config keys, e.g. by defining a ConfigInterface? I have not seen it mentioned and I'm not sure how to do it elegantly, though...

@ehorodyski

This comment has been minimized.

Show comment
Hide comment
@ehorodyski

ehorodyski Feb 18, 2017

@biesbjerg - Right, this is what I'd like accomplished as well. I need to brush up on how to do Typescript module aliasing, so that the paths can be dynamically generated when you run ionic serve.

If ionic-app-scripts doesn't want to implement something like this, I think it would be great to use for yourself. It's really not hard, I've done it before.

ehorodyski commented Feb 18, 2017

@biesbjerg - Right, this is what I'd like accomplished as well. I need to brush up on how to do Typescript module aliasing, so that the paths can be dynamically generated when you run ionic serve.

If ionic-app-scripts doesn't want to implement something like this, I think it would be great to use for yourself. It's really not hard, I've done it before.

@coldAlphaMan

This comment has been minimized.

Show comment
Hide comment
@coldAlphaMan

coldAlphaMan Feb 18, 2017

@danbucholtz all of the other stuff people are mentioning are great nice to haves. That said, i think your first comment is awesome enough and will suit most needs.

@danbucholtz all of the other stuff people are mentioning are great nice to haves. That said, i think your first comment is awesome enough and will suit most needs.

@fiznool

This comment has been minimized.

Show comment
Hide comment
@fiznool

fiznool Feb 19, 2017

Contributor

@danbucholtz

You could write a service like this:

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest('$BACKEND_SERVICE_URL');
   }
}

Your implementation to replace it could look like this:

module.exports = function() {
   return Promise.resolve({
      '$BACKEND_SERVICE_URL' : 'localhost:8080/myService'
   });
}

Are you suggesting that '$BACKEND_SERVICE_URL' becomes a sort of 'magic string' that is replaced during compile time by the contents defined in the environment config file?

If so, I think this might be a bit confusing and difficult to maintain.

As others have suggested, an ideal solution would be something along the lines of the following, which could be achieved in webpack using resolve aliasing:

import { BACKEND_SERVICE_URL } from 'config';

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest(BACKEND_SERVICE_URL);
   }
}

I'm not sure how the above could be achieved with rollup. If this is a concern, a slightly lesser (but still very workable) version would be to add all config parameters to a global variable, e.g. ENV. With webpack, this could be achieved using the define plugin, and with rollup you could use the rollup replace plugin.

declare const ENV;

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest(ENV.BACKEND_SERVICE_URL);
   }
}

For those wishing for TypeScript autocomplete, ENV could instead be declared in src/declarations.d.ts as follows:

declare namespace ENV {
  const BACKEND_SERVER_URL: string;
  // Any other properties go here
}
Contributor

fiznool commented Feb 19, 2017

@danbucholtz

You could write a service like this:

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest('$BACKEND_SERVICE_URL');
   }
}

Your implementation to replace it could look like this:

module.exports = function() {
   return Promise.resolve({
      '$BACKEND_SERVICE_URL' : 'localhost:8080/myService'
   });
}

Are you suggesting that '$BACKEND_SERVICE_URL' becomes a sort of 'magic string' that is replaced during compile time by the contents defined in the environment config file?

If so, I think this might be a bit confusing and difficult to maintain.

As others have suggested, an ideal solution would be something along the lines of the following, which could be achieved in webpack using resolve aliasing:

import { BACKEND_SERVICE_URL } from 'config';

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest(BACKEND_SERVICE_URL);
   }
}

I'm not sure how the above could be achieved with rollup. If this is a concern, a slightly lesser (but still very workable) version would be to add all config parameters to a global variable, e.g. ENV. With webpack, this could be achieved using the define plugin, and with rollup you could use the rollup replace plugin.

declare const ENV;

export class MyService {
   construtor(public http: Http){
   }

   makeServiceCall() {
      return this.http.makeRequest(ENV.BACKEND_SERVICE_URL);
   }
}

For those wishing for TypeScript autocomplete, ENV could instead be declared in src/declarations.d.ts as follows:

declare namespace ENV {
  const BACKEND_SERVER_URL: string;
  // Any other properties go here
}
@danbucholtz

This comment has been minimized.

Show comment
Hide comment
@danbucholtz

danbucholtz Feb 20, 2017

Contributor

The resolve aliasing is interesting and wouldn't really require Webpack or Rollup to do it. From our perspective, a bundler is just a bundler, not a build tool. We don't dive into the world of plugins unless absolutely necessary, because they tighten our coupling to the tool. In our ideal world, we're going to swap out Webpack for a better bundler someday without 90% of our developers even noticing.

I think the aliasing leaves many use-cases unfulfilled where you need to load asynchronous data prior to a build. I guess you could do it ahead of time in a separate workflow/process and put it in an environment variable? I'm not sure, this seems a bit complicated to me.

I'll chat with some other team members this week and we'll figure out what we want to do. Any of these solutions will cover the vast majority of use cases.

Thanks,
Dan

Contributor

danbucholtz commented Feb 20, 2017

The resolve aliasing is interesting and wouldn't really require Webpack or Rollup to do it. From our perspective, a bundler is just a bundler, not a build tool. We don't dive into the world of plugins unless absolutely necessary, because they tighten our coupling to the tool. In our ideal world, we're going to swap out Webpack for a better bundler someday without 90% of our developers even noticing.

I think the aliasing leaves many use-cases unfulfilled where you need to load asynchronous data prior to a build. I guess you could do it ahead of time in a separate workflow/process and put it in an environment variable? I'm not sure, this seems a bit complicated to me.

I'll chat with some other team members this week and we'll figure out what we want to do. Any of these solutions will cover the vast majority of use cases.

Thanks,
Dan

@Ross-Rawlins

This comment has been minimized.

Show comment
Hide comment

+1

@ehorodyski

This comment has been minimized.

Show comment
Hide comment
@ehorodyski

ehorodyski Feb 20, 2017

I think the aliasing leaves many use-cases unfulfilled where you need to load asynchronous data prior to a build. I guess you could do it ahead of time in a separate workflow/process and put it in an environment variable? I'm not sure, this seems a bit complicated to me.

Could you provide some use-cases for where you'd load async data prior to a build -- that would be environmental configurations?

Maybe there's a disconnect in what certain people are talking about. In my view, and many others, this is analogous to having .properties files in a Java project. They'd stay in the repo and only the specific file would get included in the build. Would the Promise resolutions happen during run-time?

ehorodyski commented Feb 20, 2017

I think the aliasing leaves many use-cases unfulfilled where you need to load asynchronous data prior to a build. I guess you could do it ahead of time in a separate workflow/process and put it in an environment variable? I'm not sure, this seems a bit complicated to me.

Could you provide some use-cases for where you'd load async data prior to a build -- that would be environmental configurations?

Maybe there's a disconnect in what certain people are talking about. In my view, and many others, this is analogous to having .properties files in a Java project. They'd stay in the repo and only the specific file would get included in the build. Would the Promise resolutions happen during run-time?

@riltsken

This comment has been minimized.

Show comment
Hide comment
@riltsken

riltsken Feb 20, 2017

Just my perspective as a user, but I pretty much use environment variables for any configuration changes required among different environments or deploys. This means I can change a configuration value for prod, staging, dev, individual branch builds ... any N+ environments. Supporting N+ environments is easy with environment variables. Just use one template. When talking about property files though it becomes harder because now you need a property file per environment or to generate a property file per environment.

I'll post my specific project use cases that we have currently:

maxCacheAgeInDays
ionicCloudChannel
apiUrl
oauthClientId
logLevel
logentriesToken
googleMapsJavascriptApiUrl

In the individual threads I see this asked more than once for environment variable support. I could generate a property file I guess at buildtime, but that would basically be me duplicating what I am today in a different form. Below are some references to what I would consider are analogous solutions for the same problem.

references:

Anyway just some thoughts. I would really love environment variable support :).

Just my perspective as a user, but I pretty much use environment variables for any configuration changes required among different environments or deploys. This means I can change a configuration value for prod, staging, dev, individual branch builds ... any N+ environments. Supporting N+ environments is easy with environment variables. Just use one template. When talking about property files though it becomes harder because now you need a property file per environment or to generate a property file per environment.

I'll post my specific project use cases that we have currently:

maxCacheAgeInDays
ionicCloudChannel
apiUrl
oauthClientId
logLevel
logentriesToken
googleMapsJavascriptApiUrl

In the individual threads I see this asked more than once for environment variable support. I could generate a property file I guess at buildtime, but that would basically be me duplicating what I am today in a different form. Below are some references to what I would consider are analogous solutions for the same problem.

references:

Anyway just some thoughts. I would really love environment variable support :).

@vovikdrg

This comment has been minimized.

Show comment
Hide comment

Angular-cli has implemented and its easy to configure it in angular-cli file why not to do the same?

https://github.com/angular/angular-cli/blob/3c3f74c060cc79f3872230a18eb0c1dd8371065f/packages/%40angular/cli/blueprints/ng2/files/angular-cli.json#L24

@nottinhill

This comment has been minimized.

Show comment
Hide comment
@nottinhill

nottinhill Mar 8, 2017

We want this too!

We want this too!

@juarezpaf

This comment has been minimized.

Show comment
Hide comment
@juarezpaf

juarezpaf Mar 8, 2017

We're currently using a script that runs before ionic serve and generate the correct file for us.
Let me explain it in more details here:

  1. We created an env folder in the root of the Ionic project and added this folder to our .gitignore
  2. Inside of the env folder we have different files:
./env
  production.json
  sandbox.json
  staging.json
  1. Each file has the variables we need, for example, our staging.json file looks like:
{
  "api_url": "'https://staging.api.url'",
  "client_id_login": "'566d41f...'",
  "client_secret_login": "'511480878...'",
  "google_maps_key": "'tEkoJK...'"
}
  1. We changed the scripts section inside of the package.json file to have a preionic phase and also an independent command:
"scripts": {
    "ionic:build": "ionic-app-scripts build",
    "ionic:serve": "ionic-app-scripts serve",
    "preionic:build": "node ./scripts/replace.env",
    "preionic:serve": "node ./scripts/replace.env",
    "generate-env": "node ./scripts/replace.env",
    "test": "ng test"
}
  1. Our replace.env.js file handles the generations of our config.ts file https://gist.github.com/juarezpaf/7a9007dfcef25ec24f7de900df8fe04e
  2. The config.ts.sample is very simple and it'll just expose these variables to use use across our app
export class AppConfig {
  static get API() {
    return ENV.api_url;
  }

  static get GOOGLE_MAPS_KEY() {
    return ENV.google_maps_key;
  }

  static get CREDENTIALS() {
    const credentials = {
      'login': {
        'client_id': ENV.login.client_id,
        'client_secret': ENV.login.client_secret
      }
    }
    return credentials;
  }
}
  1. We use the AppConfig in our AuthService:
import { AppConfig } from '../config/config';

@Injectable()
export class Auth {
  credentials: any;
  private apiUrl = AppConfig.API;
  
  constructor(public http: Http) {
    credentials.client_id = AppConfig.CREDENTIALS.login.client_id;
    credentials.client_secret = AppConfig.CREDENTIALS.login.client_secret;
  }
}
  1. With everything in place we can use it in 3 different ways:
  • npm run generate-env --sandbox
  • ionic serve --staging
  • ionic build:ios --production

I found some cons with this replace.env.js approach:

  • Local files with tokens and other sensitive info
  • If we need other environments we need to change this file;
  • Write the variables into a the config.ts file every time;

We're currently using a script that runs before ionic serve and generate the correct file for us.
Let me explain it in more details here:

  1. We created an env folder in the root of the Ionic project and added this folder to our .gitignore
  2. Inside of the env folder we have different files:
./env
  production.json
  sandbox.json
  staging.json
  1. Each file has the variables we need, for example, our staging.json file looks like:
{
  "api_url": "'https://staging.api.url'",
  "client_id_login": "'566d41f...'",
  "client_secret_login": "'511480878...'",
  "google_maps_key": "'tEkoJK...'"
}
  1. We changed the scripts section inside of the package.json file to have a preionic phase and also an independent command:
"scripts": {
    "ionic:build": "ionic-app-scripts build",
    "ionic:serve": "ionic-app-scripts serve",
    "preionic:build": "node ./scripts/replace.env",
    "preionic:serve": "node ./scripts/replace.env",
    "generate-env": "node ./scripts/replace.env",
    "test": "ng test"
}
  1. Our replace.env.js file handles the generations of our config.ts file https://gist.github.com/juarezpaf/7a9007dfcef25ec24f7de900df8fe04e
  2. The config.ts.sample is very simple and it'll just expose these variables to use use across our app
export class AppConfig {
  static get API() {
    return ENV.api_url;
  }

  static get GOOGLE_MAPS_KEY() {
    return ENV.google_maps_key;
  }

  static get CREDENTIALS() {
    const credentials = {
      'login': {
        'client_id': ENV.login.client_id,
        'client_secret': ENV.login.client_secret
      }
    }
    return credentials;
  }
}
  1. We use the AppConfig in our AuthService:
import { AppConfig } from '../config/config';

@Injectable()
export class Auth {
  credentials: any;
  private apiUrl = AppConfig.API;
  
  constructor(public http: Http) {
    credentials.client_id = AppConfig.CREDENTIALS.login.client_id;
    credentials.client_secret = AppConfig.CREDENTIALS.login.client_secret;
  }
}
  1. With everything in place we can use it in 3 different ways:
  • npm run generate-env --sandbox
  • ionic serve --staging
  • ionic build:ios --production

I found some cons with this replace.env.js approach:

  • Local files with tokens and other sensitive info
  • If we need other environments we need to change this file;
  • Write the variables into a the config.ts file every time;
@jpbrown250

This comment has been minimized.

Show comment
Hide comment
@jpbrown250

jpbrown250 Mar 31, 2017

Hey guys, I have been working all day on a temporary solution to this thread that should work for most of the use cases I have read in this and a few other threads of the same topic. The main concerns I noticed form suggestions include:

  1. Fear of an update that could break a solution
  2. Unable to use environment variables
  3. Required changing the text in a file to switch environments
  4. Required cardinal knowledge of the script and how it functions (personally)

My project should solve all of those issues. If there are any others I am happy to work on it.
Git Repository

I have it fairly well spelled out in the README.md.

Hey guys, I have been working all day on a temporary solution to this thread that should work for most of the use cases I have read in this and a few other threads of the same topic. The main concerns I noticed form suggestions include:

  1. Fear of an update that could break a solution
  2. Unable to use environment variables
  3. Required changing the text in a file to switch environments
  4. Required cardinal knowledge of the script and how it functions (personally)

My project should solve all of those issues. If there are any others I am happy to work on it.
Git Repository

I have it fairly well spelled out in the README.md.

@pbowyer

This comment has been minimized.

Show comment
Hide comment
@pbowyer

pbowyer Apr 6, 2017

There is a very clean implementation at #683 (comment) which I'm using successfully.

pbowyer commented Apr 6, 2017

There is a very clean implementation at #683 (comment) which I'm using successfully.

@vkniazeu

This comment has been minimized.

Show comment
Hide comment
@vkniazeu

vkniazeu Apr 6, 2017

@pbowyer , does it work for you with ionic build browser --prod?
I've commented on #683 as it fails for me when the AoT Compiler kicks in.

vkniazeu commented Apr 6, 2017

@pbowyer , does it work for you with ionic build browser --prod?
I've commented on #683 as it fails for me when the AoT Compiler kicks in.

@vkniazeu

This comment has been minimized.

Show comment
Hide comment
@vkniazeu

vkniazeu Apr 9, 2017

Solution in #683 fails with
[17:52:03] Error: Error encountered resolving symbol values statically. Could not resolve @app/config relative to XXX/src/app/app.module.ts., resolving symbol AppModule in...

vkniazeu commented Apr 9, 2017

Solution in #683 fails with
[17:52:03] Error: Error encountered resolving symbol values statically. Could not resolve @app/config relative to XXX/src/app/app.module.ts., resolving symbol AppModule in...

@pbowyer

This comment has been minimized.

Show comment
Hide comment
@pbowyer

pbowyer Apr 10, 2017

@vkniazeu That exact command works for me - but I can't see anything in the output saying AoT is running, so can't definitively confirm.

pbowyer commented Apr 10, 2017

@vkniazeu That exact command works for me - but I can't see anything in the output saying AoT is running, so can't definitively confirm.

@vkniazeu

This comment has been minimized.

Show comment
Hide comment
@vkniazeu

vkniazeu Apr 10, 2017

@pbowyer , thanks! It's the ngc started ... line which indicates that angular-cli AoT compiler is being used. It should be default if the --prod flag is used.
Are you using latest Ionic and Angular packages in your project?

@pbowyer , thanks! It's the ngc started ... line which indicates that angular-cli AoT compiler is being used. It should be default if the --prod flag is used.
Are you using latest Ionic and Angular packages in your project?

@pbowyer

This comment has been minimized.

Show comment
Hide comment
@pbowyer

pbowyer Apr 10, 2017

@vkniazeu That line's present in the output, so it's being used. It's not present if I omit --prod.

As to versions, I'm on:

Cordova CLI: 6.5.0
Ionic Framework Version: 2.2.0
Ionic CLI Version: 2.2.1
Ionic App Lib Version: 2.2.0
Ionic App Scripts Version: 1.1.4
ios-deploy version: 1.9.1
ios-sim version: 5.0.13
OS: OS X El Capitan
Node Version: v6.10.0
Xcode version: Xcode 8.2.1 Build version 8C1002

pbowyer commented Apr 10, 2017

@vkniazeu That line's present in the output, so it's being used. It's not present if I omit --prod.

As to versions, I'm on:

Cordova CLI: 6.5.0
Ionic Framework Version: 2.2.0
Ionic CLI Version: 2.2.1
Ionic App Lib Version: 2.2.0
Ionic App Scripts Version: 1.1.4
ios-deploy version: 1.9.1
ios-sim version: 5.0.13
OS: OS X El Capitan
Node Version: v6.10.0
Xcode version: Xcode 8.2.1 Build version 8C1002
@vkniazeu

This comment has been minimized.

Show comment
Hide comment
@vkniazeu

vkniazeu Apr 10, 2017

@pbowyer , thank you for your time. Strangely, it fails for another person as well with the same error and same setup. My package versions are newer, but I don't think this is the reason for the resolving symbol values statically error. I'll keep digging.

@pbowyer , thank you for your time. Strangely, it fails for another person as well with the same error and same setup. My package versions are newer, but I don't think this is the reason for the resolving symbol values statically error. I'll keep digging.

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Apr 12, 2017

Contributor

@pbowyer The solution described in #683 does not work with AOT. And ngc should fail for import ... from '@app/config' as it will not be able to resolve it since it's never declared.

Contributor

rolandjitsu commented Apr 12, 2017

@pbowyer The solution described in #683 does not work with AOT. And ngc should fail for import ... from '@app/config' as it will not be able to resolve it since it's never declared.

@vkniazeu

This comment has been minimized.

Show comment
Hide comment
@vkniazeu

vkniazeu Apr 12, 2017

@rolandjitsu , thanks for bringing this up again. Do you have an idea of how to overcome this lack of declaration for AoT to work? Or, perhaps, you have an alternative solution that would be similar in simplicity?

vkniazeu commented Apr 12, 2017

@rolandjitsu , thanks for bringing this up again. Do you have an idea of how to overcome this lack of declaration for AoT to work? Or, perhaps, you have an alternative solution that would be similar in simplicity?

@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Apr 12, 2017

Contributor

@vkniazeu I currently use Rollup instead of Webpack and rollup-plugin-replace.

rollup.config.js:

// For reference, check the following:
// https://github.com/driftyco/ionic-app-scripts/blob/master/config/rollup.config.js
// Config borrowed from: https://github.com/driftyco/ionic-cli/issues/1205#issuecomment-255744604
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const globals = require('rollup-plugin-node-globals');
const builtins = require('rollup-plugin-node-builtins');
const json = require('rollup-plugin-json');

// We use the replace plugin to provide the correct env variables
const replace = require('rollup-plugin-replace');

// We will use the machine ip to make HTTP requests to the local machine from an external device while in dev mode.
const ip = require('ip');
// We need to expose the app id to the app.
const ionicConfig = require('../ionic.config.json');


// NOTE: If IONIC_ENV is not set, we check if the CLI args contain the '--prod' flag.
function isProd() {
	return process.env.NODE_ENV === 'production' || process.env.IONIC_ENV === 'prod' || process.argv.slice(2)
			.some((arg) => arg.indexOf('--prod') !== -1)
}


// https://github.com/rollup/rollup/wiki/JavaScript-API
const config = {
	/**
	 * entry: The bundle's starting point. This file will
	 * be included, along with the minimum necessary code
	 * from its dependencies
	 */
	entry: process.env.IONIC_APP_ENTRY_POINT,

	/**
	 * sourceMap: If true, a separate sourcemap file will
	 * be created.
	 */
	sourceMap: true,

	/**
	 * format: The format of the generated bundle
	 */
	format: 'iife',

	/**
	 * dest: the output filename for the bundle in the buildDir
	 */
	dest: process.env.IONIC_OUTPUT_JS_FILE_NAME,

	/**
	 * plugins: Array of plugin objects, or a single plugin object.
	 * See https://github.com/rollup/rollup/wiki/Plugins for more info.
	 */
	plugins: [
		builtins(),
		commonjs(),
		nodeResolve({
			module: true,
			jsnext: true,
			main: true,
			browser: true,
			extensions: ['.js']
		}),
		globals(),
		json(),
		replace({
			values: {
				'{{API_HOST}}': isProd() ? 'https://api.domain.com' : `http://${ip.address()}:8888`,
				'{{IONIC_ENV}}': isProd() ? 'prod' : 'dev',
				'{{APP_ID}}': ionicConfig.app_id
			},
			// Config
			exclude: 'node_modules/**'
		})
	]
};


module.exports = config;

env.ts (I placed it under src/app/):

export const env: any = '{{IONIC_ENV}}';
export const apiHost = '{{API_HOST}}';
export const appId = '{{APP_ID}}';

export function isProd(): boolean {
	return env === 'prod';
}

And you just import from env wherever you need. It should work with AOT as well.
Though I'm encountering other issues with Rollup that are unrelated.

I'm not sure about a fix for @app/config, I imagine making a declaration file should suffice, but I'm not sure how ngc will work with it since it needs to resolve the values statically.

Contributor

rolandjitsu commented Apr 12, 2017

@vkniazeu I currently use Rollup instead of Webpack and rollup-plugin-replace.

rollup.config.js:

// For reference, check the following:
// https://github.com/driftyco/ionic-app-scripts/blob/master/config/rollup.config.js
// Config borrowed from: https://github.com/driftyco/ionic-cli/issues/1205#issuecomment-255744604
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const globals = require('rollup-plugin-node-globals');
const builtins = require('rollup-plugin-node-builtins');
const json = require('rollup-plugin-json');

// We use the replace plugin to provide the correct env variables
const replace = require('rollup-plugin-replace');

// We will use the machine ip to make HTTP requests to the local machine from an external device while in dev mode.
const ip = require('ip');
// We need to expose the app id to the app.
const ionicConfig = require('../ionic.config.json');


// NOTE: If IONIC_ENV is not set, we check if the CLI args contain the '--prod' flag.
function isProd() {
	return process.env.NODE_ENV === 'production' || process.env.IONIC_ENV === 'prod' || process.argv.slice(2)
			.some((arg) => arg.indexOf('--prod') !== -1)
}


// https://github.com/rollup/rollup/wiki/JavaScript-API
const config = {
	/**
	 * entry: The bundle's starting point. This file will
	 * be included, along with the minimum necessary code
	 * from its dependencies
	 */
	entry: process.env.IONIC_APP_ENTRY_POINT,

	/**
	 * sourceMap: If true, a separate sourcemap file will
	 * be created.
	 */
	sourceMap: true,

	/**
	 * format: The format of the generated bundle
	 */
	format: 'iife',

	/**
	 * dest: the output filename for the bundle in the buildDir
	 */
	dest: process.env.IONIC_OUTPUT_JS_FILE_NAME,

	/**
	 * plugins: Array of plugin objects, or a single plugin object.
	 * See https://github.com/rollup/rollup/wiki/Plugins for more info.
	 */
	plugins: [
		builtins(),
		commonjs(),
		nodeResolve({
			module: true,
			jsnext: true,
			main: true,
			browser: true,
			extensions: ['.js']
		}),
		globals(),
		json(),
		replace({
			values: {
				'{{API_HOST}}': isProd() ? 'https://api.domain.com' : `http://${ip.address()}:8888`,
				'{{IONIC_ENV}}': isProd() ? 'prod' : 'dev',
				'{{APP_ID}}': ionicConfig.app_id
			},
			// Config
			exclude: 'node_modules/**'
		})
	]
};


module.exports = config;

env.ts (I placed it under src/app/):

export const env: any = '{{IONIC_ENV}}';
export const apiHost = '{{API_HOST}}';
export const appId = '{{APP_ID}}';

export function isProd(): boolean {
	return env === 'prod';
}

And you just import from env wherever you need. It should work with AOT as well.
Though I'm encountering other issues with Rollup that are unrelated.

I'm not sure about a fix for @app/config, I imagine making a declaration file should suffice, but I'm not sure how ngc will work with it since it needs to resolve the values statically.

@seiyria

This comment has been minimized.

Show comment
Hide comment
@seiyria

seiyria Sep 13, 2017

@danbucholtz if it helps, the only approach I've had success with so far is @pbowyer's cascading config. Although, I've had mixed success with it - the environment default has to be ts, and the env files need to be js. All of them TS will complain about something, and all of them JS will complain about overwriting the source file. Basically, if you can do what he has there, but more elegant, that would be ideal, I think.

seiyria commented Sep 13, 2017

@danbucholtz if it helps, the only approach I've had success with so far is @pbowyer's cascading config. Although, I've had mixed success with it - the environment default has to be ts, and the env files need to be js. All of them TS will complain about something, and all of them JS will complain about overwriting the source file. Basically, if you can do what he has there, but more elegant, that would be ideal, I think.

@gshigeto

This comment has been minimized.

Show comment
Hide comment
@gshigeto

gshigeto Sep 20, 2017

@danbucholtz I also put together another solution that has been working great for me and others that I posted about here.
I also set up a sample repo here.
There is a PR open #1201

I created an alias that references the IONIC_ENV which points to an environment file in my src directory.

This way it is

  • Easy to use import {ENV} from @app/env
  • You can even use it in your app.module.ts

gshigeto commented Sep 20, 2017

@danbucholtz I also put together another solution that has been working great for me and others that I posted about here.
I also set up a sample repo here.
There is a PR open #1201

I created an alias that references the IONIC_ENV which points to an environment file in my src directory.

This way it is

  • Easy to use import {ENV} from @app/env
  • You can even use it in your app.module.ts
@sneko

This comment has been minimized.

Show comment
Hide comment
@sneko

sneko Sep 30, 2017

@danbucholtz any news when it will be released? Thanks

sneko commented Sep 30, 2017

@danbucholtz any news when it will be released? Thanks

@sneko

This comment has been minimized.

Show comment
Hide comment
@sneko

sneko Oct 6, 2017

@danbucholtz are you still working on this? Thanks :)

sneko commented Oct 6, 2017

@danbucholtz are you still working on this? Thanks :)

@mhartington

This comment has been minimized.

Show comment
Hide comment
@mhartington

mhartington Oct 20, 2017

Member

Hey folks, just chiming in here. @danbucholtz is out for a bit (new baby 🎉 )

We'll pick this up in a bit.

Member

mhartington commented Oct 20, 2017

Hey folks, just chiming in here. @danbucholtz is out for a bit (new baby 🎉 )

We'll pick this up in a bit.

@okonon

This comment has been minimized.

Show comment
Hide comment

okonon commented Oct 22, 2017

Nice!

@capcom-r

This comment has been minimized.

Show comment
Hide comment
@capcom-r

capcom-r Oct 31, 2017

Looking very forward to this also, wishing you the best @danbucholtz

Looking very forward to this also, wishing you the best @danbucholtz

@agasbzj

This comment has been minimized.

Show comment
Hide comment
@agasbzj

agasbzj Nov 14, 2017

I wonder since 3.1.0 is out, is this proposal accomplished?

agasbzj commented Nov 14, 2017

I wonder since 3.1.0 is out, is this proposal accomplished?

@bladerunner41

This comment has been minimized.

Show comment
Hide comment
@bladerunner41

bladerunner41 Nov 14, 2017

Don't believe so - see the ionic change log

Don't believe so - see the ionic change log

@yuricamara

This comment has been minimized.

Show comment
Hide comment
@yuricamara

yuricamara Nov 14, 2017

Why "see the ionic change log", @murraybauer ? I found nothing about env there.

Why "see the ionic change log", @murraybauer ? I found nothing about env there.

@mlegenhausen

This comment has been minimized.

Show comment
Hide comment
@mlegenhausen

mlegenhausen Nov 14, 2017

This does not work in prodution. See the following comment: #762 (comment)

If someone still looks for the environments file way like in the angular-cli. You can use this custom webpack.config.js file:

const path = require('path');

const webpack = require('webpack');
const config = require('@ionic/app-scripts/config/webpack.config.js');

config.prod.plugins.push(new webpack.NormalModuleReplacementPlugin(
  /src\/environments\/environment\.ts/,
  path.resolve('./src/environments/environment.prod.ts')
));

module.exports = config;

Don't forget to add "config": { "ionic_webpack": "./webpack.config.js" } to your package.json and to create the appropriate files under src/environments.

mlegenhausen commented Nov 14, 2017

This does not work in prodution. See the following comment: #762 (comment)

If someone still looks for the environments file way like in the angular-cli. You can use this custom webpack.config.js file:

const path = require('path');

const webpack = require('webpack');
const config = require('@ionic/app-scripts/config/webpack.config.js');

config.prod.plugins.push(new webpack.NormalModuleReplacementPlugin(
  /src\/environments\/environment\.ts/,
  path.resolve('./src/environments/environment.prod.ts')
));

module.exports = config;

Don't forget to add "config": { "ionic_webpack": "./webpack.config.js" } to your package.json and to create the appropriate files under src/environments.

@AksimO

This comment has been minimized.

Show comment
Hide comment
@AksimO

AksimO Nov 14, 2017

Take a look at plugin webpack-environment-suffix-plugin, similar approach to what @mlegenhausen suggests.

const EnvironmentSuffixPlugin = require('webpack-environment-suffix-plugin');
//...
new EnvironmentSuffixPlugin(
    ext: 'ts'
    suffix: process.env.NODE_ENV
);

AksimO commented Nov 14, 2017

Take a look at plugin webpack-environment-suffix-plugin, similar approach to what @mlegenhausen suggests.

const EnvironmentSuffixPlugin = require('webpack-environment-suffix-plugin');
//...
new EnvironmentSuffixPlugin(
    ext: 'ts'
    suffix: process.env.NODE_ENV
);
@agasbzj

This comment has been minimized.

Show comment
Hide comment
@agasbzj

agasbzj Nov 15, 2017

@AksimO This works perfectly, THANK YOU!!

agasbzj commented Nov 15, 2017

@AksimO This works perfectly, THANK YOU!!

@alexbonhomme

This comment has been minimized.

Show comment
Hide comment
@alexbonhomme

alexbonhomme Nov 15, 2017

@mlegenhausen Your solution is exactly what I was looking for ! I don't understand why the Ionic team doesn't include this feature :/

@mlegenhausen Your solution is exactly what I was looking for ! I don't understand why the Ionic team doesn't include this feature :/

@emcniece

This comment has been minimized.

Show comment
Hide comment
@emcniece

emcniece Nov 17, 2017

#762 (comment) (@mlegenhausen ) is AOT-compatible, which is a crucial feature for me. I am including environment-specific variables in the main @NgModule declaration:

@NgModule({
    imports: [
        CommonModule,
        BrowserModule,
        HttpModule,
        PagesModule,
        ComponentsModule,
        StoreModule.provideStore(reducer),
        EffectsModule.run(DeviceEffects),
        IonicModule.forRoot(AppComponent,
            // Disable page transitions for screenshot task
            {animate: environment.enableAnimation}
        ),
       ....

This can only be done by including a non-NgModule component - ngc fails if you use a solution like #762 (comment) (@juarezpaf).

emcniece commented Nov 17, 2017

#762 (comment) (@mlegenhausen ) is AOT-compatible, which is a crucial feature for me. I am including environment-specific variables in the main @NgModule declaration:

@NgModule({
    imports: [
        CommonModule,
        BrowserModule,
        HttpModule,
        PagesModule,
        ComponentsModule,
        StoreModule.provideStore(reducer),
        EffectsModule.run(DeviceEffects),
        IonicModule.forRoot(AppComponent,
            // Disable page transitions for screenshot task
            {animate: environment.enableAnimation}
        ),
       ....

This can only be done by including a non-NgModule component - ngc fails if you use a solution like #762 (comment) (@juarezpaf).

@Daskus1

This comment has been minimized.

Show comment
Hide comment
@Daskus1

Daskus1 Dec 12, 2017

Are we going to see this feature any time soon?

Daskus1 commented Dec 12, 2017

Are we going to see this feature any time soon?

@luckylooke

This comment has been minimized.

Show comment
Hide comment
@luckylooke

luckylooke Dec 14, 2017

Angular CLI has parfect solution, which best fits my needs, see here. It would be very nice to have same mechanism in ionic projects. Thanks ;)

Angular CLI has parfect solution, which best fits my needs, see here. It would be very nice to have same mechanism in ionic projects. Thanks ;)

@danbucholtz

This comment has been minimized.

Show comment
Hide comment
@danbucholtz

danbucholtz Dec 15, 2017

Contributor

We put this feature on hold because we are moving to the Angular CLI 🎉 .

Thanks,
Dan

Contributor

danbucholtz commented Dec 15, 2017

We put this feature on hold because we are moving to the Angular CLI 🎉 .

Thanks,
Dan

@keithdmoore

This comment has been minimized.

Show comment
Hide comment
@keithdmoore

keithdmoore Dec 19, 2017

@danbucholtz Any idea as to when the ionic-cli will be replaced by the Angular-CLI?

@danbucholtz Any idea as to when the ionic-cli will be replaced by the Angular-CLI?

@danbucholtz

This comment has been minimized.

Show comment
Hide comment
@danbucholtz

danbucholtz Dec 21, 2017

Contributor

As soon as possible. No date yet but it will coincide with Ionic 4.

Check out the core branch of the Ionic repo if you're interested in following along.

Thanks,
Dan

Contributor

danbucholtz commented Dec 21, 2017

As soon as possible. No date yet but it will coincide with Ionic 4.

Check out the core branch of the Ionic repo if you're interested in following along.

Thanks,
Dan

@keithdmoore

This comment has been minimized.

Show comment
Hide comment
@keithdmoore

keithdmoore Dec 22, 2017

Thanks. That helps. I will need to implement something prior to that.

Thanks. That helps. I will need to implement something prior to that.

@emcniece

This comment has been minimized.

Show comment
Hide comment
@emcniece

emcniece Dec 22, 2017

@keithdmoore:

  1. Extend webpack.config.js, inject your own customEnvPlugin:
// Extends ./webpack.default.config.js
// which is a direct copy of node_modules/@ionic/app-scripts/config/webpack.config.js
//
// We force devs to copy/paste to webpack.default.config.js so that upgrading
// @ionic/app-scripts is explicit, intentional, and reviewed.
//
// Added minimist, customEnvPlugin

var defaultConfig = require('./webpack.default.config.js');
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var chalk = require('chalk');

var argv = require('minimist')(process.argv.slice(2));

// Build environment vars
var customEnvPlugin;

if(!!argv.env){
  fileName = './src/environments/environment.'+argv.env+'.ts';
  var fileExists = fs.existsSync(fileName);

  if(fileExists){
    console.log(chalk.bgGreen('CONFIG LOADED: ', fileName));

    customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
      /src\/environments\/environment\.ts/,
      path.resolve('./src/environments/environment.'+argv.env+'.ts')
    );
  } else {
    console.log(chalk.bgRed('CONFIG SPECIFIED BUT MISSING: ', fileName));
    process.exit();
  }
} else {
  console.log('CONFIG DEFAULT: ', './src/environments/environment.ts');
  customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
    /src\/environments\/environment\.ts/,
    path.resolve('./src/environments/environment.ts')
  );
}

defaultConfig.prod.plugins.push(customEnvPlugin);
defaultConfig.dev.plugins.push(customEnvPlugin);

module.exports = defaultConfig;
  1. Create src/environments/environment.ts, place your variables:
// Dev profile: animations & devtools

export const environment = {
  production: false,
  enableAnimation: true,
  enableDevTools: true,
  settings: {}
};

2.5 Clone src/environments/environment.ts to src/environments/environment.dev.ts or src/environments/environment.prod.ts as needed. The .dev and .prod are used in CLI

  1. Import into modules as needed. Example: src/app/app.module.ts:
...
import { environment } from '../environments/environment';
...

@NgModule({
    imports: [
        CommonModule,
        BrowserModule,
        HttpModule,
        PagesModule,
        ComponentsModule,
        ...
        IonicModule.forRoot(AppComponent,
            // Disable page transitions for screenshot task
            {animate: environment.enableAnimation}
...
  1. Run via CLI with flag: ionic serve -b --env=dev

Credits: this thread and https://www.williamghelfi.com/blog/2017/06/22/ionic-environments-webpack/

emcniece commented Dec 22, 2017

@keithdmoore:

  1. Extend webpack.config.js, inject your own customEnvPlugin:
// Extends ./webpack.default.config.js
// which is a direct copy of node_modules/@ionic/app-scripts/config/webpack.config.js
//
// We force devs to copy/paste to webpack.default.config.js so that upgrading
// @ionic/app-scripts is explicit, intentional, and reviewed.
//
// Added minimist, customEnvPlugin

var defaultConfig = require('./webpack.default.config.js');
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var chalk = require('chalk');

var argv = require('minimist')(process.argv.slice(2));

// Build environment vars
var customEnvPlugin;

if(!!argv.env){
  fileName = './src/environments/environment.'+argv.env+'.ts';
  var fileExists = fs.existsSync(fileName);

  if(fileExists){
    console.log(chalk.bgGreen('CONFIG LOADED: ', fileName));

    customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
      /src\/environments\/environment\.ts/,
      path.resolve('./src/environments/environment.'+argv.env+'.ts')
    );
  } else {
    console.log(chalk.bgRed('CONFIG SPECIFIED BUT MISSING: ', fileName));
    process.exit();
  }
} else {
  console.log('CONFIG DEFAULT: ', './src/environments/environment.ts');
  customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
    /src\/environments\/environment\.ts/,
    path.resolve('./src/environments/environment.ts')
  );
}

defaultConfig.prod.plugins.push(customEnvPlugin);
defaultConfig.dev.plugins.push(customEnvPlugin);

module.exports = defaultConfig;
  1. Create src/environments/environment.ts, place your variables:
// Dev profile: animations & devtools

export const environment = {
  production: false,
  enableAnimation: true,
  enableDevTools: true,
  settings: {}
};

2.5 Clone src/environments/environment.ts to src/environments/environment.dev.ts or src/environments/environment.prod.ts as needed. The .dev and .prod are used in CLI

  1. Import into modules as needed. Example: src/app/app.module.ts:
...
import { environment } from '../environments/environment';
...

@NgModule({
    imports: [
        CommonModule,
        BrowserModule,
        HttpModule,
        PagesModule,
        ComponentsModule,
        ...
        IonicModule.forRoot(AppComponent,
            // Disable page transitions for screenshot task
            {animate: environment.enableAnimation}
...
  1. Run via CLI with flag: ionic serve -b --env=dev

Credits: this thread and https://www.williamghelfi.com/blog/2017/06/22/ionic-environments-webpack/

@mlegenhausen

This comment has been minimized.

Show comment
Hide comment
@mlegenhausen

mlegenhausen Dec 22, 2017

I have to step back from my solution all these webpack solutions fail when you run the production build (--prod). The problem is that ngc is executed before the webpack configuration does happen. That means that during the static type analyses your default environment.ts is used and statically compiled in your application. After that webpack is executed and your environment.ts is replaced by e. g. environment.prod.ts. Now do you have following problem. When you try console.log(environment) you see your production configuration, but because ngc deconstructs your environment object during the compilation step everywhere where you have used an environment variable in your @NgModule configuration it stayes the default configuration. This error is hard to track down so I would not recommend to replace any module via webpack.

My recommendation is to use the angular cli with ionic when you need environment file support!

mlegenhausen commented Dec 22, 2017

I have to step back from my solution all these webpack solutions fail when you run the production build (--prod). The problem is that ngc is executed before the webpack configuration does happen. That means that during the static type analyses your default environment.ts is used and statically compiled in your application. After that webpack is executed and your environment.ts is replaced by e. g. environment.prod.ts. Now do you have following problem. When you try console.log(environment) you see your production configuration, but because ngc deconstructs your environment object during the compilation step everywhere where you have used an environment variable in your @NgModule configuration it stayes the default configuration. This error is hard to track down so I would not recommend to replace any module via webpack.

My recommendation is to use the angular cli with ionic when you need environment file support!

@emcniece

This comment has been minimized.

Show comment
Hide comment
@emcniece

emcniece Dec 22, 2017

@mlegenhausen make the prod config the default environment file? Then when not using ngc (ie. non-production) you can specify --env=devand let webpack rewrite without issues.

To be clear, I have 3 environment files:

  • environment.ts
  • environment.prod.ts
  • environment.dev.ts

The first 2 files are identical - I include the environment.prod.ts for verbosity if anyone checks them out. This is working well for me so far.

emcniece commented Dec 22, 2017

@mlegenhausen make the prod config the default environment file? Then when not using ngc (ie. non-production) you can specify --env=devand let webpack rewrite without issues.

To be clear, I have 3 environment files:

  • environment.ts
  • environment.prod.ts
  • environment.dev.ts

The first 2 files are identical - I include the environment.prod.ts for verbosity if anyone checks them out. This is working well for me so far.

@mlegenhausen

This comment has been minimized.

Show comment
Hide comment
@mlegenhausen

mlegenhausen Dec 22, 2017

This does only work when you have only one production version. I need to configuer differntly for android ios and web.

This does only work when you have only one production version. I need to configuer differntly for android ios and web.

@GFoley83

This comment has been minimized.

Show comment
Hide comment
@GFoley83

GFoley83 Feb 22, 2018

@emcniece @gshigeto Do your solutions support using a custom environment file e.g. environment.test.ts when using ngc? If not what modification would be needed?
I'd like to be able to build APKs for the various environments e.g. ionic cordova build android --test.

GFoley83 commented Feb 22, 2018

@emcniece @gshigeto Do your solutions support using a custom environment file e.g. environment.test.ts when using ngc? If not what modification would be needed?
I'd like to be able to build APKs for the various environments e.g. ionic cordova build android --test.

@emcniece

This comment has been minimized.

Show comment
Hide comment
@emcniece

emcniece Feb 22, 2018

@GFoley83 Yes, AOT (ngc) compilation works well with this pattern.

The downside of the provided example is that "production" builds end up with double flags, but that's not the end of the world. Here's a sample of my package.json scripts:

"build-aot": "ionic-app-scripts build --release --prod --env=prod",
"production-ios": "ionic cordova build ios --buildConfig ./config/build-ios.json --release --prod --env=prod",
"production-android": "ionic cordova build android --release --prod --env=prod",
"production-browser": "ionic cordova build browser --release --prod --env=prod",

The --release and --prod flags are for Ionic. The --env=prod is the custom flag for the provided example Webpack config modification, which matches the ./src/environments/environment.prod.ts file. Using --prod or --release compiles the code with ngc, not sure exactly which one.

Oh, again as a disclaimer, that is totally not "my" solution, it came from William Ghelfi.

emcniece commented Feb 22, 2018

@GFoley83 Yes, AOT (ngc) compilation works well with this pattern.

The downside of the provided example is that "production" builds end up with double flags, but that's not the end of the world. Here's a sample of my package.json scripts:

"build-aot": "ionic-app-scripts build --release --prod --env=prod",
"production-ios": "ionic cordova build ios --buildConfig ./config/build-ios.json --release --prod --env=prod",
"production-android": "ionic cordova build android --release --prod --env=prod",
"production-browser": "ionic cordova build browser --release --prod --env=prod",

The --release and --prod flags are for Ionic. The --env=prod is the custom flag for the provided example Webpack config modification, which matches the ./src/environments/environment.prod.ts file. Using --prod or --release compiles the code with ngc, not sure exactly which one.

Oh, again as a disclaimer, that is totally not "my" solution, it came from William Ghelfi.

@bladerunner41

This comment has been minimized.

Show comment
Hide comment
@bladerunner41

bladerunner41 Feb 22, 2018

@GFoley83

This comment has been minimized.

Show comment
Hide comment
@GFoley83

GFoley83 Feb 22, 2018

I ended up using a combo of @emcniece and @gshigeto solutions, so massive thanks to them.

I wanted to have environment variables work in Ionic exactly like how they do in Angular CLI (especially given Ionic 4 is somewhere on the horizon), whilst also supporting Karma and Protractor tests.

To use the environment variables, just add import { environment } from '@env/environment';.
Also supports ngc so I can build an Android APK with any environment config e.g. my environment.test.ts config.

This solution also supports using ionic serve with any of your configurations e.g.
ionic serve --env=prod

I've managed all this with the following setup:

/src/app/environments folder contains

environment.model.ts
environment.ts
environment.prod.ts
environment.test.ts

/tsconfig.json

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./src",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "paths": {   
      "@env/*": [
        "environments/*"
      ]
    },
    "lib": [
      "dom",
      "es2016"
    ],
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules",
    "src/**/*.spec.ts"
  ],
  "typeRoots": [
    "node_modules/@types"
  ],
  "compileOnSave": false,
  "atom": {
    "rewriteTsconfig": false
  }
}

/config/webpack.config.js

var chalk = require("chalk");
var defaultConfig = require('@ionic/app-scripts/config/webpack.config.js');
var ionic_env = process.env.IONIC_ENV;
var fs = require('fs');
var path = require('path');
var env = require('minimist')(process.argv.slice(2)).env || process.env.IONIC_ENV || 'dev';
var webpack = require('webpack');

defaultConfig.dev.resolve.alias = {
    "@env/environment": path.resolve(environmentPath('dev'))
};

defaultConfig.prod.resolve.alias = {
    "@env/environment": path.resolve(environmentPath('prod'))
};

console.log(chalk.yellow.bgBlack('\nUsing ' + env + ' environment variables.\n'));

if (!!env) {
    var pathToCustomEnvFile = path.resolve(environmentPath(env));

    defaultConfig[env] = defaultConfig[ionic_env];
    defaultConfig[env].resolve.alias = {
        "@env/environment": pathToCustomEnvFile
    };

    var customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
        /src\/environments\/environment\.ts/, pathToCustomEnvFile
    );

    defaultConfig.prod.plugins.push(customEnvPlugin);
    defaultConfig.dev.plugins.push(customEnvPlugin);
}

module.exports = function() {
    return defaultConfig;
};


function environmentPath(env) {
    var filePath = './src/environments/environment' + (env === '' ? '' : '.' + env) + '.ts';
    if (fs.existsSync(filePath)) {
        return filePath;
    }

    console.log(chalk.red.bgWhite('\n' + filePath + ' does not exist!\n'));
    process.exit();
}

/test-config/webpack.test.js

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var chalk = require('chalk');

module.exports = {
    devtool: 'inline-source-map',

    resolve: {
        alias: {
            '@env/environment': path.resolve(environmentPath('dev')),
        },
        extensions: ['.ts', '.js']
    },

    module: {
        rules: [{
                test: /\.ts$/,
                loaders: [{
                    loader: 'ts-loader'
                }, 'angular2-template-loader']
            },
            {
                test: /\.html$/,
                loader: 'html-loader?attrs=false'
            },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                loader: 'null-loader'
            }
        ]
    },

    plugins: [
        new webpack.ContextReplacementPlugin(
            // The (\\|\/) piece accounts for path separators in *nix and Windows
            /(ionic-angular)|(angular(\\|\/)core(\\|\/)@angular)/,
            root('./src'), // location of your src
            {} // a map of your routes
        )
    ]
};

function environmentPath(env) {
    var filePath = './src/environments/environment' + (env === '' ? '' : '.' + env) + '.ts';
    if (fs.existsSync(filePath)) {
        return filePath;
    }

    console.log(chalk.red('\n' + filePath + ' does not exist!\n'));
    process.exit();
}

function root(localPath) {
    return path.resolve(__dirname, localPath);
}

/package.json

....
        "ionic": "ionic",
        "build": "ionic build",
        "build:test": "ionic build --env=test",
        "build:prod": "ionic build --prod",
        "build:android": "ionic cordova build android --dev",
        "build:android:test": "ionic cordova build android --prod --env=test",
        "build:android:prod": "ionic cordova build android --prod",
....

GFoley83 commented Feb 22, 2018

I ended up using a combo of @emcniece and @gshigeto solutions, so massive thanks to them.

I wanted to have environment variables work in Ionic exactly like how they do in Angular CLI (especially given Ionic 4 is somewhere on the horizon), whilst also supporting Karma and Protractor tests.

To use the environment variables, just add import { environment } from '@env/environment';.
Also supports ngc so I can build an Android APK with any environment config e.g. my environment.test.ts config.

This solution also supports using ionic serve with any of your configurations e.g.
ionic serve --env=prod

I've managed all this with the following setup:

/src/app/environments folder contains

environment.model.ts
environment.ts
environment.prod.ts
environment.test.ts

/tsconfig.json

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./src",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "paths": {   
      "@env/*": [
        "environments/*"
      ]
    },
    "lib": [
      "dom",
      "es2016"
    ],
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules",
    "src/**/*.spec.ts"
  ],
  "typeRoots": [
    "node_modules/@types"
  ],
  "compileOnSave": false,
  "atom": {
    "rewriteTsconfig": false
  }
}

/config/webpack.config.js

var chalk = require("chalk");
var defaultConfig = require('@ionic/app-scripts/config/webpack.config.js');
var ionic_env = process.env.IONIC_ENV;
var fs = require('fs');
var path = require('path');
var env = require('minimist')(process.argv.slice(2)).env || process.env.IONIC_ENV || 'dev';
var webpack = require('webpack');

defaultConfig.dev.resolve.alias = {
    "@env/environment": path.resolve(environmentPath('dev'))
};

defaultConfig.prod.resolve.alias = {
    "@env/environment": path.resolve(environmentPath('prod'))
};

console.log(chalk.yellow.bgBlack('\nUsing ' + env + ' environment variables.\n'));

if (!!env) {
    var pathToCustomEnvFile = path.resolve(environmentPath(env));

    defaultConfig[env] = defaultConfig[ionic_env];
    defaultConfig[env].resolve.alias = {
        "@env/environment": pathToCustomEnvFile
    };

    var customEnvPlugin = new webpack.NormalModuleReplacementPlugin(
        /src\/environments\/environment\.ts/, pathToCustomEnvFile
    );

    defaultConfig.prod.plugins.push(customEnvPlugin);
    defaultConfig.dev.plugins.push(customEnvPlugin);
}

module.exports = function() {
    return defaultConfig;
};


function environmentPath(env) {
    var filePath = './src/environments/environment' + (env === '' ? '' : '.' + env) + '.ts';
    if (fs.existsSync(filePath)) {
        return filePath;
    }

    console.log(chalk.red.bgWhite('\n' + filePath + ' does not exist!\n'));
    process.exit();
}

/test-config/webpack.test.js

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var chalk = require('chalk');

module.exports = {
    devtool: 'inline-source-map',

    resolve: {
        alias: {
            '@env/environment': path.resolve(environmentPath('dev')),
        },
        extensions: ['.ts', '.js']
    },

    module: {
        rules: [{
                test: /\.ts$/,
                loaders: [{
                    loader: 'ts-loader'
                }, 'angular2-template-loader']
            },
            {
                test: /\.html$/,
                loader: 'html-loader?attrs=false'
            },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                loader: 'null-loader'
            }
        ]
    },

    plugins: [
        new webpack.ContextReplacementPlugin(
            // The (\\|\/) piece accounts for path separators in *nix and Windows
            /(ionic-angular)|(angular(\\|\/)core(\\|\/)@angular)/,
            root('./src'), // location of your src
            {} // a map of your routes
        )
    ]
};

function environmentPath(env) {
    var filePath = './src/environments/environment' + (env === '' ? '' : '.' + env) + '.ts';
    if (fs.existsSync(filePath)) {
        return filePath;
    }

    console.log(chalk.red('\n' + filePath + ' does not exist!\n'));
    process.exit();
}

function root(localPath) {
    return path.resolve(__dirname, localPath);
}

/package.json

....
        "ionic": "ionic",
        "build": "ionic build",
        "build:test": "ionic build --env=test",
        "build:prod": "ionic build --prod",
        "build:android": "ionic cordova build android --dev",
        "build:android:test": "ionic cordova build android --prod --env=test",
        "build:android:prod": "ionic cordova build android --prod",
....
@raajmayukh

This comment has been minimized.

Show comment
Hide comment
@raajmayukh

raajmayukh Mar 15, 2018

@keithdmoore Thanks for your idea to fix this solution. I have followed your solution and able to load files as per what's been set as fileName. But with this solution is it possible to access process.argv.env in TS files anywhere in the application?

I need to access them in my app to load some Mock services for different environments. Earlier(Angular 4 and ionic app scripts 1.x) i was achieving this by setting process.argv.NODE_ENV. I was using Opaque token for the same. Now with Angular 5 and ionic app scripts 3.1.x i am using Injection token and trying to access process.argv.env together with your solution but it always comes as undefined in TS files. Any idea on this?

I had created an issue earlier in Ionic repo, probably should have created here: Here is the link with all info:

[(https://github.com/ionic-team/ionic/issues/14150)]

raajmayukh commented Mar 15, 2018

@keithdmoore Thanks for your idea to fix this solution. I have followed your solution and able to load files as per what's been set as fileName. But with this solution is it possible to access process.argv.env in TS files anywhere in the application?

I need to access them in my app to load some Mock services for different environments. Earlier(Angular 4 and ionic app scripts 1.x) i was achieving this by setting process.argv.NODE_ENV. I was using Opaque token for the same. Now with Angular 5 and ionic app scripts 3.1.x i am using Injection token and trying to access process.argv.env together with your solution but it always comes as undefined in TS files. Any idea on this?

I had created an issue earlier in Ionic repo, probably should have created here: Here is the link with all info:

[(https://github.com/ionic-team/ionic/issues/14150)]

@keithdmoore

This comment has been minimized.

Show comment
Hide comment
@keithdmoore

keithdmoore Mar 16, 2018

I’m sure you could. I followed someone else’s suggestion above. I have a name attribute in my environment model that is the same as what is in the file name. I can access the name in my ts files if I need to.

I’m sure you could. I followed someone else’s suggestion above. I have a name attribute in my environment model that is the same as what is in the file name. I can access the name in my ts files if I need to.

@tabirkeland

This comment has been minimized.

Show comment
Hide comment
@tabirkeland

tabirkeland Mar 16, 2018

All, with Ionic 4 release approaching, I know that ENV vars will be handled. If you are using Ionic 3.9.2, here is what I have been using for months to handle ENV vars. It's been pieced together from this and a few other threads.

https://gist.github.com/tabirkeland/a17c67b2f1ea3331d94db34ed7191c34

All, with Ionic 4 release approaching, I know that ENV vars will be handled. If you are using Ionic 3.9.2, here is what I have been using for months to handle ENV vars. It's been pieced together from this and a few other threads.

https://gist.github.com/tabirkeland/a17c67b2f1ea3331d94db34ed7191c34

@writer0713

This comment has been minimized.

Show comment
Hide comment

made a starter project with detail explanations.

https://github.com/writer0713/ionic-environment-setting/blob/master/README.md

@tabirkeland

This comment has been minimized.

Show comment
Hide comment
@tabirkeland

tabirkeland Apr 3, 2018

@writer0713 nice one. So I assume my gist helped?

@writer0713 nice one. So I assume my gist helped?

@writer0713

This comment has been minimized.

Show comment
Hide comment
@writer0713

writer0713 Apr 4, 2018

@tabirkeland
Yes, I read so many different articles, but nothing worked. After I followed your code, it worked. Thanks.

@tabirkeland
Yes, I read so many different articles, but nothing worked. After I followed your code, it worked. Thanks.

@daraul

This comment has been minimized.

Show comment
Hide comment
@daraul

daraul May 22, 2018

@writer0713 your readme was a life saver. Worked like a charm
Thanks to you and @tabirkeland

daraul commented May 22, 2018

@writer0713 your readme was a life saver. Worked like a charm
Thanks to you and @tabirkeland

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment