20

I recently switched over to grunt 0.4.5 and it changed how connect works.

I previously used connect-modrewrite and it worked pretty well (had some issues with urls generated by /:parameter).

Here is the old version that worked with grunt 0.4.1 from generator-angular 0.8.0 with the middleware part modded by me to use html5mode.

connect: {
    options: {
        port: 9000,
        hostname: '*IP HERE*',
        livereload: 35729,
        middleware: function (connect, options) {
            var optBase = (typeof options.base === 'string') ? [options.base] : options.base;
            return [require('connect-modrewrite')(['!(\\..+)$ / [L]'])].concat(
                optBase.map(function(path){ return connect.static(path); })
            );
        }
    },
    livereload: {
        options: {
            open: true,
            base: [
                '.tmp',
                '<%= yeoman.app %>'
            ]
        }
    },

Here is the new version from generator-angular 0.9.0-1

connect: {
    options: {
        port: 9000,
        hostname: '*IP HERE*',
        livereload: 35729
    },
    livereload: {
        options: {
            open: true,
            middleware: function (connect) {
                return [
                    connect.static('.tmp'),
                    connect().use(
                        '/bower_components',
                        connect.static('./bower_components')
                    ),
                    connect.static(appConfig.app)
                ];
            }
        }
    },

How can I change this to use mod-rewrite or any other method to achieve html5mode?

I tried using the method provided here: https://gist.github.com/nnarhinen/7719157 I combined it to create the following:

middleware: function (connect) {
    return [
        connect.static(modRewrite(['^[^\\.]*$ /index.html [L]'])),
        connect.static('.tmp'),
        connect().use(
            '/bower_components',
            connect.static('./bower_components')
        ),
        connect.static(appConfig.app)
    ];
}

This allows me to view the normal view, but the modRewrite part does not seem to do what it needs to in order to get to any other view via url.

Chenmunka
  • 685
  • 4
  • 21
  • 25
Mark Lenser
  • 1,070
  • 8
  • 12
  • Strange, I'm currently using grunt 0.4.5 and the middleware worked fine. I tried a number of different implementations and this was the only one that worked with a 'base' option. – Evan Plaice Mar 02 '15 at 01:55

4 Answers4

65

If anyone else stumbles across this here is the fix:

(the only line added was the modRewrite line)

livereload: {
    options: {
        open: true,
        middleware: function (connect) {
            return [
                modRewrite(['^[^\\.]*$ /index.html [L]']),
                connect.static('.tmp'),
                connect().use(
                    '/bower_components',
                    connect.static('./bower_components')
                ),
                connect.static(appConfig.app)
            ];
        }
    }
},

Make sure you have the following declared at the top of your grunt file:

var modRewrite = require('connect-modrewrite');
Mark Lenser
  • 1,070
  • 8
  • 12
  • 7
    also make sure connect-modrewrite is installed `npm install connect-modrewrite --save-dev` – jubalm Sep 14 '14 at 05:39
  • 1
    Thanks!! there is a lot of articles explaining this for old versions. I tested around 5 different ways already without success. I tested this solution and works perfectly. – leticia Nov 19 '14 at 20:59
  • @Kryx what about support for an alternative base? – Evan Plaice Mar 02 '15 at 01:53
  • @EvanPlaice No idea how that interacts with this at all. – Mark Lenser Mar 02 '15 at 09:49
  • 2
    I followed this approach but still have issues with /:params. The url to load main.css is getting prefixed with current url instead of base path. It is like http://localhost:9000/mypage/styles/main.css (failed to load 404) instead of http://localhost:9000/styles/main.css. – mnm Sep 23 '15 at 11:41
  • Hey thanks, worked perfectly for my case. Another question, @Kryx do you know correspoding configuration for Nginx, if I want to achieve same behaviour in production server? – ikenator Feb 24 '18 at 22:05
7

Given how other answers are pretty verbose and don't preserve default grunt-contrib-connect middlewares, I came up with solution that uses dedicated middleware – connect-history-api-fallback:

npm install connect-history-api-fallback --save-dev
var history = require('connect-history-api-fallback')
//...
connect: {
    options: {
        middleware: function(connect, options, middleware) {
            middleware.unshift(history())
            return middleware
        },
        //...
    },
    //...
}
3

Though above accepted answer is correct. Im adding the config which I use, it works perfectly on CentOs.

Below 1 to 3 steps are for making Angularjs clean URL work in your local machine using $ grunt serve

But if you want to make them run fine on Server, specially nginx you will also need to update nginx config. (step 4)

  1. $ npm install connect-modrewrite --save

  2. Edit your gruntfile.js. Add at the top of the file

    var modRewrite = require('connect-modrewrite');
    

Then in your middleware:

middleware: function (connect) {
    return [
        modRewrite(['^[^\\.]*$ /index.html [L]']),
        connect.static('.tmp'),
        connect().use('/bower_components',
        connect.static('./bower_components')),
        connect.static(config.app)
    ];
}

for e.g.

// Generated on 2015-11-09 using generator-angular 0.14.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
var modRewrite = require('connect-modrewrite');

module.exports = function (grunt) {

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

3.Then go to Livereload middleware, add modRewrite

livereload: {
    options: {
        middleware: function (connect) {
            return [
                modRewrite([
                  '^[^\\.]*$ /index.html [L]'
                ]),
                connect.static('.tmp'),
                connect().use('/bower_components', connect.static('./bower_components')),
                connect.static(config.app)
            ];
        }
    }
},

4.Edit NGINX config:

server {
    server_name yoursite.com;
    root /usr/share/html;
    index index.html;

    location / {
    try_files $uri $uri/ /index.html;
    }
}

Hope it helps :)

Igor Raush
  • 15,080
  • 1
  • 34
  • 55
STEEL
  • 8,955
  • 9
  • 67
  • 89
1

Here is my solution, adapted to generator-angular setup, but can be used anywhere. It allows a rewriting syntax (the interesting part is the example livereload configuarion).

connect: {
  options: {
    port: 9000,
    // Change this to '0.0.0.0' to access the server from outside.
    hostname: 'localhost',
    livereload: 35729,
    // Modrewrite rule, connect.static(path) for each path in target's base
    middleware: function (connect, options) {
      var optBase = (typeof options.base === 'string') ? [options.base] : options.base,
          middleware = [require('connect-modrewrite')(['!(\\..+)$ / [L]'])]
            .concat(optBase.map(function (path) { 
              if (path.indexOf('rewrite|') === -1) {
                return connect.static(path);
              } else {
                path = path.replace(/\\/g, '/').split('|');
                return  connect().use(path[1], connect.static(path[2]))
              }
            }));

      return middleware;
    }
  },
  livereload: {
    options: {
      open: true,
      base: [
        '.tmp',
        'rewrite|/bower_components|./bower_components',
        'rewrite|/app/styles|./app/styles', // for sourcemaps
        '<%= yeoman.app %>'
      ]
    }
  }
}
pilau
  • 6,635
  • 4
  • 56
  • 69