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
I have a large minified css file and a small non-minified file.
I'll be changing the non-minified file regularly, but I want them combined.
However re-minifying a large minified file takes WAY too long.
What I want is something like this ...
var cssnano = require('gulp-cssnano');
var cssjoin = require('gulp-cssjoin');
gulp.task('cssjoin', function() {
gulp.src( 'changing.css' )
.pipe(cssnano())
.pipe(rename( 'changed.min.css' ))
.pipe(gulp.dest( '.' ));
gulp.src( ['big.min.css','changed.min.css'] )
.pipe(cssjoin())
.pipe(gulp.dest( 'final.min.css' ))
});
Basically, first minify the small file, then join it to the end of the large one. The code above doesn't work of course, tasks need a return in the function, etc.
I also want to delete 'changed.min.css' once the join is done.
I tried various combinations of things for 3 hours without finding a working solution. How would one go about solving this?
You can use gulp-add-src to append big.min.css to the stream. That way you only run cssnano() on changed.css, not both files.
Afterwards you use gulp-concat to combine both files into a single new final.min.css:
var gulp = require('gulp');
var cssnano = require('gulp-cssnano');
var concat = require('gulp-concat');
var addsrc = require('gulp-add-src');
gulp.task('cssjoin', function() {
return gulp.src('changing.css')
.pipe(cssnano())
.pipe(addsrc.append('big.min.css'))
.pipe(concat('final.min.css'))
.pipe(gulp.dest('.'));
});
There's no need to delete an intermediary changed.min.css with this since none is written to disk. All operations occur in-memory.
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'})
Normally we can run gulp task from console via something like gulp mytask. Is there anyway that I can pass in parameter to gulp task? If possible, please show example how it can be done.
It's a feature programs cannot stay without. You can try yargs.
npm install --save-dev yargs
You can use it like this:
gulp mytask --production --test 1234
In the code, for example:
var argv = require('yargs').argv;
var isProduction = (argv.production === undefined) ? false : true;
For your understanding:
> gulp watch
console.log(argv.production === undefined); <-- true
console.log(argv.test === undefined); <-- true
> gulp watch --production
console.log(argv.production === undefined); <-- false
console.log(argv.production); <-- true
console.log(argv.test === undefined); <-- true
console.log(argv.test); <-- undefined
> gulp watch --production --test 1234
console.log(argv.production === undefined); <-- false
console.log(argv.production); <-- true
console.log(argv.test === undefined); <-- false
console.log(argv.test); <-- 1234
Hope you can take it from here.
There's another plugin that you can use, minimist. There's another post where there's good examples for both yargs and minimist: (Is it possible to pass a flag to Gulp to have it run tasks in different ways?)
If you want to avoid adding extra dependencies, I found node's process.argv to be useful:
gulp.task('mytask', function() {
console.log(process.argv);
});
So the following:
gulp mytask --option 123
should display:
[ 'node', 'path/to/gulp.js', 'mytask', '--option', '123']
If you are sure that the desired parameter is in the right position, then the flags aren't needed.** Just use (in this case):
var option = process.argv[4]; //set to '123'
BUT: as the option may not be set, or may be in a different position, I feel that a better idea would be something like:
var option, i = process.argv.indexOf("--option");
if(i>-1) {
option = process.argv[i+1];
}
That way, you can handle variations in multiple options, like:
//task should still find 'option' variable in all cases
gulp mytask --newoption somestuff --option 123
gulp mytask --option 123 --newoption somestuff
gulp mytask --flag --option 123
** Edit: true for node scripts, but gulp interprets anything without a leading "--" as another task name. So using gulp mytask 123 will fail because gulp can't find a task called '123'.
There's an official gulp recipe for this using minimist.
https://github.com/gulpjs/gulp/blob/master/docs/recipes/pass-arguments-from-cli.md
The basics are using minimist to separate the cli arguments and combine them with known options:
var options = minimist(process.argv.slice(2), knownOptions);
Which would parse something like
$ gulp scripts --env development
More complete info in the recipe.
Passing a parameter to gulp can mean a few things:
From the command line to the gulpfile (already exemplified here).
From the main body of the gulpfile.js script to gulp tasks.
From one gulp task to another gulp task.
Here's an approach of passing parameters from the main gulpfile to a gulp task. By moving the task that needs the parameter to it's own module and wrapping it in a function (so a parameter can be passed).:
// ./gulp-tasks/my-neat-task.js file
module.exports = function(opts){
opts.gulp.task('my-neat-task', function(){
console.log( 'the value is ' + opts.value );
});
};
//main gulpfile.js file
//...do some work to figure out a value called val...
var val = 'some value';
//pass that value as a parameter to the 'my-neat-task' gulp task
require('./gulp-tasks/my-neat-task.js')({ gulp: gulp, value: val});
This can come in handy if you have a lot of gulp tasks and want to pass them some handy environmental configs. I'm not sure if it can work between one task and another.
If you want to use environment params and other utils as well such as log, you can use gulp-util
/*
$npm install gulp-util --save-dev
$gulp --varName 123
*/
var util = require('gulp-util');
util.log(util.env.varName);
Update
gulp-util is now deprecated. You can use minimist instead.
var argv = require('minimist')(process.argv.slice(2));
console.dir(argv);
#Ethan's answer would completely work. From my experience, the more node way is to use environment variables. It's a standard way to configure programs deployed on hosting platforms (e.g. Heroku or Dokku).
To pass the parameter from the command line, do it like this:
Development:
gulp dev
Production:
NODE_ENV=production gulp dev
The syntax is different, but very Unix, and it's compatible with Heroku, Dokku, etc.
You can access the variable in your code at process.env.NODE_ENV
MYAPP=something_else gulp dev
would set
process.env.MYAPP === 'something_else'
This answer might give you some other ideas.
Here is my sample how I use it. For the css/less task. Can be applied for all.
var cssTask = function (options) {
var minifyCSS = require('gulp-minify-css'),
less = require('gulp-less'),
src = cssDependencies;
src.push(codePath + '**/*.less');
var run = function () {
var start = Date.now();
console.log('Start building CSS/LESS bundle');
gulp.src(src)
.pipe(gulpif(options.devBuild, plumber({
errorHandler: onError
})))
.pipe(concat('main.css'))
.pipe(less())
.pipe(gulpif(options.minify, minifyCSS()))
.pipe(gulp.dest(buildPath + 'css'))
.pipe(gulpif(options.devBuild, browserSync.reload({stream:true})))
.pipe(notify(function () {
console.log('END CSS/LESS built in ' + (Date.now() - start) + 'ms');
}));
};
run();
if (options.watch) {
gulp.watch(src, run);
}
};
gulp.task('dev', function () {
var options = {
devBuild: true,
minify: false,
watch: false
};
cssTask (options);
});
If you use gulp with yargs, notice the following:
If you have a task 'customer' and wan't to use yargs build in Parameter checking for required commands:
.command("customer <place> [language]","Create a customer directory")
call it with:
gulp customer --customer Bob --place Chicago --language english
yargs will allway throw an error, that there are not enough commands was assigned to the call, even if you have!! β
Give it a try and add only a digit to the command (to make it not equal to the gulp-task name)... and it will work:
.command("customer1 <place> [language]","Create a customer directory")
This is cause of gulp seems to trigger the task, before yargs is able to check for this required Parameter. It cost me surveral hours to figure this out.
Hope this helps you..
Here is another way without extra modules:
I needed to guess the environment from the task name, I have a 'dev' task and a 'prod' task.
When I run gulp prod it should be set to prod environment.
When I run gulp dev or anything else it should be set to dev environment.
For that I just check the running task name:
devEnv = process.argv[process.argv.length-1] !== 'prod';
I know I am late to answer this question but I would like to add something to answer of #Ethan, the highest voted and accepted answer.
We can use yargs to get the command line parameter and with that we can also add our own alias for some parameters like follow.
var args = require('yargs')
.alias('r', 'release')
.alias('d', 'develop')
.default('release', false)
.argv;
Kindly refer this link for more details.
https://github.com/yargs/yargs/blob/HEAD/docs/api.md
Following is use of alias as per given in documentation of yargs. We can also find more yargs function there and can make the command line passing experience even better.
.alias(key, alias)
Set key names as equivalent such that updates to a key will propagate
to aliases and vice-versa.
Optionally .alias() can take an object that maps keys to aliases. Each
key of this object should be the canonical version of the option, and
each value should be a string or an array of strings.
There is certainly a shorter notation, but that was my approach:
gulp.task('check_env', function () {
return new Promise(function (resolve, reject) {
// gulp --dev
var env = process.argv[3], isDev;
if (env) {
if (env == "--dev") {
log.info("Dev Mode on");
isDev = true;
} else {
log.info("Dev Mode off");
isDev = false;
}
} else {
if (variables.settings.isDev == true) {
isDev = true;
} else {
isDev = false;
}
}
resolve();
});
});
If you want to set the env based to the actual Git branch (master/develop):
gulp.task('set_env', function (cb) {
exec('git rev-parse --abbrev-ref HEAD', function (err, stdout, stderr) {
const git__branch = stdout.replace(/(\r\n|\n|\r)/gm, ""),
regex__feature = new RegExp('feature/feature-*');
if (git__branch == "develop") {
log.info("π¨βπ»Develop Branch");
isCompressing = false;
} else if (git__branch == "master") {
log.info("πMaster Branch");
isCompressing = true;
} else if (regex__feature.test(git__branch) === true){
log.info("β¨Feature Branch");
isCompressing = true;
}else{
//TODO: check for other branch
log.warn("Unknown " + git__branch + ", maybe hotfix?");
//isCompressing = variables.settings.isCompressing;
}
log.info(stderr);
cb(err);
});
return;
})
P.s. For the log I added the following:
var log = require('fancy-log');
In Case you need it, thats my default Task:
gulp.task('default',
gulp.series('set_env', gulp.parallel('build_scss', 'minify_js', 'minify_ts', 'minify_html', 'browser_sync_func', 'watch'),
function () {
}));
Suggestions for optimization are welcome.
Just load it into a new object on process .. process.gulp = {} and have the task look there.
In my gulp.js file I'm streaming all HTML files from the examples folder into the build folder.
To create the gulp task is not difficult:
var gulp = require('gulp');
gulp.task('examples', function() {
return gulp.src('./examples/*.html')
.pipe(gulp.dest('./build'));
});
But I can't figure out how retrieve the file names found (and processed) in the task, or I can't find the right plugin.
I'm not sure how you want to use the file names, but one of these should help:
If you just want to see the names, you can use something like gulp-debug, which lists the details of the vinyl file. Insert this anywhere you want a list, like so:
var gulp = require('gulp'),
debug = require('gulp-debug');
gulp.task('examples', function() {
return gulp.src('./examples/*.html')
.pipe(debug())
.pipe(gulp.dest('./build'));
});
Another option is gulp-filelog, which I haven't used, but sounds similar (it might be a bit cleaner).
Another options is gulp-filesize, which outputs both the file and it's size.
If you want more control, you can use something like gulp-tap, which lets you provide your own function and look at the files in the pipe.
I found this plugin to be doing what I was expecting: gulp-using
Simple usage example: Search all files in project with .jsx extension
gulp.task('reactify', function(){
gulp.src(['../**/*.jsx'])
.pipe(using({}));
....
});
Output:
[gulp] Using gulpfile /app/build/gulpfile.js
[gulp] Starting 'reactify'...
[gulp] Finished 'reactify' after 2.92 ms
[gulp] Using file /app/staging/web/content/view/logon.jsx
[gulp] Using file /app/staging/web/content/view/components/rauth.jsx
Here is another simple way.
var es, log, logFile;
es = require('event-stream');
log = require('gulp-util').log;
logFile = function(es) {
return es.map(function(file, cb) {
log(file.path);
return cb(null, file);
});
};
gulp.task("do", function() {
return gulp.src('./examples/*.html')
.pipe(logFile(es))
.pipe(gulp.dest('./build'));
});
You can use the gulp-filenames module to get the array of paths.
You can even group them by namespaces:
var filenames = require("gulp-filenames");
gulp.src("./src/*.coffee")
.pipe(filenames("coffeescript"))
.pipe(gulp.dest("./dist"));
gulp.src("./src/*.js")
.pipe(filenames("javascript"))
.pipe(gulp.dest("./dist"));
filenames.get("coffeescript") // ["a.coffee","b.coffee"]
// Do Something With it
For my case gulp-ignore was perfect.
As option you may pass a function there:
function condition(file) {
// do whatever with file.path
// return boolean true if needed to exclude file
}
And the task would look like this:
var gulpIgnore = require('gulp-ignore');
gulp.task('task', function() {
gulp.src('./**/*.js')
.pipe(gulpIgnore.exclude(condition))
.pipe(gulp.dest('./dist/'));
});
If you want to use #OverZealous' answer (https://stackoverflow.com/a/21806974/1019307) in Typescript, you need to import instead of require:
import * as debug from 'gulp-debug';
...
return gulp.src('./examples/*.html')
.pipe(debug({title: 'example src:'}))
.pipe(gulp.dest('./build'));
(I also added a title).