Gulp: Convert cloned images to webp, but keep the original file extension - gulp

I have the following gulp worklow:
I clone jpg and png images of a source folder, convert the cloned images to webp, and copy both (original and webp) to the dest folder.
So after I let it run, the dest folder contains the following files:
example.jpg
example.webp
foo.png
foo.webp
So far, so good. But what I would like to achieve ist that the original file extension (.jpg or .png) should be maintained in the webp version, and the .webp extension should simply be appended. So the dest folder should look like this:
example.jpg
example.jpg.webp
foo.png
foo.png.webp
How would that be possible in the following worklow?
import * as clone from "gulp-clone";
import * as size from "gulp-size";
import * as webp from "gulp-webp";
function copyImages() {
var sink = clone.sink(); // init sink
return src(conf.images.src + '**/*')
.pipe(sink) // clone image
.pipe(webp()) // convert cloned image to webp
.pipe(sink.tap()) // restore original image
.pipe(dest(conf.images.dest))
.pipe(size({
title: 'images'
}));
}

It's probably not the most efficient solution, but it works:
import * as clone from "gulp-clone";
import * as webp from "gulp-webp";
import * as revertPath from "gulp-revert-path";
import * as rename from "gulp-rename";
import * as size from "gulp-size";
function copyImages() {
var sink = clone.sink(); // init sink
return src(conf.images.src + '**/*')
.pipe(sink) // clone image
.pipe(webp()) // convert cloned image to webp
.pipe(revertPath())
.pipe(rename(function (path) {
path.extname = path.extname + '.webp';
}))
.pipe(sink.tap()) // restore original image
.pipe(dest(conf.images.dest))
.pipe(size({
title: 'images'
}));
}

Related

gulp: set multiple gulp.src and respective gulp.dest (on gulp-sass example)

Project structure:
📁 development
 📁 public
  📁 pug
  📁 1sass
  📁 2css
 📁 admin
  📁 pug
  📁 3sass
  📁 4css      
I add digits to folder names to imitate the situations when gulp can not guess somehow which output folder is respects to input ones.
Now, I want to compile .sass files in public/1sass and admin/3sass to .css and put it in public/2css and admin/4css respectively:
📁 public/1sass → 📁 public/2css
📁 admin/3sass → 📁 admin/4css
How I need to setup the sass task in gulpfile? Even if we put the paths array to gulp.src, how gulp will understand which output path respects to input ones?
Maybe gulp.parallel() becomes available in gulp 4.x will do?
Update
Two things that I did not understand yet:
How I should to setup the multiple output paths in gulp.dest()?
I learned that file.dirname = path.dirname(file.dirname); removes the last parent directory of the relative file path.But how I should to setup it for each of 1sass ans 3sass? Via array?
const gulp = require('gulp'),
sass = require('gulp-sass'),
path = require('path'),
rename = require('gulp-rename');
gulp.task('sass', function(){
return gulp.src([
`development/public/1sass/*.sass`,
`development/public/3sass/*.sass`])
.pipe(sass())
// As I can suppose, here we must to setup output paths for each input one
.pipe(rename(function(file){
file.dirname = path.dirname(file.dirname);
}))
.pipe(/* ??? */);
});
Simply in case of dynamic src and you want respective same dest (as received in src) then you can use following
Example Suppose we have array of scss file:
var gulp = require('gulp');
var sass = require('gulp-sass');
var scssArr = [
'src/asdf/test2.scss',
'src/qwerty/test1.scss'
];
function runSASS(cb) {
scssArr.forEach(function(p){
gulp.src(p, {base:'.'})
.pipe(sass({outputStyle: 'compressed'}))//outputStyle is optional or simply sass()
.pipe(gulp.dest('.')); //if othe folder including src path then use '/folder-name' instead of '.', so output path '/folder-name/{src-received-path}'
})
cb();
}
exports.runSASS = runSASS; // gulp runSASS
Run command gulp runSASS This will create following files:
src/asdf/test2.css
src/qwerty/test1.css
Happy Coding..
See my answer to a similar question: Gulp.dest for compiled sass. You should be able to modify that easily for your purposes. If you have trouble edit your question with your code and you will get help.
Even if we put the paths array to gulp.src, how gulp will understand which output path respects to input ones?
Gulp will retain the relative paths for each file that it processes. So, in your case, the files in public/1sass will all have their relative path info after sass processing still intact. And the files in admin/3sass will all have their relative path info as well. Thus you only need to find a way to modify that path info (parent directory structure) to redirect the files to a desired destination.
In your case, that would involve removing the immediate parent directory and replacing it with the 'css' directory. Gulp-rename is one way, not the only way, to do that. In gulp-rename you can examine and modify the parent directory structure - it is just string manipulation.
Maybe gulp.parallel() becomes available in gulp 4.x will do?
No, gulp.parallel() will not be of any help here. It will just order the execution and finishing of different tasks. It would not be necessary or of any real help in your case.
[EDIT]
var gulp = require("gulp");
var rename = require("gulp-rename");
var path = require("path");
var sass = require("gulp-sass");
gulp.task('modules-sass', function () {
// using .scss extensions for sass files
return gulp.src(`development/**/*.scss`)
.pipe(sass())
.pipe(rename(function (file) {
// file.dirname before any changes
console.log("file.dirname 1 = " + file.dirname);
// this removes the last directory
var temp = path.dirname(file.dirname);
console.log(" temp = " + temp);
// now add 'Css' to the end of the directory path
file.dirname = path.join(temp, 'Css');
console.log(" after = " + file.dirname);
}))
.pipe(gulp.dest('development'));
});
// this is the directory structure I assumed
// gulpfile.js is just above the 'development' directory
// development / Admin / Sass1 / file1.scss
// development / Admin / Sass1 / file2.scss
// development / Admin / Sass2 / file3.scss
// development / Admin / Sass2 / file4.scss
// development / Admin / Css
// development / Public / Sass1 / file5.scss
// development / Public / Sass1 / file6.scss
// development / Public / Sass2 / file7.scss
// development / Public / Sass1 / file8.scss
// development / Public / Css

Bower. How to set base forder for main directories?

I override the main directories for the Bootstrap in bower.json:
"main" : [
"./dist/css/bootstrap.css",
"./dist/css/bootstrap.css.map",
"./dist/css/bootstrap-theme.css",
"./dist/css/bootstrap-theme.css.map",
"./dist/js/bootstrap.js",
"./dist/fonts/*",
"./less/**"
]
And I want that a files were copied with css, js, fonts folders. I.e. can I set '/dist/' as a base forder?
Or can I do it in the gulp task? In gulpfile.js I wrote:
var files = mainBowerFiles('**/dist/**');
return gulp.src( files, {base: 'C:/Users/Den/Desktop/HTML5-Demo/bower_components/bootstrap/dist/'} )
.pipe( gulp.dest('public_html/libs') );
But I'm forced to write a full path which of course is bad. Is there way to use a relative path?
Also I want to ask what does '.' in the beginning of the directories mean?
To use relative path you need to get current working directory.
var path = require('path');
var cwd = process.cwd(); // current working directory
var basePath = path.resolve(cwd, "bower_components/bootstrap/dist");
The next code works:
var stream = gulp.src(files, {base: './bower_components/bootstrap/dist'})

how to modify config files using gulp

I use gulp to configure complex local setup and need to auto-edit files.
The scenario is:
determine if certain file contains certain lines after certain other line (found using regular expression)
if line is not found, insert the line.
optionally, delete some lines found in the file.
I need this to amend system configuration files and compile scenarios.
What would be the best way to do it in gulp?
Gulp is plain javascript. So what I would do if I were you is to create a plugin to pipe to the original config file.
Gulp streams emit Vinyl files. So all you really got to do is to create a "pipe factory" that transforms the objects.
It would look something like this (using EventStream):
var es = require('event-stream');
// you could receive params in here if you're using the same
// plugin in different occasions.
function fixConfigFile() {
return es.map(function(file, cb) {
var fileContent = file.contents.toString();
// determine if certain file contains certain lines...
// if line is not found, insert the line.
// optionally, delete some lines found in the file.
// update the vinyl file
file.contents = new Buffer(fileContent);
// send the updated file down the pipe
cb(null, file);
});
}
gulp.task('fix-config', function() {
return gulp.src('path/to/original/*.config')
.pipe(fixConfigFile())
.pipe(gulp.dest('path/to/fixed/configs');
});
Or you can use vinyl-map:
const map = require('vinyl-map')
const gulp = require('gulp')
const modify = map((contents, filename) => {
contents = contents.toString()
// modify contents somehow
return contents
})
gulp.task('modify', () =>
gulp.src(['./index.js'])
.pipe(modify)
.pipe(gulp.dest('./dist'))
})

creating tar archives using gulp

I'm using gulp-tar to create a tar file... how do I add a top level folder so that when the user runs tar -xzf myArchive.tar it extracts into a specific folder.
here's my code:
gulp.task('prod', ['min', 'gittag'], function() {
//copy all files under /server into a zip file
gulp.src('../server/**/*')
.pipe(tar('xoserver' + '-'+ gittag +'.tar'))
.pipe(gzip())
.pipe(gulp.dest('../prod'));
});
The above creates a tar.zip file all right, but I have to be careful to add a -C <folder> while extracting, else the files get extracted to the current folder.
[edited]
What I'm trying to do here is generate a tarball of the format xoserver-alpha-d414ddf.tar.gz which, when extracted with a tar xvf will create a folder xoserver-alpha-d414ddf and unpack all the files under it. Essentially I am trying to add new folder name above my packed files.
If I add a base option, the folder extracted to is just server
[ANSWER]
Thanks to ddprrt for a good answer. I am reproducing the final code in case someone else wants to use a similar strategy of embedding the git tag into the name of the tarball for distribution/testing.
gulp.task('gittag', function(cb) { // generate the git tag
git.exec({args : 'branch -v'}, function (err, stdout) {
var lines = stdout.split('\n');
for (var l in lines) {
if (lines[l][0] == '*') {
var words = lines[l].split(/\s+/);
gittag = words[1]+ '-' + words[2];
console.log('Gittag is %s', gittag);
break;
}
}
cb();
});
});
gulp.task('min', ['runbmin', 'template', 'vendor']); // generate min files
gulp.task('prod', ['min', 'gittag'], function() { // create tarball
//copy all files under /server into a zip file
return gulp.src('../server/**/*')
.pipe(rename(function(path) {
path.dirname = 'server-' + gittag + '/' + path.dirname;
}))
.pipe(tar('xoserver-'+gittag+'.tar'))
.pipe(gzip())
.pipe(gulp.dest('../prod'));
});
This is what the base option is for.
gulp.task('prod', ['min', 'gittag'], function() {
return gulp.src('../server/**/*', { base: '../server/' })
.pipe(tar('xoserver' + '-'+ gittag +'.tar'))
.pipe(gzip())
.pipe(gulp.dest('../prod'));
});
With it you can tell gulp which paths to include when dealing with the globs you receive.
Btw. Don't forget to return streams or call the done callback in your task. Helps gulp orchestrating your build pipeline
As for the second question, you can use gulp-rename task to change the directory where your virtual files are located. Would be something like
.pipe(rename(function(path) {
path.dirname = 'whatever/' + path.dirname
}));

Gulp-js pass only saved file on to task

I want to pass a saved file which does not need to be compiled or does not need to be saved to a new location to gulp-tap so I can run an external script on it.
Right now I watch a complete directory and on each save I upload the whole directory:
gulp.task('shopify_theme',function(){
gulp.src( './theme/**/*.liquid' )
.pipe(tap(function(file){
upload(file);
}));
})
And this is the upload part (theme is an application that uploads assets to shopify)
var upload = function( file ){
var splitPath = file.path.split('theme/').pop();
run('theme upload ' + splitPath, { cwd: 'theme' }).exec();
};
Every time I save a liquid file in the /theme directory all files (theme/**/*.liquid) are uploaded. gulp-changed doesn't work as at the time the task runs the destination and the source are the same.
What's the best way to only upload the changed file?
You can use gulp.watch to watch for changes to individual files:
var upload = function(filePath){
var splitPath = filePath.split('theme/').pop();
run('theme upload ' + splitPath, { cwd: 'theme' }).exec();
};
gulp.watch('./theme/**/*.liquid', function(evnt) {
upload(evnt.path);
});