Gulp Browserify with glob and uglify/factor-bundle - gulp

I'm currently getting into browserify. I like it so far but before I start using it I want to automate it. Gulp is the build system of my choice.
So what I actually want to do is:
Get js/app/**.js, bundle it to js/bundle/ and extract common dependencies into js/bundle/common.js. In addition uglify everything and add source maps.
Well. The gulp support for browserify kinda seems poor, at least my google researches were pretty disappointing.
Anyway. What I've got so far.
var gulp = require('gulp'),
browserify = require('browserify'),
factor = require('factor-bundle');
// ...
// gulp task
return browserify({
entries: ['js/app/page1.js', 'js/app/page2.js'],
debug: true
})
.plugin(factor, {
o: ['js/bundle/page1.js', 'js/bundle/page2.js']
})
.bundle()
.pipe(source('common.js'))
.pipe(gulp.dest('js/bundle/'));
Well this is neither uglifying nor adding sourcemaps and much less using a glob pattern. I can find an official recipe which shows me how to use the pipe to add additional transformations like uglify. But it's only for a single file.

as an outputs parameter to factor-bundle, use streams instead of file paths. You can do whatever you want with the streams then.
var indexStream = source("index.js");
var testStream = source("tests.js");
var commonStream = bundler.plugin('factor-bundle', { outputs: [indexStream, testStream] })
.bundle()
.pipe(source('common.js'));
return merge(indexStream, commonStream, testStream)
.pipe(buffer())
.pipe(sourcemaps.init({ debug: true, loadMaps: true }))
.pipe(uglify())
.pipe(gulp.dest('js/bundle/'))

Thanks to Liero's answer, I got something very similar working. Here's the complete gulpfile:
const gulp = require('gulp');
const browserify = require('browserify');
const factor = require('factor-bundle');
const source = require('vinyl-source-stream');
const sourcemaps = require('gulp-sourcemaps');
const buffer = require('gulp-buffer');
const merge = require('gulp-merge');
gulp.task('bfb', function () {
const fejs = 'public/javascripts/' // location of source JS
const fejsb = fejs + 'b/'; // location of bundles
const modules = [ // aka entry points
'accounts',
'invoice',
'invoices',
// etc...
];
const inputs = [];
const streams = [];
modules.forEach(function (module) {
inputs.push(fejs + module + '.js');
streams.push(source(module + '.js'));
});
const bundler = browserify(inputs, {});
const commonStream = bundler.plugin(factor, { outputs: streams })
.bundle()
.pipe(source('common.js'));
streams.push(commonStream);
return merge(streams)
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
//.pipe(uglify()) // haven't tested this bit
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(fejsb));
});

Related

Why is Gulp concatenating my output in the wrong order?

As shown in the following gulpfile.js, I am trying to compile jQuery, bootstrap.js, and a collection of Javascript snippets from a subfolder into a single app.js output file. It is working, except that the snippets from the subfolder are appearing at the top of the app.js output file, prior to jQuery being loaded.
How can I ensure that these files are output in the correct order?
const { src, dest, watch, series, parallel } = require('gulp');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
var merge2 = require('merge2');
const files = {
jsSrcPath: [
'../node_modules/jquery/dist/jquery.js',
'../node_modules/bootstrap/dist/js/bootstrap.js',
'js/*.js'
],
jsDstPath: '../public/js'
}
function jsTask(){
return merge2(files.jsSrcPath.map(function (file) {
return src(file)
}))
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(dest(files.jsDstPath));
}
function watchTask(){
watch(files.jsSrcPath, jsTask);
}
exports.default = series(
jsTask,
watchTask
);
There's something internal here going on, in my tests I saw the order was sometimes random, sometimes based on modification time, sometimes in order. In any case, best to use a tool to ensure our streams are always in the order we want them.
gulp-order exists for this purpose. It can take specific paths and glob syntax, which you already have, so you can re-pass that to the plugin:
const { src, dest, watch, series, parallel } = require('gulp');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const order = require('gulp-order'); // Added
var merge2 = require('merge2');
const files = {
jsSrcPath: [
'../node_modules/jquery/dist/jquery.js',
'../node_modules/bootstrap/dist/js/bootstrap.js',
'js/*.js'
],
jsDstPath: 'dist'
}
function jsTask() {
return merge2(files.jsSrcPath.map(function (file) {
return src(file)
}))
.pipe(order(files.jsSrcPath)) // Added
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(dest(files.jsDstPath));
}
function watchTask() {
watch(files.jsSrcPath, jsTask);
}
exports.default = series(
jsTask,
watchTask
);

SCSS to Style.css

Long story story, the main developer that worked on this is no longer with the company and I have to take over the project that they built out - So all help would be appreciated with this.
I'm not too familiar with WordPress nor SCSS/Foundation so this will be a learning process. I need to make a css change and deploy it, but it's not showing the changes on my local environment at all.
Here is the knowledge that I have:
- Foundation was used to build this
- SCSS is being converted to Style.css
- https://cdn.site.pl/wp-content/themes/sites/style.css?ver=4.9.10 - There is a version being applied to the end of style.css
Here are the tasks that are in my gulpfile.js:
Here is his documentation:
The default gulp task runs both gulp scripts and gulp styles. To run
this task, navigate in Terminal to the project's htdocs directory, and
type:
gulp
I get this in response:
[14:00:37] Task never defined: default
[14:00:37] To list available tasks, try running: gulp --tasks
Below, I will be providing the whole gulpfile.js file
const argv = require('yargs').argv;
const {
src,
dest,
parallel,
series,
watch
} = require('gulp');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const concat = require('gulp-concat');
const postcss = require('gulp-postcss');
const cssnano = require('cssnano');
const rename = require('gulp-rename');
const uglify = require('gulp-uglify');
// Usage in gulp: gulp task-name --theme=theme-name
// Default value is 'f-sites'
const themeName = argv.theme ? argv.theme : 'f-sites';
// GULP CONFIG
const config = {
themeName: themeName,
themeDirectory: `wp-content/themes/${themeName}`
};
// GULP TASKS
// SCSS/CSS TASKS
//==================================================
function sassToCss() {
const nodeModulesSassPaths = [
'node_modules/foundation-sites/scss',
'node_modules/slick-carousel/slick',
'node_modules/jquery-fancybox/source/scss',
'node_modules/font-awesome/scss'
];
const srcPaths = [
`${config.themeDirectory}/scss/theme.scss`,
`${config.themeDirectory}/scss/theme-rtl.scss`
];
return src(srcPaths, { sourcemaps: true })
.pipe(sass({ includePaths: nodeModulesSassPaths }).on('error', sass.logError))
.pipe(autoprefixer({ browsers: ['last 2 versions', 'ie >= 10'] }))
.pipe(dest(`${config.themeDirectory}/css`, { sourcemaps: true }));
}
function cssConcatLTR() {
const srcFiles = [
`${config.themeDirectory}/css/wordpress.css`,
'./node_modules/animate.css/animate.css',
`${config.themeDirectory}/css/theme.css`,
];
return src(srcFiles, { sourcemaps: true })
.pipe(concat('project.css'))
.pipe(dest(`${config.themeDirectory}/css`, { sourcemaps: true }));
}
function cssConcatRTL() {
const srcFiles = [
config.themeDirectory + '/css/wordpress.css',
'./node_modules/animate.css/animate.css',
config.themeDirectory + '/css/theme-rtl.css'
];
return src(srcFiles, { sourcemaps: true })
.pipe(concat('project-rtl.css'))
.pipe(dest(`${config.themeDirectory}/css`, { sourcemaps: true }));
}
function cssMinifyLTR() {
const plugins = [cssnano()];
return src(`${config.themeDirectory}/css/project.css`)
.pipe(postcss(plugins))
.pipe(rename('style.css'))
.pipe(dest(config.themeDirectory));
}
function cssMinifyRTL() {
const plugins = [cssnano()];
return src(`${config.themeDirectory}/css/project-rtl.css`)
.pipe(postcss(plugins))
.pipe(rename('style-rtl.css'))
.pipe(dest(config.themeDirectory));
}
exports.default = parallel(
series(
sassToCss,
parallel(cssConcatLTR, cssConcatRTL),
parallel(cssMinifyLTR, cssMinifyRTL)
),
series(jsConcat, jsMinify)
);
exports.styles = series(
sassToCss,
parallel(cssConcatLTR, cssConcatRTL),
parallel(cssMinifyLTR, cssMinifyRTL)
);
exports.scripts = series(jsConcat, jsMinify);
This may be caused by the version of gulp-cli, you can try to upgrade it.
Relevant issues:
https://github.com/gulpjs/gulp-cli/issues/191
https://github.com/gulpjs/gulp/issues/1634

Using stream-combiner2 with Gulp 4

I'm trying to use stream-combiner2 with my Gulp 4 task, as advised in the current version of this recipe. However, I always receive:
The following tasks did not complete: build:js
Did you forget to signal async completion?
I've read the excellent information in this answer about Gulp 4 async completion, but I'm having trouble applying that in my task. Here's what I have:
const browserify = require('browserify')
const buffer = require('vinyl-buffer')
const combiner = require('stream-combiner2')
const gulp = require('gulp')
const jsDest = 'static/js'
const jsPath = 'build/js'
const jsSrc = `${jsPath}/**/*.js`
const source = require('vinyl-source-stream')
const sourcemaps = require('gulp-sourcemaps')
const uglify = require('gulp-uglify')
gulp.task('build:js', function () {
const combined = combiner.obj([
browserify({
entries: `${jsPath}/main.js`,
debug: true
}),
source(`${jsPath}/main.js`),
buffer(),
sourcemaps.init({ loadMaps: true }),
uglify(),
sourcemaps.write('./'),
gulp.dest(jsDest)
])
combined.on('error', console.error.bind(console))
return combined
})
Somehow I missed this fantastic recipe for browserify with multiple sources and destinations. It allowed me to finally get what I was after, including well-formed error handling:
const browserify = require('browserify')
const buffer = require('gulp-buffer')
const combiner = require('stream-combiner2')
const gulp = require('gulp')
const gutil = require('gulp-util')
const jsDest = 'static/js'
const jsSrc = 'build/js/*.js'
const sourcemaps = require('gulp-sourcemaps')
const tap = require('gulp-tap')
const uglify = require('gulp-uglify')
gulp.task('build:js', function () {
const combined = combiner.obj([
gulp.src(jsSrc, { read: false }),
tap(function (file) {
gutil.log('bundling ' + file.path)
file.contents = browserify(file.path, { debug: true }).bundle()
}),
buffer(),
sourcemaps.init({ loadMaps: true }),
uglify(),
sourcemaps.write('./'),
gulp.dest(jsDest)
])
combined.on('error', console.error.bind(console))
return combined
})
After a few hours of hacking away at it, I got fed up with the task not doing what I want at all (see comments to the post) and resorted to the deprecated gulp-browserify. Here's the solution with that plugin, but I'm completely willing to accept an answer that can correctly incorporate stream-combiner2 with browserify itself, because that'd be much better.
const combiner = require('stream-combiner2')
const gulp = require('gulp')
const jsDest = 'static/js'
const jsSrc = 'build/js/*.js'
const sourcemaps = require('gulp-sourcemaps')
const uglify = require('gulp-uglify')
gulp.task('build:js', function () {
const combined = combiner.obj([
gulp.src(jsSrc),
sourcemaps.init(),
browserify(),
uglify(),
sourcemaps.write('.'),
gulp.dest(jsDest)
])
combined.on('error', console.error.bind(console))
return combined
})

node-hbsfy not compiling templates with Gulp

I'm simply trying to compile hbs templates via Browserify and Gulp, but the compilation process fails as soon as any HTML markup is encountered from my hbs file.
I've confirmed this by removing the HTML code within the hbs file, at which point Browserify runs as expected.
Here is a simplified version of my Gulp task:
const _gulp = require('gulp');
const _browserify = require('browserify');
const _remapify = require('remapify');
const _hbsfy = require('hbsfy');
const _vinylSourceStream = require('vinyl-source-stream');
const _vinylBuffer = require('vinyl-buffer');
_gulp.task('js:dev', () => {
return _browserify({entries: './src/js/app.js', debug: true})
.plugin(_remapify, [
{
src: '**/*.hbs', // glob for the files to remap
cwd: './src/markup/components',
expose: 'components' // this will expose './src/markup/components' as 'components'
}
])
.transform(_hbsfy)
.bundle()
.pipe(_vinylSourceStream('app.js'))
.pipe(_vinylBuffer())
.pipe(_gulp.dest('dist'))
});
The hbs template:
<div class="menu"> </div>
The main JS file:
(function (){
const _handlebars = require('hbsfy/runtime');
function init () {
_handlebars.registerPartial('menu', require('components/menu.hbs'));
}
document.addEventListener('DOMContentLoaded', init);
})();
What could be going wrong? It's as if the hbsfy transform isn't running properly...
It seems this issue is actually caused by the remapify plugin. Compilation works as expected using pathmodify instead.
Here is my updated Gulp file:
const _gulp = require('gulp');
const _browserify = require('browserify');
const _pathmodify = require('pathmodify');
const _hbsfy = require('hbsfy');
const _vinylSourceStream = require('vinyl-source-stream');
const _vinylBuffer = require('vinyl-buffer');
_gulp.task('js:dev', () => {
return _browserify({entries: './src/js/app.js', debug: true})
.plugin(_pathmodify, {
mods: [
_pathmodify.mod.dir('components', process.cwd() + '/src/markup/partials/components')
]
})
.transform(_hbsfy)
.bundle()
.pipe(_vinylSourceStream('app.js'))
.pipe(_vinylBuffer())
.pipe(_gulp.dest('dist'))
});

Gulp + browserify + 6to5 + source maps

I'm trying to write a gulp task allowing me to use modules in JS (CommonJS is fine), using browserify + 6to5. I also want source mapping to work.
So:
1. I write modules using ES6 syntax.
2. 6to5 transpiles these modules into CommonJS (or other) syntax.
3. Browserify bundles the modules.
4. Source maps refers back to the original ES6 files.
How to write such a task?
Edit: Here's what I have so far:
gulp task
gulp.task('browserify', function() {
var source = require('vinyl-source-stream');
var browserify = require('browserify');
var to5ify = require('6to5ify');
browserify({
debug: true
})
.transform(to5ify)
.require('./app/webroot/js/modules/main.js', {
entry: true
})
.bundle()
.on('error', function(err) {
console.log('Error: ' + err.message);
})
.pipe(source('bundle.js'))
.pipe(gulp.dest(destJs));
});
modules/A.js
function foo() {
console.log('Hello World');
let x = 10;
console.log('x is', x);
}
export {
foo
};
modules/B.js
import {
foo
}
from './A';
function bar() {
foo();
}
export {
bar
};
modules/main.js
import {
bar
}
from './B';
bar();
The code seems to be working, but it's not minified and the source map is inline (which is not really working for production).
Use this as your start point:
var gulp = require('gulp');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var browserify = require('browserify');
var to5ify = require('6to5ify');
var uglify = require('gulp-uglify');
gulp.task('default', function() {
browserify('./src/index.js', { debug: true })
.transform(to5ify)
.bundle()
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
.pipe(uglify())
.pipe(sourcemaps.write('./')) // writes .map file
.pipe(gulp.dest('./build'));
});
I didn't understand why we had to use certain things in order to get this to work so I'm adding my own answer here. For those looking for a solution with babelify, I added one below. I also thought it'd be good to talk about what each line does.
For those that want to use ES6 in their Gulpfile, you can look here but Gulp supports it if you rename your file to Gulpfile.babel.js as of Gulp 3.9
One big thing to note is you need to use vinyl-source-stream with Browserify in order to convert the output into something Gulp can understand. From there a lot of gulp plugins require vinyl buffers which is why we buffer the source stream.
For those not familiar with sourcemaps, they are essentially a way for you to map your minifed bundled file to the main source file. Chrome and Firefox support it so when you debug you can look at your ES6 code and where it failed.
import gulp from 'gulp';
import uglify from 'gulp-uglify';
import sourcemaps from 'gulp-sourcemaps';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
import browserify from 'browserify';
import babel from 'babelify';
gulp.task('scripts', () => {
let bundler = browserify({
entries: ['./js/main.es6.js'], // main js file and files you wish to bundle
debug: true,
extensions: [' ', 'js', 'jsx']
}).transform(babel.configure({
presets: ["es2015"] //sets the preset to transpile to es2015 (you can also just define a .babelrc instead)
}));
// bundler is simply browserify with all presets set
bundler.bundle()
.on('error', function(err) { console.error(err); this.emit('end'); })
.pipe(source('main.es6.js')) // main source file
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true })) // create sourcemap before running edit commands so we know which file to reference
.pipe(uglify()) //minify file
.pipe(rename("main-min.js")) // rename file
.pipe(sourcemaps.write('./', {sourceRoot: './js'})) // sourcemap gets written and references wherever sourceRoot is specified to be
.pipe(gulp.dest('./build/js'));
});
Other useful readings:
Gulp browserify the gulp-y way