6

I have an angular app setup that's using grunt, but I'd like to be able to use grunt as a preprocessor to replace variables and I haven't been able to find anything that matches my needs.

For instance, if I change the name of my main app module to "someAppName" in a config file, I'd like to just use something like "ENV.APP_NAME" in various html and js files and have that replaced by an appropriate value for that environment.

Ideally I'd like to have a config file somewhere along these lines, either as a .json file or using module.exports to expose an object, which specifies values for different environments:

{
    APP_NAME:{
        dev: "someAppDev",
        prod: "someApp"
    },
    API_BASE:{
        dev: "localhost:8000/",
        prod: "https://www.some-site.com/api/"
    }
}

and then I could create a grunt task and pass it either "dev" or "prod" to have it run the preprocessor and replace each instance with the corresponding value. I've found this https://github.com/STAH/grunt-preprocessor but the examples are confusing and I don't think it's quite what I'm looking for.

Is there anything like this that allows you to create preprocessed environment variables and read them from an external config file, or am I forced to build my own grunt plugin? Has anyone achieved something similar with grunt?

EDIT: I've begun building a grunt plugin for this specific task, once it's done and tested I'll post it up on npm

ejfrancis
  • 2,925
  • 4
  • 26
  • 42
  • Maybe this would help: http://mindthecode.com/how-to-use-environment-variables-in-your-angular-application – changtung Feb 07 '15 at 22:25
  • I've seen that, it does add environment variables but not as a preprocessor. so I'd have access to environment variables within a controller but I couldn't, say, have it as part of an html file or json and have it replaced as I'm looking for – ejfrancis Feb 07 '15 at 22:29
  • Maybe independent program that would read all html js files before and replace specified tags? It may be written in java. Something like replace all. – changtung Feb 07 '15 at 22:36
  • yeah I'm sure there are some tools out there to do it but I'd like a grunt task so it can be part of the automated workflow if possible. adding a watch task to the config file to recompile, similar to using a watch task with SASS files, would be nice – ejfrancis Feb 07 '15 at 22:54
  • maybe this: http://stackoverflow.com/questions/18271744/set-env-variables-based-on-grunt-task – changtung Feb 07 '15 at 22:57
  • but what if you change variable in html files at build time? – changtung Feb 07 '15 at 22:59
  • How I have done this in my project is, have one configuration file which defines variables in JSON objects which are used everywhere, and then search replace in that file as per env passed to grunt task... Used https://github.com/erickrdch/grunt-string-replace for replace task.. – Vishwanath Feb 08 '15 at 06:38
  • how do you go about reading the config file and passing that to the grunt-string-replace task? I've begun building a grunt plugin for this myself and I've gotten it to read config from a file, all I have to do is find a way to replace each string. I was thinking of using grunt-string-replace but I can't find a way to run one grunt task within a grunt plugin – ejfrancis Feb 08 '15 at 15:57

3 Answers3

6

Use grunt-ng-constant.

Npm install this plugin:

npm install grunt-ng-constant --save-dev

Then in grunt write to config file:

ngconstant: {
  // Options for all targets
  options: {
    space: '  ',
    wrap: '"use strict";\n\n {%= __ngModule %}',
    name: 'config',
  },
  // Environment targets
  development: {
    options: {
      dest: '<%= yeoman.app %>/scripts/config.js'
    },
    constants: {
      ENV: {
        myvar1: 'Hello from devel',
        myname: 'John Doe'
      }
    }
  },
  production: {
    options: {
      dest: '<%= yeoman.dist %>/scripts/config.js'
    },
    constants: {
      ENV: {
        myvar1: 'Hello',
        myname: 'John Doe'
      }
    }
  }
},

Then add to grunt task:

 grunt.task.run([
    'clean:server',
    'ngconstant:development',
    'connect:livereload',
    'watch'
  ]);

Running task would generate config.js with constants defined in gruntfile. Then add config.js to your index.html:

<script src="/scripts/config.js" />

Inject it to your app:

var app = angular.module('myApp', [ 'config' ]);

And inject into controller:

.controller('MainCtrl', function ($scope, $http, ENV) {
      console.log(ENV.myvar1);
  });

You can set different variables for production and different for development, by setting it in gruntfile and setting ng:production or ng:development.

Refer this article explaining the procedure for more information.

Vishwanath
  • 6,284
  • 4
  • 38
  • 57
changtung
  • 1,614
  • 15
  • 19
  • This is useful, though I don't think it's exactly what the OP is looking for. I might make use of it, though, so thanks. – Michael Oryl Feb 07 '15 at 23:42
2

You could do something making use of grunt-string-replace while loading some config values into Grunt from a JSON file. Suppose you had this in myconfig.json:

{
  "appName": "MyGrowth",
  "version": "1.1.2",
}

Then perhaps load the config into Gruntfile.js from JSON with something like:

grunt.initConfig({
    myConfig: grunt.file.readJSON('myconfig.json'),
// ... the rest of the stuff in your initConfig goes sure
}

Then you would have some sort of task like this maybe:

'string-replace': {
    version: {
        options: {
            replacements: [
                {
                    pattern: /MY_VERSION/ig,
                    replacement: '<%= myConfig.version %>'
                }
            ]
        },
        files: [{
          expand: true,
          cwd: 'src/',
          src: '**/*.js',
          dest: 'dist/'
        }]
    }
}

That would make actual changes to the files if you had 'src' and 'dest' in the same folder, otherwise it would put the processed files in the dest folder.

I've not actually used this recipe for preprocessing, but I use it for other types of things like replacing tags in a framework with the app name as configured in package.json.

Hope it helps.

Michael Oryl
  • 20,856
  • 14
  • 77
  • 117
  • Very helpful answer! Regarding the final part of the OP's question ("create a grunt task and pass it either `dev` or `prod`"), [outaTiME/grunt-config](https://github.com/outaTiME/grunt-config) can define target-specific configuration values, to be used when doing the string replacement. See the ["Environment variable in source"](https://github.com/outaTiME/grunt-config#environment-variable-in-source-with-grunt-replace) section of the README for a good example. – TachyonVortex May 06 '15 at 16:31
2

Alright I ended up building a grunt plugin just for this task because it's something that I really needed. It allows you to place environment variable definitions in a .json file like I described in my original question and replaces all instances in a specified file or list of files.

https://github.com/ejfrancis/grunt-envpreprocess

So if you have something like this


HTML

<head>
        <title>ENV.APP_NAME</title>
 </head>
 <script src="ENV.API_BASE/user/create">

JS

 var version = "ENV.APP_VERSION";
 alert(version);

The task will replace it with this


HTML

<head>
    <title>AppDev</title>
</head>
<script src="http://localhost:8000/user/create">

JS

var version = "0.1.0";
alert(version);

ejfrancis
  • 2,925
  • 4
  • 26
  • 42