How grunt command works? - node.js

What command is actually executed?
Like using npm start with following included in package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
}
npm start runs node index.js behind the scenes,
Similarly, what does grunt run (behind the scenes)?

When you call the Grunt task runner, it runs whatever Grunt plugins you have specified in your Gruntfile in the order you specify.
A Grunt plugin consists of a single task file. That file is essentially nothing but a Node.js script that carries out the task in question. It has access to the settings passed to the plugin and can use Grunt's file API to access the filesystem, but otherwise it's just a Node.js script.
It's not hard to write a Grunt plugin and if you're interested in learning more about Grunt it's a good way to become more familiar with it. I personally have written several static site generators as Grunt plugins and it was very useful. Grunt task file ie. gruntfile.js looks something like this,
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};
when you run command grunt uglify basically it runs the task defined under uglify.You can find more in their get started guide here

grunt command executes Gruntfile.js, it does this through node, however it passes grunt configurations and functions to the plugins used inside Gruntfile. thats why you write
module.exports = function(grunt) {
// grunt parameter is passed from grunt-cli
// which contains grunt functions and utilities
// that you can use to configure the tasks etc.
});

Related

How do you work with dev, pre and prod with Snowpack?

I want to define environment variables in my package.json where I'm using Snowpack, but as far as I know Snowpack sets the NODE_ENV automatically based on dev vs build.
Is it possible to define variables for 3 modes instead of 2, I mean:
development
preproduction
production
These are my scripts in my package.json:
"scripts": {
"start": "snowpack dev",
"build": "snowpack build NODE_ENV=pre",
"build:production": "snowpack build NODE_ENV=pro"
}
However, import.meta.env.MODE returns production for the 2 types of build.
I couldn't make it work, maybe there is another way of doing this.
My use case was not exactly the same, but similar, and you should be able to generate as many different environments as you want with this technique.
I was able to do this by writing a custom snowpack plugin to use .env files with the dotenv npm package, and two separate snowpack.config.js files; one for dev and one for prod. Here's how...
Add dotenv to your project: npm i -D dotenv
Create this new file. This will be our custom snowpack plugin
// env-loader.js
const dotenv = require('dotenv');
module.exports = function plugin(snowpackConfig, { path }) {
dotenv.config({ path: path });
return { name: 'Custom plugin from StackOverflow' };
};
Create your .env files; .env.develop and .env.production
Create your snowpack.config files; snowpack-develop.config.js and snowpack-production.config.js
Add your custom plugin to both snowpack.config files. Be sure to give it the correct path to your custom plugin, and the correct path to your .env files.
// snowpack-develop.config.js
const path = require('path');
module.exports = {
plugins: [
['./path/to/env-loader', { path: path.resolve(process.cwd(), '.develop.env') }],
],
};
Finally, add your npm scripts. Point your dev scripts to the snowpack-develop file, and prod to the snowpack-production file.
"scripts": {
"develop": "snowpack dev --config ./snowpack-develop.config.js",
"build:dev": "snowpack build --config ./snowpack-develop.config.js",
"build:prod": "snowpack build --config ./snowpack-production.config.js"
},
In snowpack, environment variables must include the prefix SNOWPACK_PUBLIC_, and to use a variable in your code you would access it like this: import.meta.env.SNOWPACK_PUBLIC_MY_VARIABLE. It runs a find-and-replace at build time.
Snowpack Config Docs
Snowpack Plugin Docs
Snowpack Env Docs

Run npm script with nodemon command

I am testing GraphQL Server from Apollo and what to integrate nodemon to it. Here's sample file structure :
build/
src/
server.js
Here my npm scripts looks like this
"scripts": {
"start": "babel --presets es2015,stage-2 server.js -d build/ && node build/server.js",
"dev": "nodemon server.js" // Sample code here
}
What npm run start would do is to convert ES6 code into build/server.js using babel and execute it. This would start the server correctly.
What I want is to watch for changes in server.js or in src/ and restart the server if changes occur. Here I want to execute npm run start command if any changes occur. What is the correct 'nodemon' command to my need. Its better if I could use npm run dev like command to start development using nodemon.
you can use gulpjs to watch any changes in specific folders and then command it to do something.
With your sample, you want to convert the code to es6 too. so it also need gulp-bable and . you can include babel-preset-stage-2 if you want. So you can simply put the code below in gulpfile.js
gulp.task('build-es2015', () => {
return gulp.src('server.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('build'));
});
gulp.task('watch', () => {
gulp.watch(['./app/*.js'], ['build-es2015'])
})
Basically, the task 'watch' will keep watching the specific files. When they are saved then it will to execute the task 'build-es2015' to convert to es6.
And then nodemon, it needs gulp-nodemon then you can do on gulpfile.js
gulp.task('server', () => {
nodemon({
script: 'build/server.js',
ext: 'js',
ignore: [
'server.js',
'node_modules/**',
'test/**',
'build/**'
]
})
.on('restart', () => { console.log(`Server restarted!`) })
})
The above will keep watching build/server.js'. whenever it is changed, nodemon will automatically restart the server.
And the last piece for gulpfile.js
gulp.task('dev', ['server', 'watch'])
Include the tasks that need to be executed for gulp command.
$ gulp dev
or with npm command
"scripts": {
"start": "gulp dev"
}
so you can npm run start as well.
And dont forget to require all packages in gulpfile.js
const gulp = require('gulp')
const babel = require('gulp-babel')
const nodemon = require('gulp-nodemon')

How to use Grunt to report error for node js

My question is how do I get grunt to report error like the default node start script. In my latest node/express project, I am trying to use Grunt task manager and changed the start script from
"start": "node ./bin/www"
to
"start": "grunt"
The problem is that when I run "npm start" or "grunt", the error report is not sufficient.
[nodemon]app crashed - waiting for file changes before starting //error reported by grunt
When I change the start script back to "node ./bin/www", i was able to see error like
Error: Cannot find module mongoose //error reported by node
This shows me that I have forgot to npm install mongoose. So how can i get Grunt to report that error?
Edit: another example of error reported by node
MissingSchemaError: Schema hasnt been registed for model "Admin" //error reported by node
Edit 2: The gruntfile.js config is as follow
/**** other configurations to minify and uglify ****/
nodemon: {
dev: {
script: 'server.js'
}
},
// run watch and nodemon at the same time
concurrent: {
options: {
logConcurrentOutput: true
},
tasks: ['nodemon', 'watch']
}
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-concurrent');
There are two ways you can achieve this:
You can specify the --stack option on the command line:
"start": "grunt --stack"
You can enable the stack option in your gruntfile.js:
grunt.option("stack", true);

How to define a gulp task that initiates watchify and gulp-stylus?

I am trying to create a composite gulp task which runs 2 separate watch tasks at the same time:
gulp.task('watch-css', watchCssTask); // uses gulp-stylus
gulp.task('watch-js', watchJsTask); // uses watchify (browserify)
What is the correct way to achieve the following with gulp?
gulp.task('watch', watchCssAndJsTask);
For instance, with vanilla npm tasks I would just do this:
"scripts": {
"watch-css": ...
"watch-js": ...
"watch": "npm run watch-css && npm run watch-js",
}
The following seems to work:
gulp.task('watch', [ 'watch-css', 'watch-js' ]);

How to deploy node that uses Gulp to heroku

I'm using gulp and also gulp plugins like gulp-minify-css, gulp-uglify etc (that listed as npm dependencies for my application).
Also I don't commit npm_modules folder and public folder, where all generated files are. And I can't figure out how to build my app (I have gulp build command) after deploy and setup my server (it's already looking for public folder).
It seems me a bad idea to commit before upload. Maybe there are some gentle decisions... Any thoughts?
Forked from: How to deploy node app that uses grunt to heroku
I was able to get this to work by adding this into my "package.json" file:
"scripts": {
"start": "node app",
"postinstall": "gulp default"
}
The postinstall script is run after the build pack. Check this for more information. The only annoying thing is that all of your dependencies have to live under "dependencies" instead of having separate "devDependencies"
I didn't need to do anything else with buildpacks or configuration. This seems like the simplest way to do it.
I wrote about the process I used here
You can do it!
There were a few key measures that helped me along the way:
heroku config:set NODE_ENV=production - to set your environment to 'production'
heroku config:set BUILDPACK_URL=https://github.com/krry/heroku-buildpack-nodejs-gulp-bower - to enable a customised Heroku buildpack. I incorporated elements of a few to make one that worked for me.
A gulp task entitled heroku:production that performs the build tasks that need to happen on the heroku server when NODE_ENV===production. Here's mine:
var gulp = require('gulp')
var runSeq = require('run-sequence')
gulp.task('heroku:production', function(){
runSeq('clean', 'build', 'minify')
})
clean, build, and minify are, of course separate gulp tasks that do the magic gulpage
If your application lives in /app.js, either:
(A) make a Procfile in the project root that contains only: web: node app.js, or
(B) add a start script to your package.json:
"name": "gulp-node-app-name",
"version": "10.0.4",
"scripts": {
"start": "node app.js"
},
And like #Zero21xxx says, put your gulp modules in your normal dependencies list in package.json, not in the devDependencies, which get overlooked by the buildpack, which runs npm install --production
The easiest way I found was:
Setup gulp on package.json scripts area:
"scripts": {
"build": "gulp",
"start": "node app.js"
}
Heroku will run build before starting the app.
Include gulp on dependencies instead of devDevependencies, otherwise Heroku won't be able to find it.
There is more relevant info about it on Heroku Dev Center: Best Practices for Node.js Development
How to deploy to Heroku (or Azure) with git-push
// gulpfile.js
var gulp = require('gulp');
var del = require('del');
var push = require('git-push');
var argv = require('minimist')(process.argv.slice(2));
gulp.task('clean', del.bind(null, ['build/*', '!build/.git'], {dot: true}));
gulp.task('build', ['clean'], function() {
// TODO: Build website from source files into the `./build` folder
});
gulp.task('deploy', function(cb) {
var remote = argv.production ?
{name: 'production', url: 'https://github.com/<org>/site.com', branch: 'gh-pages'},
{name: 'test', url: 'https://github.com/<org>/test.site.com', branch: 'gh-pages'};
push('./build', remote, cb);
});
Then
$ gulp build --release
$ gulp deploy --production
See also
https://github.com/koistya/git-push
https://github.com/kriasoft/react-starter-kit (tools/deploy.js)
There's a specific startup script that Heroku provides;
"scripts": {
"start": "nodemon app.js",
"heroku-postbuild": "gulp"
}
note that in your gulpfile.js (gulpfile.babel.js if you es6-ifed your gulp build process), you should have a task name default which will be automatically run after the dependencies are installed via Heroku.
https://devcenter.heroku.com/articles/nodejs-support#heroku-specific-build-steps
Heroku finds that there is a gulpfile in your project and expects there to be a heroku:production task (in the gulpfile). So all you need to do is register a task that matches that name:
gulp.task("heroku:production", function(){
console.log('hello'); // the task does not need to do anything.
});
This is enough for heroku to not reject your app.
I had to take a slightly different to get this working because I'm using browsersync:
package.json
"scripts": {
"start": "gulp serve"
}
gulp.js
gulp.task('serve', function() {
browserSync({
server: {
baseDir: './'
},
port: process.env.PORT || 5000
});
gulp.watch(['*.html', 'css/*.css', 'js/*.js', 'views/*.html', 'template/*.html', './*.html'], {cwd: 'app'}, reload);
});
Setting the port to be environment port is important to prevent error when deploying in Heroku. I did not need to set a postinstall script.
It's possible to piggyback any command you want over the top of npm install. Much like the linked question in your post, you can add an install directive in scripts within package.json that will run after all the node deps have been installed that does the build.
Your main issue will be sorting out the correct relative paths for everything.
{
...
scripts:{
install: "YOUR GULP BUILD COMMAND"
}
...
}

Resources