I am using Gulp to start a web application. I have the following basic code for my gulpfile.js:
var gulp = require('gulp'),
nodemon = require('gulp-nodemon');
gulp.task('default', function () {
nodemon({
script: 'server.js'
, ext: 'js html'
, env: { 'NODE_ENV': 'development' }
})
})
Using Gulp, I want to check for dependencies and if they are not available then install them and then run 'script.js'. How can this be done?
I have the following package.json:
{
"name": "sample-project",
"version": "1.0.0",
"description": "Displays users and user details",
"main": "server.js",
"dependencies": {
"jquery" : “>=1.5.1",
“bootstrap”: ">= 3.0.0”
}
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "Arihant Jain",
"license": "ISC"
}
You can run npm install independently from an executing task using node's child_process as such:
var gulp = require('gulp');
var nodemon = require('gulp-nodemon');
var child_process = require('child_process');
gulp.task('default', function () {
// Run npm install from the child process
child_process.exe('npm install', function(err, stdout, stderr){
// if everything goes well
if(!err){
// run nodemon
nodemon({
script: 'server.js'
, ext: 'js html'
, env: { 'NODE_ENV': 'development' }
})
}
});
})
Given your requirement:
Using Gulp, I want to check for dependencies and if they are not
available then install them...
That is exactly what npm install does. It checks the local package.json and proceeds to install missing packages.
So, I worked it around in a way by using gulp-run. I actually run the command npm install.
gulpfile looks like this:
var gulp = require('gulp'),
nodemon = require('gulp-nodemon')
run = require('gulp-run')
runSequence = require('run-sequence')
open = require('gulp-open');
gulp.task('default', function() {
runSequence('dependencies',
'start',
'uri');
});
gulp.task('dependencies', function() {
return run('npm install').exec();
})
gulp.task('uri', function(){
gulp.src(__filename)
.pipe(open({uri: 'http://localhost:3000/index.html'}));
});
gulp.task('start', function () {
nodemon({
script: 'server.js'
, ext: 'js html'
, env: { 'NODE_ENV': 'development' }
})
})
Below is my current Gulpfile.js and when I'm attempting to run gulp minify I'm getting the error, "Task 'minify' is not in your gulpfile". However, I'm able to run gulp sass with no issues. I also have each module installed with npm.
module.exports = function(gulp) {
'use strict'
var sass = require('gulp-sass');
var browserSync = require('browser-sync');
var cssnano = require('gulp-cssnano');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
gulp.task('sass', function() {
return gulp.src('app/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
.pipe(browserSync.reload({
stream: true
}))
});
gulp.task('minify', function() {
return gulp.src('app/css/**/*.css')
.pipe(cssnano())
.pipe(concat('style.min.css'))
.pipe(gulp.dest('app/css/min'))
.pipe(browserSync.reload({
stream: true
}))
});
gulp.task('watch', ['browserSync', 'sass', 'cssnano'], function() {
gulp.watch('app/scss/**/*.scss', ['sass']);
gulp.watch('app/*.html', browserSync.reload);
gulp.watch('app/js/**/*.js', browserSync.reload);
});
gulp.task('browserSync', function() {
browserSync({
server: {
baseDir: 'app/'
},
})
})
}
You should not treat the gulpfile.js as a node module and drop the following part
module.exports = function(gulp) {
(don't forget to remove the closing curly (}) at the end of the gulpfile.js)
Then, you should require gulp
var gulp = require('gulp');
That should make it run smoothly.
Why it does run your gulp sass, I don't know. I just created a simple test file in a fresh directory, and it didn't recognise the sass task for me. Maybe you installedgulp, sass or both globally at some point (e.g. earlier project).
Trying to get gulp-mocha to run mocha test on JavaScript files and return the result using nyan cat reporter. When I run the command I get the following error
'mocha' errored after 244 ms
ReferenceError in plugin 'gulp-mocha'
Message:
document is not defined
Here is the task in my Gulpfile.js
var gulp = require('gulp');
var mocha = require ('gulp-mocha');
gulp.task('mocha', function () {
return gulp.src(jsSources, {read: false})
.pipe(mocha({reporter: 'nyan'}));
});
Do I need to install the 'nayn' reporter as a dependency also?
No, you don't need to install it as a dependency. Try this:
var gulp = require('gulp');
var mocha = require('gulp-mocha');
gulp.task('default', function () {
gulp.watch(['./mocha/*'], ['mocha']);
});
gulp.task('mocha', function(){
return gulp.src('./mocha/*', {read: true})
.pipe(mocha({reporter: 'nyan'}));
});
I want to avoid duplicate code, so i am trying to load grunt task from Grunt file "a" and use them in gruntfile "b".
that means: i want to see all task of "a" in file "b" (but without code), just setup like a reference or template to another gruntfile.
here is grunt file "b":
module.exports = function (grunt) {
'use strict';
var karmaGrunt = './../../grunt',
abortHandler = function () {
var errors = grunt.fail.errorcount,
warnings = grunt.fail.warncount;
if (errors > 0 || warnings > 0) {
//run rocketlauncher python script and then stop the grunt runner.
grunt.task.run(["shell:rocketlauncher", "fatal"]);
}
},
fatal = function () {
// this function stops grunt and make the jenkins build red.
grunt.fail.fatal('failed');
};
require("grunt-load-gruntfile")(grunt);
// load grunt task from another file and add it.
grunt.loadGruntfile(karmaGrunt);
//grunt needs to continue on error or warnings, that's why we have to set the force property true
grunt.option('force', true);
grunt.initConfig({
shell: {
options: {
execOptions: {
cwd: '../scripts'
}
},
'rocketlauncher': {
command: './runRocketLauncher.sh'
}
}
});
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('build-process', ['karma', 'abortHandler']);
grunt.registerTask('abortHandler', abortHandler);
grunt.registerTask('fatal', fatal);
}
here is file "a":
module.exports = function (grunt) {
"use strict";
var eConfig = '../e-specs/karma.config.js',
dConfig = '../d-specs/karma.config.js',
cConfig = '../c-specs/karma.config.js';
grunt.initConfig({
karma: {
options: {
reporters: ['progress', 'coverage', 'threshold']
},
c: {
configFile: cConfig
},
d: {
configFile: dConfig
},
e: {
configFile: eConfig
}
}
});
grunt.loadNpmTasks('grunt-karma');
};
my file b load the task "Karma" but if i run only the grunt file of a i have 3 nested task ("e","c","d") but if i load them from another file, the only task i can see is "karma"
the error is:
No "karma" targets found.
Warning: Task "karma" failed. Used --force, continuing.
Done, but with warnings.
If i run the same task in file "a" directly the task is working like a charm.
There is a grunt plugin to load another Gruntfile: grunt-load-gruntfile
With this you can merge two Grunt configurations, including the defined tasks.
Here is an example:
./Gruntfile.js:
module.exports = function (grunt) {
require("grunt-load-gruntfile")(grunt);
grunt.loadGruntfile("web"); //loads the Gruntfile from the folder web/
grunt.registerTask('showConfig', "shows the current config", function(){
console.log(JSON.stringify(grunt.config(), null, 2));
});
};
and the second Gruntfile in ./web/Gruntfile.js.
module.exports = function (grunt) {
grunt.config("WebConfig", "Configuration from the Gruntfile in web/Gruntfile.js");
grunt.registerTask('server', "runs the server",function(){
console.log("just shows this message");
});
};
running grunt showConfig executes the task from the first Gruntfile and displays the configuration, including the parameter defined in ./web/Gruntfile.js.
running grunt server executes the task from ./web/Gruntfile.js.
Background: I am compiling 2 dependent TypeScript files to js, which produces also source maps (one source map per file) using tsc 1.0
I'm using -m commonjs and then use browserify to generate a single bundle.js
However I noticed that I get the original source map references twice in the bundle, which doesn't seem to work.
Passing --debug doesn't seem to do the trick either.
I had a feeling this issue: https://github.com/substack/node-browserify/issues/325 is somewhat related, but I couldn't figure out how the issue was resolved.
Also https://github.com/substack/browser-pack was suggested, but again I don't fully understand how to use it, is it a replacement to browserify?
Bottom line, I would like to merge the 2 js files but "merge" the js to ts source maps using browserify. Is that possible?
tsify is a browserify plugin that is better and replaces e.g. typescriptifier.
npm install tsify browserify watchify
You use tsify like this:
browserify src/index.ts -p tsify --debug -o build/index.js
Notice that this supports browserify --debug switch, no extra tricks required. So you can also use it with watchify like this:
watchify src/index.ts -p tsify --debug -o build/index.js
Using the minifyify browserify plugin I believe you can use TypeScript with Browserify and retain the source maps. After compiling the TypeScript files you should be able to pass the "entry" file (the one that imports the other one via commonjs syntax) through browserify with the minifyify plugin.
var browserify = require('browserify'),
bundler = new browserify();
bundler.add('entry.js');
bundler.plugin('minifyify', {map: 'bundle.js.map'});
bundler.bundle({debug: true}, function (err, src, map) {
if (err) console.log(err);
fs.writeFileSync('bundle.js', src);
fs.writeFileSync('bundle.js.map', map);
});
Here is my working solution:
var settings = {
projectName : "test"
};
gulp.task("bundle", function() {
var mainTsFilePath = "src/main.ts";
var outputFolder = "bundle/src/";
var outputFileName = settings.projectName + ".min.js";
var pkg = require("./package.json");
var banner = [
"/**",
" * <%= pkg.name %> v.<%= pkg.version %> - <%= pkg.description %>",
" * Copyright (c) 2015 <%= pkg.author %>",
" * <%= pkg.license %>",
" */", ""
].join("\n");
var bundler = browserify({
debug: true,
standalone : settings.projectName
});
// TS compiler options are in tsconfig.json file
return bundler.add(mainTsFilePath)
.plugin(tsify)
.bundle()
.pipe(source(outputFileName))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(header(banner, { pkg : pkg } ))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(outputFolder));
});
I created example project.
You can run it with $(npm bin)/gulp build --env=dev for development environment and source maps will be generated.
There is gulpfile.js:
'use strict';
var path = require('path'),
gulp = require('gulp'),
del = require('del'),
typescript = require('gulp-typescript'),
sourcemaps = require('gulp-sourcemaps'),
browserify = require('browserify'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer'),
uglify = require('gulp-uglify'),
gutil = require('gulp-util'),
inject = require('gulp-inject'),
babel = require('gulp-babel'),
argv = require('yargs').argv;
var devEnvironment = 'dev',
prodEnvironment = 'prod',
environment = argv.env || prodEnvironment,
isDevelopment = environment === devEnvironment;
var projectPath = __dirname,
srcDir = 'src',
srcPath = path.join(projectPath, srcDir),
buildDir = path.join('build', environment),
buildPath = path.join(projectPath, buildDir),
distDir = 'dist',
distRelativePath = path.join(buildDir, distDir),
distPath = path.join(buildPath, distDir);
var tsSrcPath = path.join(srcPath, 'typescript'),
tsGlob = path.join(tsSrcPath, '**', '*.ts'),
tsBuildPath = path.join(buildPath, 'tsc');
var indexHtmlName = 'index.html',
indexJsName = 'index.js';
var distIndexJsPath = path.join(distPath, 'index.js'),
distIndexHtmlPath = path.join(distPath, indexHtmlName);
var tsProject = typescript.createProject('tsconfig.json');
console.log('Environment: ' + environment);
gulp.task('clean', function () {
return del([buildPath]);
});
gulp.task('tsc', ['clean'], function () {
var stream = gulp.src([tsGlob]);
if (isDevelopment) {
stream = stream
.pipe(sourcemaps.init());
}
stream = stream
.pipe(typescript(tsProject))
.pipe(babel({
presets: ['es2015']
}));
if (isDevelopment) {
stream = stream.pipe(sourcemaps.write({sourceRoot: tsSrcPath}));
}
return stream.pipe(gulp.dest(tsBuildPath));
});
gulp.task('bundle', ['tsc'], function () {
var b = browserify({
entries: path.join(tsBuildPath, indexJsName),
debug: isDevelopment
});
var stream = b.bundle()
.pipe(source(indexJsName))
.pipe(buffer());
if (!isDevelopment) {
stream = stream.pipe(uglify());
}
return stream
.on('error', gutil.log)
.pipe(gulp.dest(distPath));
});
gulp.task('build', ['bundle'], function() {
return gulp.src(path.join(srcPath, indexHtmlName))
.pipe(inject(gulp.src([distIndexJsPath], {read: false}), {ignorePath: distRelativePath, addRootSlash: true}))
.pipe(gulp.dest(distPath));
});
You should pay attention to lines:
stream = stream.pipe(sourcemaps.write('', {sourceRoot: tsSrcPath})); - write inline source maps with sourceRoot pointing to your typescript sources path. Inline maps are written directly to .js files generated by tsc to build/dev/tsc.
debug: isDevelopment - in development environment make browserify generate his own source maps for resulting bundle build/dev/dist/index.js file so it will have source maps referencing .js files from build/dev/tsc which in turn have source maps referencing .ts files from src/typescript.
With this setup you will be able to see and debug .ts files in browser:
I faced similar issue when trying to debug my Angular2 app running in Chrome in Visual Studio Code (Using Debugger for Chrome extension)
I use gulp as my task runner and my setup is as follows:
Typescript files -> tsc -> intermediate es5 js -> browserify (plus uglify in production build) -> compiled bundle
My directory structure is as follows:
|- src
|- my .ts files here
|- main.ts - my entry file
|- dist
|- intermediate files go here
|- web
|- app.js - final bundle
|- app.js.map - final bundle map
|- gulpfile.js
gulpfile.js:
var gulp = require('gulp'),
tsc = require('gulp-typescript'),
browserify = require('browserify'),
uglify = require('gulp-uglify'),
sourcemaps = require('gulp-sourcemaps'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer');
gulp.task('tsc', [], () => {
return gulp.src(['src/**/*.ts'])
.pipe(sourcemaps.init())
.pipe(tsc({
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}))
.pipe(sourcemaps.write(null, {
"sourceRoot": function(file) {
let parts = file.relative.split('\\');
let root = Array(parts.length + 1).join('../') + 'src';
return root;
}
}))
.pipe(gulp.dest('dist/'));
});
gulp.task('bundle', ['tsc'], () => {
let b = browserify({
entries: 'dist/main.js',
debug: true,
});
return b.bundle()
.pipe(source('app.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('./', {
"sourceRoot": "../",
}))
.pipe(gulp.dest('web/'));
})
gulp.task('default', ['bundle']);
Explanation/reasoning:
For some reason browserify doesn't read and parse .js.map files linked in .js file (via special comment at the end) but it does when the source map is embedded in js file. So, by passing null instead of path to sourcemaps it will embed it at the end of generated .js file.
Next issue I noticed was that sourcemaps doesn't automatically follow directory structure (add '../' to sourceRoot when it goes to next directory level), so I made a quick function to complement this. Keep in mind that it only works on Windows - on Linux you'd have to change split character.
function(file) {
let parts = file.relative.split('\\'); // put '/' here on Linux
let root = Array(parts.length + 1).join('../') + 'src';
return root;
}
Certainly there is a way to detect correct path separator, I'm debugging only on Windows thus it's not important for my purposes.
I hope it helps someone, cause I've spent whole Sunday morning tracking down this problem.