I'm writing a gulp file to build and concatenate JS files. The main purpose is to build ES6 code with Babel in a main.build.js, then concatenate additional libraries/dependencies in a main.deps.jsfile, and concatenate both files in a final main.bundle.js file. This is done for frontend and admin, so the output must be two files (main.bundle.js and admin.bundle.js).
To do that, I've created 3 tasks, and one to trigger all with one command. Here is my gulpfile.js code:
task("js:build", (done) => {
browserify({entries: path.resolve(srcPath, "js/main.js")})
.transform(babelify.configure({
presets: ["#babel/preset-env"]
}))
.bundle()
.on("error", (e) => {
console.error(e.message);
})
.pipe(source("main.build.js"))
.pipe(dest(path.resolve(distPath, "js/standalone")));
browserify({entries: path.resolve(srcPath, "js/admin.js")})
.transform(babelify.configure({
presets: ["#babel/preset-env"]
}))
.bundle()
.on("error", (e) => {
console.error(e.message);
})
.pipe(source("admin.build.js"))
.pipe(dest(path.resolve(distPath, "js/standalone")));
done();
});
task("js:deps", (done) => {
let mainDeps = jsDeps.main.map((asset) => {
return asset.indexOf("node_modules") > -1 ? asset : path.resolve(srcPath, "js/plugins", asset);
});
let adminDeps = jsDeps.admin.map((asset) => {
return asset.indexOf("node_modules") > -1 ? asset : path.resolve(srcPath, "js/plugins", asset);
});
if(mainDeps.length > 0){
src(mainDeps)
.pipe(plumber())
.pipe(concat("main.deps.js"))
.pipe(dest(path.resolve(distPath, "js/standalone")));
}
if(adminDeps.length > 0){
src(adminDeps)
.pipe(plumber())
.pipe(concat("admin.deps.js"))
.pipe(dest(path.resolve(distPath, "js/standalone")));
}
done();
});
task("js:concat", (done) => {
let mainDeps = path.resolve(distPath, "js/standalone/main.deps.js");
let mainBuild = path.resolve(distPath, "js/standalone/main.build.js");
let adminDeps = path.resolve(distPath, "js/standalone/admin.deps.js");
let adminBuild = path.resolve(distPath, "js/standalone/admin.build.js");
let main = fs.existsSync(mainDeps) ? [mainDeps, mainBuild] : [mainBuild];
let admin = fs.existsSync(adminDeps) ? [adminDeps, adminBuild] : [adminBuild];
src(main)
.pipe(plumber())
.pipe(concat("main.bundle.js"))
.pipe(dest(path.resolve(distPath, "js")));
src(admin)
.pipe(plumber())
.pipe(concat("admin.bundle.js"))
.pipe(dest(path.resolve(distPath, "js")));
done();
});
task("js", series("js:deps", "js:build", "js:concat"));
The problem is: when I run gulp js in my terminal, it throws an error that main.build.jsfile was not found, as if the js:build task hasn't executed. And, obviously, main.build.js isn't generated at desired folder.
But, for my surprise, if I run each task separately in my terminal (gulp js:deps, then gulp js:build, then gulp js:concat), it works like a charm, just like as expected. All .build and .deps file are generated and concatenated in .bundle files.
Looks like the gulp js:build isn't running when calling inside a series, or the series isn't running tasks in series (it's running like a parallel).
Can someone help me fixing that? Thanks!
Related
After reading through a few related topics I still can't figure out what's causing gulp running a task twice.
This is the only task running twice:
const acf = "_sass/_admin/acf.scss";
const output = "./assets";
gulp.task("acf", () => {
return gulp.src(acf)
.pipe(plumber())
.pipe(sass())
.pipe(autoprefixer("last 2 versions", "> 5%"))
.pipe(concat("acf.min.css"))
.pipe(minify({ compatibility: '*' }))
.pipe(gulp.dest(output))
.pipe(browserSync.stream());
});
Another task which is handling a sibling file is running only once:
const gutenberg = "_sass/_admin/gutenberg.scss";
gulp.task("gutenberg", () => {
return gulp.src(gutenberg)
.pipe(plumber())
.pipe(sass())
.pipe(autoprefixer("last 2 versions", "> 5%"))
.pipe(concat("gutenberg.min.css"))
.pipe(minify({ compatibility: '*' }))
.pipe(gulp.dest(output))
.pipe(browserSync.stream());
});
Writing to the same DIR that another task is watching does not seem to be the issue.
See this handler:
gulp.task("default", done => {
browserSync.init({
open: false,
proxy: siteURL
});
gulp.watch(acf, gulp.series("acf"));
gulp.watch(gutenberg, gulp.series("gutenberg"));
done();
});
What am I missing? How do I make sure that the ACF task only runs once?
I am having a difficult time getting gulp.dest to output anything anywhere. I specifically want to just output to the root of the project (I'm hijacking an ASP.NET Core project for TypeScript development only). I'd really appreciate some help on how I can get dest to do what I want. Here's my current gulpfile.js:
/// <binding AfterBuild="build" Clean="clean" ProjectOpened="watch" />
"use strict";
const del = require("del"),
gulp = require("gulp"),
gulpConcat = require("gulp-concat"),
gulpRename = require("gulp-rename"),
gulpTerser = require("gulp-terser");
gulp.task("concatenate", () => {
return gulp.src([
"*.js",
"!gulpfile.js"
])
.pipe(gulpConcat("concatenated.js"))
.pipe(gulp.dest("/"));
});
gulp.task("minify", () => {
return gulp.src("concatenated.js")
.pipe(gulpTerser())
.pipe(gulpRename("min.js"))
.pipe(gulp.dest("/"));
});
gulp.task("clean", () => del([
"*.js",
"!gulpfile.js"
]));
gulp.task("build", gulp.series(
"concatenate",
"minify"
));
gulp.task("watch", () => {
gulp.watch([
"*.ts"
], gulp.series(
"build"
));
});
I think
.pipe(gulp.dest('.'))
is all you want.
I'm new to gulp and I've got a couple of gulp questions that I hope are pretty easy (meaning I'll probably have one of those forehead smacking moments when I hear the answer)...
My gulpfile has a number of repetitive one-way copy tasks, and then I'm watching these separate tasks in the watch command, however I'm almost certain that the way I'm doing it is totally inefficient.
Also, I'm noticing some interesting behavior, the copy command for syncHtmlRootDir task works exactly as I hoped (it will delete files as necessary), but none of my other one way copy tasks will remove deleted files, and I'm guessing it's a pathing issue, but I'm stumped on it.
gulpfile.js
var gulp = require('gulp');
var browserSync = require('browser-sync').create();
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css')
var uglify = require('gulp-uglify');
var newer = require('gulp-newer');
var path = require('path');
var del = require('del');
function handleError (error) {
console.log(error.toString())
this.emit('end')
}
//setup browerSync to serve from both src and dist directories.
gulp.task('browserSync', function() {
browserSync.init({
server: {
baseDir: ["./", "src"] // ./ signifies root of folder, allows to load files from dist and src folders.
},
})
});
//one way sync of root folder
gulp.task('syncHtmlRootDir', function(done) {
return gulp.src(['src/*.html'])
.pipe(newer('dist/'))
.pipe(gulp.dest('dist/'))
.pipe(browserSync.reload({
stream: true
}))
});
//one way sync of app folder
gulp.task('syncHtmlAppDir', function(done) {
return gulp.src(['src/app/**/*'])
.pipe(newer('dist/app/'))
.pipe(gulp.dest('dist/app/'))
.pipe(browserSync.reload({
stream: true
}))
});
//one way sync of image folder
gulp.task('syncImgDir', function(done) {
return gulp.src(['src/assets/img/**/*'])
.pipe(newer('dist/assets/img/'))
.pipe(gulp.dest('dist/assets/img/'))
.pipe(browserSync.reload({
stream: true
}))
});
//copy and compile SCSS code
gulp.task('compileSass', function() {
return gulp.src('src/assets/css/**/*.scss')
.pipe(sass())
.on('error', handleError)
.pipe(minifyCss())
.pipe(gulp.dest('dist/assets/css'))
.pipe(browserSync.reload({
stream: true
}))
});
//minify JS
gulp.task('uglifyJS', function() {
gulp.src('src/assets/js/**/*.js')
.pipe(uglify())
.on('error', handleError)
.pipe(gulp.dest('dist/assets/js'))
.pipe(browserSync.reload({
stream: true
}))
});
//watch tasks
gulp.task('watch', ['browserSync'], function() {
var rootDir = gulp.watch('src/*.html', ['syncHtmlRootDir']);
rootDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src','dist'));
}
});
var appDir = gulp.watch('src/app/**/*', ['syncHtmlAppDir']);
appDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/app/','dist/app/'));
}
});
var imgDir = gulp.watch('src/assets/img/**/*', ['syncImgDir']);
imgDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/img/','dist/assets/img/'));
}
});
var jsDir = gulp.watch('src/assets/js/**/*', ['uglifyJS']);
jsDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/js/','dist/assets/js/'));
}
});
var cssDir = gulp.watch('src/assets/css/**/*', ['compileSass']);
cssDir.on('change', function(ev) {
if(ev.type === 'deleted') {
del(path.relative('./', ev.path).replace('src/assets/css/','dist/assets/css/'));
}
});
});
So I'm 1) looking to combine my repetitive copy tasks into fewer tasks 2)have the delete file function work on all copy tasks and 3) optimize my watch task function to reduce repetition.
p.s., I've also noticed that if I add new files to the watch folders, it won't "recognize" them until I restart the watch command, so my method of syncing isn't exactly bulletproof. =/
Your thoughts are most appreciated.
Thanks!
The concatenation and destination to my public/assets/css is only working if i run 2 times the task. Even if I run just the app.css (npm run dev app.css), only work if I run twice.
const gulp = require('gulp')
const htmlmin = require('gulp-htmlmin')
const uglify = require('gulp-uglify')
const uglifycss = require('gulp-uglifycss')
const concat = require('gulp-concat')
const babel = require('gulp-babel')
const sass = require('gulp-sass')
gulp.task('app', ['app.html', 'app.sass', 'app.js', 'app.assets', 'app.css'])
gulp.task('app.html', function(){
gulp.src(['app/**/*.html'])
.pipe(htmlmin({ collapseWhitespace: true, removeComments: true}))
.pipe(gulp.dest('public'))
})
gulp.task('app.sass', function () {
gulp.src(['sass/main.scss'])
.pipe(sass.sync().on('error', sass.logError))
.pipe(gulp.dest('app/css'))
});
gulp.task('app.css', function(){
gulp.src(['app/**/*.css'])
.pipe(uglifycss({ "uglyComments": true}))
.pipe(concat('app.min.css'))
.pipe(gulp.dest('public/assets/css'))
})
gulp.task('app.js', function(){
gulp.src(['app/**/*.js'])
.pipe(babel({ presets: ['es2015'] }))
.pipe(uglify())
.pipe(concat('app.min.js'))
.pipe(gulp.dest('public/assets/js'))
})
gulp.task('app.assets', function(){
gulp.src(['assets/**/*.*'])
.pipe(gulp.dest('public/assets'))
})
For gulp to respect the order of the task dependencies you defined, you need to tell it when the task is done.
To do so, you need to return a stream, a promise, or call the task function callback parameter.
Since your tasks are pretty basic, just returning the stream should be enough.
gulp.task('app.sass', function () {
return gulp.src(['sass/main.scss']) // notice the return here
.pipe(sass.sync().on('error', sass.logError))
.pipe(gulp.dest('app/css'));
});
// make the sass task a dependency of the CSS task
gulp.task('app.css', ['app.sass'], function(){
return gulp.src(['app/**/*.css']) // notice the return here
.pipe(uglifycss({ "uglyComments": true}))
.pipe(concat('app.min.css'))
.pipe(gulp.dest('public/assets/css'));
});
Now you don't need the app.sass since it will run before app.css.
gulp.task('app', ['app.html', 'app.js', 'app.assets', 'app.css'])
I think that your tasks should be waiting for a result from the previous task. You can't just run several tasks in one time.
Try with gulp-sequence plugin. Install gulp-sequence and add the following line on the top of your gulpfile.js:
const gulpSequence = require('gulp-sequence')
Replace your current 'app' task by:
gulp.task('app', gulpSequence('app.html', 'app.sass', 'app.js', 'app.assets', 'app.css'))
When I make changes to .jade files I want to Gulp task run only for that file, not for all files. For that I'm using gulp-changed. It's working fine, until I make changes to files that affect to global layout, eg _header.jade, _layout.jade. When I make changes to that files nothing happens. All my layout files have _ before title. How can I solve this issue?
Here is my gulpfile some lines
gulp.task('jade', function() {
return gulp.src('dev/templates/**/!(_)*.jade')
.pipe(plumber({
errorHandler: onError
}))
.pipe(changed('public', {extension: '.html'}))
.pipe(jade({
pretty: true,
}))
.pipe(gulp.dest('public'))
.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('watch', function() {
gulp.watch('dev/templates/**/*.jade', gulp.series('jade'));
});
First thing I would do is to refactor out your jade compilation task into a separate function. That allows you to parameterize your jade compilation so that you can run it on one or more files of your choice:
function compileJade(files) {
return gulp.src(files, {base:'dev/templates'})
.pipe(plumber({
errorHandler: onError
}))
.pipe(jade({
pretty: true,
}))
.pipe(gulp.dest('public'))
.pipe(browserSync.reload({
stream: true
}));
}
Your existing jade task now simply calls that function:
gulp.task('jade', function() {
return compileJade('dev/templates/**/!(_)*.jade');
});
If a changed file is a partial (starts with _) we need to be able to determine which other files are affected by that change. This is facilitated by the jade-inheritance library:
var JadeInheritance = require('jade-inheritance');
var path = require('path');
function isPartial(file) {
return path.basename(file).match(/^_.*/);
}
function findAffectedFiles(changedFile) {
return new JadeInheritance(changedFile, 'dev/templates', {basedir: 'dev/templates'})
.files
.filter(function(file) { return !isPartial(file); })
.map(function(file) { return 'dev/templates/' + file; })
}
Finally whenever a file changes we call the compileJade function for the affected files only:
gulp.task('watch', function() {
gulp.watch('dev/templates/**/*.jade').on('change', function(changedFile) {
return compileJade(isPartial(changedFile) ? findAffectedFiles(changedFile) : changedFile);
});
});