2

I'm new to full-stack development but used to doing front-end dev using Grunt to manage my workflow. I have successfully pushed my MEAN stack to GitHub from my virtual server and have cloned it locally. However, it doesn't clone node_modules and I therefore can't run Grunt locally. I did npm install to install dependencies, but grunting to view app in browser breaks and throws errors.

My question is, what is the best way to run Grunt locally and make changes to front-end (only /public folder) and then push them up to master? Like I said, after cloning repo to local machine I have to "npm install" to install dependencies, and running grunt locally results in errors that point to server set up. I only want to manipulate /public folder and push changes to github, and pull them from server to implement.

Being new to dealing with the full-stack, what's the best practice for doing this? Is there a better way to do this? Should I only clone public folder in github (there isn't a configured grunt file in public though...)? Any help or direction is much appreciated. Please let me know if I need to clarify anything.

Update:

I've fixed all of my install errors, but saving my /public files on the Mean Stack does not change the browser view running on the correct port. I've gone through this scotch.io "Using Grunt in a mean-stack-application to try and specify the correct config settings from the ground up, but to now avail. Any suggestions on why my view isn't changing is appreciated. If it makes any difference, the mean stack file structure I'm working with is Digital Ocean's one-click MEAN stack.

Update 2:

The file that I'm changing that isn't reflected in livereload is public/modules/core/views/header.client.view.html.

Here is my gruntfile:

'use strict';

module.exports = function(grunt) {
    // Unified Watch Object
    var watchFiles = {
        serverViews: ['app/views/**/*.*'],
        serverJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'app/**/*.js'],
        clientViews: ['public/modules/**/views/**/*.html'],
        clientJS: ['public/js/*.js', 'public/modules/**/*.js'],
        clientCSS: ['public/modules/**/*.css'],
        mochaTests: ['app/tests/**/*.js']
    };

    // Project Configuration
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        watch: {
            options: { livereload: true },
            serverViews: {
                files: [watchFiles.serverViews],
                options: {
                    livereload: true
                }
            },
            serverJS: {
                files: watchFiles.serverJS,
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            clientViews: {
                files: watchFiles.clientViews,
                options: {
                    livereload: true,
                }
            },
            clientJS: {
                files: watchFiles.clientJS,
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            clientCSS: {
                files: watchFiles.clientCSS,
                tasks: ['csslint'],
                options: {
                    livereload: true
                }
            }
        },
        jshint: {
            all: {
                src: watchFiles.clientJS.concat(watchFiles.serverJS),
                options: {
                    jshintrc: true
                }
            }
        },
        csslint: {
            options: {
                csslintrc: '.csslintrc',
            },
            all: {
                src: watchFiles.clientCSS
            }
        },
        uglify: {
            production: {
                options: {
                    mangle: false
                },
                files: {
                    'public/dist/application.min.js': 'public/dist/application.js'
                }
            }
        },
        cssmin: {
            combine: {
                files: {
                    'public/dist/application.min.css': '<%= applicationCSSFiles %>'
                }
            }
        },
        nodemon: {
            dev: {
                script: 'server.js',
                options: {
                    nodeArgs: ['--debug'],
                    ext: 'js,html',
                    watch: watchFiles.serverViews.concat(watchFiles.serverJS)
                }
            }
        },
        'node-inspector': {
            custom: {
                options: {
                    'web-port': 1337,
                    'web-host': 'localhost',
                    'debug-port': 5858,
                    'save-live-edit': true,
                    'no-preload': true,
                    'stack-trace-limit': 50,
                    'hidden': []
                }
            }
        },
        ngAnnotate: {
            production: {
                files: {
                    'public/dist/application.js': '<%= applicationJavaScriptFiles %>'
                }
            }
        },
        concurrent: {
            default: ['nodemon', 'watch'],
            debug: ['nodemon', 'watch', 'node-inspector'],
            options: {
                logConcurrentOutput: true,
                limit: 10
            }
        },
        env: {
            test: {
                NODE_ENV: 'test'
            }
        },
        mochaTest: {
            src: watchFiles.mochaTests,
            options: {
                reporter: 'spec',
                require: 'server.js'
            }
        },
        karma: {
            unit: {
                configFile: 'karma.conf.js'
            }
        }
    });

    // Load NPM tasks
    require('load-grunt-tasks')(grunt);

    // Making grunt default to force in order not to break the project.
    grunt.option('force', true);

    // A Task for loading the configuration object
    grunt.task.registerTask('loadConfig', 'Task that loads the config into a grunt option.', function() {
        var init = require('./config/init')();
        var config = require('./config/config');

        grunt.config.set('applicationJavaScriptFiles', config.assets.js);
        grunt.config.set('applicationCSSFiles', config.assets.css);
    });

    // Default task(s).
    grunt.registerTask('default', ['lint', 'concurrent:default']);

    // Debug task.
    grunt.registerTask('debug', ['lint', 'concurrent:debug']);

    // Lint task(s).
    grunt.registerTask('lint', ['jshint', 'csslint']);

    // Build task(s).
    grunt.registerTask('build', ['lint', 'loadConfig', 'ngAnnotate', 'uglify', 'cssmin']);

    // Test task.
    grunt.registerTask('test', ['env:test', 'mochaTest', 'karma:unit']);
};
Himmel
  • 3,629
  • 6
  • 40
  • 77
  • What sort of problems? Remember, you need to have `grunt-cli` installed separately (globally, for best results) on each system first, then have the `grunt` runtime installed via `devDependencies` (`npm install --save-dev grunt`) in your package.json and installed via `npm install`. Also, consider Yeoman and the [`generator-angular-fullstack`](https://github.com/DaftMonk/generator-angular-fullstack) to get MEAN apps bootstrapped with best practices and plumbing done for you. – moribvndvs Nov 22 '14 at 01:26
  • I'll try reinstalling grunt-cli globally and grunt runtime when I'm off mobile. I can't remember what the error was specifically but will follow up with this too. Is there a way I can run grunt for front-end and not have to run node server (set Node_env)? Thanks for the feedback. – Himmel Nov 22 '14 at 01:33

1 Answers1

3

I would highly recommend not storing the contents of node_modules (or any other package/dependency manager such as Bower, Component, etc.) The whole purpose of these tools is to resolve this stuff as needed for you. Only the configuration or package manifest needs to be stored and your build process should make sure this stuff is kept up to date or created if missing for you.

When you work with a MEAN stack on Node, the first thing you usually do is make sure Node and your build environment is created. This only needs to be done once. First install Node, next install your global build tools. For grunt, the command is npm install -g grunt. You need to run this once on every computer or server that builds your project. Next, you install any package or dependency managers globally. For MEAN stack, this is frequently Bower. So next execute npm install -g bower.

A list of steps for setting up tools (for every machine that develops or builds your project, assuming you use Grunt) (basic example; YMMV):

  1. Install Node.js
  2. Install any additional platform requirements (Mongo, etc.)
  3. Open a shell with node in the path and execute npm install -g grunt-cli bower

Now, you can initialize your project as usual to kick it off. You can piece it together as you see fit, or use a scaffolding tool (such as Yeoman) or seed project. Develop your project and commit as usual.

If the next guy (or build server) wants to work with your project, they'll need to set up the dev environment just as you (from this example, they just install node and then execute npm install -g grunt-cli bower). Then they clone your repo, navigate to the directory, and execute npm install. They should now be able to build and run the project. For build servers, you will likely need to automate this prep stage. Any build service worth its salt will accomodate this, and how to do it depends on the software you're using.

For deployment, it really depends on where and how you're deploying. Some systems (Heroku, for example, which monitors changes to a specific branch) can link right to your repo and when it detects pushed changes, it'll clone or fetch upstream, run the build system command you specify, and host from the Node.js app you specify. In other cases, you may need to build your project ahead of time and upload the results of your build task to the server. Your mileage may vary depending on the circumstances of your project.

Examining the boilerplate from something like Yeoman's generator-angular-fullstack generator I linked above, you can derive best practices for setting up a MEAN stack.

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
  • Thanks for fleshing this out. This makes sense, I installed all global dependencies on the development environment (grunt, mongo, bower, etc.). When I npm install after cloning, it seems to download all of the dependencies successfully. However, when I use grunt, I get 'Could not connect to MongoDB.. Error: failed to connect to localhost: 27017'. Is there further configuration that Mongo requires? Thanks. – Himmel Nov 22 '14 at 19:49
  • Also, when I save simple changes to the html source file, the page reloads but the DOM doesn't reflect the changes. It seems grunt is only reflecting the content of the server build and not the development files I cloned to my machine. – Himmel Nov 22 '14 at 20:07
  • you need to install mongodb! if already done, start it with `mongod` – hereandnow78 Nov 22 '14 at 23:33
  • Thanks for the tip, it seems that I got Mongo running successfully. However, changes to my views (HTML files) are still not updating on my port when running Grunt. The page reloads, but the changes don't take place. – Himmel Nov 26 '14 at 01:34
  • Because HackedByChinese answered my original question, and my follow-up question exceeds the scope of the original, I've posted my follow-up question here: http://stackoverflow.com/questions/27175835/grunt-livereload-not-showing-html-changes – Himmel Nov 27 '14 at 17:06