How do I create a Gulp Task to do minification and source maps to a .min file properly - gulp

I'm trying to create a gulp task to compress and create a source map at the same time. The compression and source map creation works, but I can't seem how to figure out how to get the output names right when using the gulp-rename plugin.
To simplify: I have a source.js file in the /src folder and I want to create both the .min.js and .js.map file in the /dist folder.
Here's what I have:
gulp.task('scripts', function () {
// compressed
gulp.src(['src/*.js'])
.pipe(sourcemaps.init({ includeContent: false, sourceRoot: './' }))
.pipe(uglify())
.pipe(sourcemaps.write('./', {
sourceMappingURL: function(file) {
return file.relative + '.map';
}
}))
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('./dist'));
});
This works in that it creates the following in /dist:
jquery-resizable.min.js (all good - compressed, map ref and right name)
jquery-resizable.js.min.map (map is there, but name is bad - should be jquery-resizable.js.map)
I've tried a ton of variations but I can't figure out how to get the map and compression to build and get the correct file names.
I also tried renaming the files in a separate step, but due to the async nature of gulp, that doesn't work reliably - sometimes it works sometimes it doesn't so that doesn't seem like an option either.
What am I missing?
I'm not married to creating the sourcemaps in just this way, but what is the proper way to do this? All the examples I've seen seem to do what I do above, except they don't rename the output file to min.js which seems like an important part of the process.

I would suggest using gulp-filter to remove the .map files from the pipeline during the rename.
var jsFilter = require('gulp-filter')([ '*.js', ], { restore: true, });
gulp.src(['src/*.js'])
.pipe(sourcemaps.init({ includeContent: false, sourceRoot: './' }))
.pipe(uglify())
.pipe(sourcemaps.write('./', {
sourceMappingURL: function(file) {
return file.relative + '.map';
}
}))
.pipe(jsFilter)
.pipe(rename({ suffix: '.min' }))
.pipe(jsFilter.restore)
.pipe(gulp.dest('./dist'));
That said, our workflow does the rename before the sourcemaps and it generates the maps correctly.
gulp.src(['src/*.js'])
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(rename({ suffix: '.min', }))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./dist'));

#bdukes answer mostly solved my problem and led me to the right solution. I'm posting the actual solution that worked for me in the specific case I mentioned above which is based on his answer.
One of my issues was related to not being able to generate the raw sources files along with the compressed and map files. I was able to make it work with an extra step explicitly moving the files in a separate .pipe(gulp.dest('./dist')) operation:
gulp.task('scripts', function () {
// compress and source map
gulp.src(['src/*.js'])
.pipe(sourcemaps.init({ includeContent: false, sourceRoot: './' }))
.pipe(uglify())
.pipe(sourcemaps.write('.', {
sourceMappingURL: function(file) {
return file.relative + '.map';
}
}))
.pipe(jsFilter)
.pipe(rename({ suffix: '.min' }))
.pipe(jsFilter.restore)
.pipe(gulp.dest('./'));
// also copy source files
gulp.src(['src/*.js'])
.pipe(gulp.dest('./dist'));
});
Moral of the story - don't overthink things - I was trying to get it all to work in one operation.

Related

gulp less compile only changed file

i have problem, when i run gulp watch -> run task styles:build, and all of my less files was recompile. How i can compile only changed file?
gulp.task('styles:build', function () {
return gulp.src(pathes.src.styles)
.pipe(changed(pathes.build.styles), {extension: '.css'})
.pipe(print(function(filepath) {
return "➔ file was changed: " + filepath;
}))
.pipe(plumber())
.pipe(less({
plugins: [autoprefix, cleanCSSPlugin],
paths: ['./', 'web/styles']
}))
.pipe(gulp.dest(pathes.build.styles))
});
gulp.task('watch', function() {
gulp.watch(pathes.src.styles, ['styles:build'])
});
You need to modify the line below to add a closing parenthsis:
.pipe(changed(pathes.build.styles, {extension: '.css'}))
Also as I cautioned the first time the task is run it probably will pass through all files.
i think i found solution
just install lessChanged = require('gulp-less-changed')
and include him before less pipe
.pipe(lessChanged())
.pipe(less())

gulp stops server on error even with jshint included in gulpfile.js

I don't know why the server still stops whenever there's an error in my js files even though I have jshint in my gulpfile. I installed jshint and included it in my project because it reports errors in js files, but it's still failing. How can I fix this?
gulp.task('scripts', () => {
return gulp.src('assets/js/src/*.js')
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish', {beep: true}))
.pipe(concat('main.js'))
.pipe(gulp.dest('assets/js/build/'))
.pipe(uglify())
.pipe(gulp.dest('assets/js/'))
.pipe(browserSync.stream({stream: true}));
});
gulp-jshint does what you says it does: it reports errors in JavaScript files. Nothing more, nothing less. It doesn't prevent defective JavaScript files from reaching later pipe stages like uglify() (which throws up and thus stops your server if there's any error in a JavaScript file).
If you want to prevent defective JavaScript files from wrecking your server, you need to put all the jshint stuff into it's own task and make sure that task fails when any JavaScript file has an error:
gulp.task('jshint', () => {
return gulp.src('assets/js/src/*.js')
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish', {beep: true}))
.pipe(jshint.reporter('fail'))
});
Then you need to make your scripts task depend on that jshint task:
gulp.task('scripts', ['jshint'], () => {
return gulp.src('assets/js/src/*.js')
.pipe(concat('main.js'))
.pipe(gulp.dest('assets/js/build/'))
.pipe(uglify())
.pipe(gulp.dest('assets/js/'))
.pipe(browserSync.stream({stream: true}));
});
Now your scripts task will only run when the jshint task was successful. If any JavaScript file was defective jshint will output the error to the console while your server continues to run using the last good version of your JavaScript.
The simplest fix would be to use gulp-plumber to handle the error a little more gracefully:
var plumber = require("gulp-plumber");
gulp.task('scripts', () => {
return gulp.src('assets/js/src/*.js')
.pipe(plumber())
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish', {beep: true}))
.pipe(concat('main.js'))
.pipe(gulp.dest('assets/js/build/'))
.pipe(uglify())
.pipe(gulp.dest('assets/js/'))
.pipe(browserSync.stream({stream: true}));
});
Personally, I don't like that solution because it will prevent your minified file from being updated. Here's what I would recommend:
var jshintSuccess = function (file) {
return file.jshint.success;
}
gulp.task('scripts', () => {
return gulp.src('assets/js/src/*.js')
.pipe(sourcemaps.init())
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish', {
beep: true
}))
.pipe(gulpif(jshintSuccess, uglify()))
.pipe(concat('main.js'))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('assets/js/'))
.pipe(browserSync.stream({
stream: true
}));
});
First, notice that I'm not writing to multiple destinations. Instead, I'm using sourcemaps so that you don't need unminified code. Second, I'm using gulp-if to conditionally pipe your code through uglify based on the results of jshint. Code with errors will bypass uglify so that it still makes it into to your destination file.
Now, you can inspect and debug it with the developer tools.
Note: I recommend this for local development only. I wouldn't connect this to a continuous integration pipeline because you'll only want good code to make it into production. Either set up a different task for that or add another gulp-if condition to prevent broken code from building based on environment variables.

Sourcemaps are in wrong location or have incorrect paths

I've been trying to get gulp sass and gulp sourcemaps to do exactly what I want and I'm finding it hard. I want to take a sass entry file (src/sass/index.scss), generate an output file (dist/css/index.css) and a separate sourcemap for that index file (dist/css/index.css.map) which has a sourceRoot set to the project base (absolute path: /home/damon/projects/test) and the sourcemap entries to be relative to that path.
Here's what I tried:
attempt 1: straight example code from gulp-sass:
var sassEntry = 'src/sass/index.scss';
gulp.src(sassEntry)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist/css'));
Outcome: this inlines the sourcemap into the CSS file so I can't tell if it's right or not.
attempt 2: write it to separate file
gulp.src(sassEntry)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/css'));
Outcome: writes a separate sourcemap, but the sourceRoot says '/sources/' (WTF is that?!, it doesn't exist and I never configured it)
and the paths are all relative to the sass entry file, not the project base, which is also going to be meaningless when my browser tries to locate the source files.
attempt 3: try to fix the sourceroot (also I found includeContent: false)
gulp.src(sassEntry)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.',{includeContent: false, sourceRoot: __dirname}))
.pipe(gulp.dest('dist/css'));
Outcome: the sourceroot is now my working folder which is nice, the content isn't included which is nice, but the files in the sourcemap are still relative to the sass entry file not to the sourceRoot, so my map is still useless
attempt 4: Set the gulp.src base
gulp.src(sassEntry, { base: __dirname })
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.',{includeContent: false, sourceRoot: __dirname}))
.pipe(gulp.dest('dist/css'));
Outcome: Infuriatingly, setting the base on gulp.src fixes the sourcemap - sourceRoot is still correct and the source file paths are relative to the sourceRoot, BUT it now outputs to dist/css/src/sass/index.css which is wrong. WTF!
attempt 5: use absolute paths
gulp.src(sassEntry, { base: __dirname })
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.',{includeContent: false, sourceRoot: __dirname}))
.pipe(gulp.dest(__dirname + '/dist/css'));
Outcome: no change, it still outputs to the same deep structure in dist.
If anyone can enlighten me on how to do this I would be forever grateful.
While Sven's answer is perfectly good, I also found an answer to my own question by getting a deeper understanding of how gulp works (which I was trying to avoid), and apparently gulp stores each matched file with a path, so adding:
{ base: __dirname }
in the gulp.src makes it that each matched file has the full path from the base, which then causes them to output with the full relative path from wherever you set the base to. The solution I ended up with was to use gulp-flatten, which removes those relative paths from files in the pipeline, so my eventual function looked like this:
gulp.src(sassEntry, { base: __dirname })
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.',{includeContent: false, sourceRoot: __dirname}))
.pipe(flatten())
.pipe(gulp.dest(__dirname + '/dist/css'));
easy once you understand more about what it's trying to do I guess.
Since your attempt 4 does everything you want except place the resulting files in the wrong location, the easiest fix would be to just change that location with gulp-rename after the sourcemaps have been generated:
gulp.src(sassEntry, { base: __dirname })
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: __dirname}))
.pipe(rename({dirname:''}))
.pipe(gulp.dest('dist/css'));

Gulp not watching correctly

I'm new to using gulp and I think I have it setup correctly, but it does not seem to be doing what it should be doing.
My gulpfile.js has
gulp.task('compass', function() {
return gulp.src('sites/default/themes/lsl_theme/sass/**/*.scss')
.pipe(compass({
config_file: 'sites/default/themes/lsl_theme/config.rb',
css: 'css',
sass: 'scss'
}))
.pipe(gulp.dest('./sites/default/themes/lsl_theme/css'))
.pipe(notify({
message: 'Compass task complete.'
}))
.pipe(livereload());
});
with
gulp.task('scripts', function() {
return gulp.src([
'sites/default/themes/lsl_theme/js/**/*.js'
])
.pipe(plumber())
.pipe(concat('lsl.js'))
.pipe(gulp.dest('sites/default/themes/lsl_theme/js'))
// .pipe(stripDebug())
.pipe(uglify('lsl.js'))
.pipe(rename('lsl.min.js'))
.pipe(gulp.dest('sites/default/themes/lsl_theme/js'))
.pipe(sourcemaps.write())
.pipe(notify({
message: 'Scripts task complete.'
}))
.pipe(filesize())
.pipe(livereload());
});
and the watch function
gulp.task('watch', function() {
livereload.listen();
gulp.watch('./sites/default/themes/lsl_theme/js/**/*.js', ['scripts']);
gulp.watch('./sites/default/themes/lsl_theme/sass/**/*.scss', ['compass']);
});
when I run gulp, the result is
[16:14:36] Starting 'compass'...
[16:14:36] Starting 'scripts'...
[16:14:36] Starting 'watch'...
[16:14:37] Finished 'watch' after 89 ms
and no changes are registered.
for file structure, my gulpfile.js is in the root directory and the sass, css, and js are all in root/sites/default/themes/lsl_theme with the sass folder containing the folder 'components' full of partials.
My assumption is that you are on windows? Correct me if I'm wrong.
There is this problem that gulp-notify tends to break the gulp.watch functions. Try commenting out
// .pipe(notify({
// message: 'Scripts task complete.'
// }))
and see if the problem still exists.
If that does fix the issue, a solution from this thread may be helpful.
You can use the gulp-if
plugin in combination with
the os node module
to determine if you are on Windows, then exclude gulp-notify, like
so:
var _if = require('gulp-if');
//...
// From https://stackoverflow.com/questions/8683895/variable-to-detect-operating-system-in-node-scripts
var isWindows = /^win/.test(require('os').platform());
//...
// use like so:
.pipe(_if(!isWindows, notify('Coffeescript compile successful')))
It turns out that a large part of my issue was just simply being a rookie with Gulp. When I removed 'scripts' from my gulp watch it started working.
I then made the connection that it was watching the same directory that it was placing the new concatenated and minified js files in so it was putting the new file, checking that file, and looping over and over causing memory issues as well as not allowing 'compass' to run.
After creating a 'dest' folder to hold the new js everything started working just peachy.

How to rename a compiled sass file in a Gulp task

I'm trying to write a simple gulp task that takes a scss file called manifest.scss and after compiling and minifying the file it saves the result into a destination folder as app.css
The following task does almost everything I want beside renaming the file (the output is build/css/manifest.css)
gulp.task('sass', function() {
gulp.src("src/sass/manifest.scss")
.pipe(sass({ style: 'compressed' }))
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'));
});
So, I have tried gulp-rename and I have update the task as follows:
gulp.task('sass', function() {
gulp.src("src/sass/manifest.scss")
.pipe(sass({ style: 'compressed' }))
.pipe(minifyCSS())
.pipe(rename('app.css'))
.pipe(gulp.dest('build/css'));
});
This produces the build/css/app.css file but it is totally blank.
How can I rename the compiled file?
Thanks