/ionic-cascading-config

Cascading config files with inheritance, for Ionic 2+ projects

Primary LanguageTypeScript

Cascading configuration files

...because separate files are painful.

Features

  • Merge our custom webpack config into the Ionic config, so upgrading Ionic won't cause future problems
  • Rewrite config paths to include the environment-specific config
  • Merge our app configurations together, so properties cascade
  • Possible to autocomplete the app configuration in my IDE (Jetbrains WebStorm)
  • Add shortcut commands for building e.g. build:prod, build:uat etc
  • Validate our config so no required fields are missing/set to the default placeholder value

Installation

See the stand-alone installation guide.

Usage

# See the default configuration on screen
$ npm run ionic:serve
# See the dev configuration on screen
$ npm run ionic:serve:dev
# You get the pattern... two others defined are
$ npm run ionic:serve:uat
$ npm run ionic:serve:prod

For Cordova I've hardcoded commands in package.json:

$ npm run cordova:build:dev
$ npm run cordova:build:uat
$ npm run cordova:build:prod

Aside: it appears to work with the ios omitted from the package.json scripts, which allows flexibility to build for Android and iOS from one command. Not yet tested!

How to add custom environments

In this example we'll create the environment myspecialone.

  1. Create a file in src/environments called environment.myspecialone.ts.
  2. Open package.json and add scripts for this environment to the scripts section:
    "cordova:build:myspecialone": "cross-env NODE_ENV=myspecialone ionic cordova build ios --prod",
    "ionic:serve:myspecialone": "cross-env NODE_ENV=myspecialone ionic serve",
    
  3. That's it :)

Sorry it's not more automatic, I can't think of a way to auto-generate these commands. If you know how, tell me!

How it works

TBC

Why?

Why does this matter? Why go to all this effort?

Having used Symfony for many years I've come to appreciate config files that inherit other config files. It reduces repetition, allows composition, and makes working with them more pleasant.

Merging configurations: choices

Merging configurations is not as straightforward as it first seems. When you encounter arrays, do you replace or append? Do you merge or overwrite?

For this project, I've taken the approach of merging where possible. This keeps the code size down, but if you want more control here's other merging libraries that can be used -- and a summary of my research.

I chose this to merge our Webpack changes into the main configuration file. Why this and not webpack-config? Because it's more popular.

I also liked the way you can choose and change how the merge happens. It's very nice - check out the documentation.

Does the same job as above, with fewer options available. However it also takes care of loading the base webpack file, which gives a cleaner API in usage.

While both are called webpack-*, they can be used to merge our app configuration. There are also more single-purpose libraries we can use:

Allows control over how to merge arrays and array values. I chose this again because it's more widely used and I liked the idea of having control over the merging, if needed in the future.

An improved alternative to deepmerge (according to the developer) which handles shallow or deep copy or merging properly. For this setup, deep vs shallow doesn't matter.

Prior work

I spent a while looking for an existing library to use. My requirements were:

  • structure. .ini files have no structure beyond KEY=VALUE. I find groupings make config values easier to deal with
  • config inheritance. We have configuration variables that don't need to change between environments - they're in a file for easy editing.

dotenv

.env (dotenv) is great for setting current values, and there are work-arounds for storing different environments and cascading them (see this great post for how to with React)

TODO

  • Investigate if combining with dotenv makes sense, so sensitive credentials are stored separately from the environment files (i.e. not in Git)
  • Add CHANGELOG.md and UPGRADE.md to the repository
  • Investigate packaging as a NPM package
  • Switch to releases, and record breaking changes
  • More documentation, notes, rationale etc