With the Browserify API, use require and exclude on a local file - javascript

With the Browserify API and Gulp, I have this:
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var dependencies = [
'lodash',
'./test.js',
];
gulp.task('lib', function() {
return browserify()
.require(dependencies)
.bundle()
.pipe(source('lib.js'))
.pipe(gulp.dest('./'));
});
gulp.task('app', function() {
return browserify('./app.js')
.external(dependencies)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./'));
});
And in app.js I have this:
var _ = require('lodash');
var test = require('./test.js');
The Lodash line works fine, but the ./test.js does not work. I get the error Error: Cannot find module '/test.js'.
How do I get this to work?
For some reason, the key differs between bundle.js and lib.js. In lib.js, the key for test.js is the full path (/Users/gary/Projects/browserify-test/test.js) whereas in bundle.js it's looking for a module with the key ./test.js. If I manually change the latter to be the same as the former, then it works.
I'm guessing that ultimately, Browserify doesn't support require on local files that are excluded from the same bundle.

browserify needs an absolute path to retrieve the file and it leaves that as the bundle key. The way to fix it is to use the expose option...
In your build..
var dependencies = [
'lodash',
{file: './test.js', expose: 'test'},
];
and in app.js...
var _ = require('lodash');
var test = require('test');

Related

Modify gulpfile to read html files in subfolder and spit them out to build folder

I have been working on modifying this relatively simple gulpfile/project: https://github.com/ispykenny/sass-to-inline-css
The first issue I had was to update to gulp v4, but I've also tried to store variables for my src and destination folders which is a bit easier to control. So now my gulpfile looks like this:
const gulp = require('gulp');
const inlineCss = require('gulp-inline-css');
const sass = require('gulp-sass');
const browserSync = require('browser-sync').create();
const plumber = require('gulp-plumber');
const del = require('del');
const srcFolder = './src'; // TODO tidy this up once working
const buildFolder = srcFolder + '/build/'; // Tidy this up once working
const src = {
scss: 'src/scss/**/*.scss',
templates: 'src/templates/**/*.html'
}
const dest = {
build: 'build/',
css: 'build/css'
};
function processClean() {
return del(`${buildFolder}**`, { force: true });
}
function processSass() {
return gulp
.src(src.scss)
.pipe(plumber())
.pipe(sass())
.pipe(gulp.dest(dest.css))
.pipe(browserSync.stream())
}
function processInline() {
return gulp
.src('./*.html')
.pipe(inlineCss({
removeHtmlSelectors: true
}))
.pipe(gulp.dest('build/'))
}
function processWatch() {
gulp.watch(['./src/scss/**/*.scss'], processSass);
gulp.watch(srcFolder).on('change', browserSync.reload);
gulp.watch(distFolder).on('change', browserSync.reload);
}
const buildStyles = gulp.series(processSass, processInline);
const build = gulp.parallel(processClean, buildStyles);
gulp.task('clean', processClean);
gulp.task('styles', buildStyles);
gulp.task('sass', processSass);
gulp.task('inline', processInline);
gulp.task('build', build);
gulp.task('watch', processWatch);
But I am now wanting to create lots of template files, store them in a subfolder and have gulp spit out each file into the destination folder. if I have index.html, test1.html etc in the root it works fine.
I tried modifying this:
function processInline() { return gulp.src('./*.html')
To this:
function processInline() { return gulp.src(src.templates) // should equate to 'src/templates/**/*html'
Now I'm seeing this error in the console:
ENOENT: no such file or directory, open 'C:\Users\myuser\pathToApp\emailTemplates\src\templates\build\css\style.css'
In the head of index.html in the root is this:
<link rel="stylesheet" href="build/css/style.css">
I actually don't really care about the css file as the final output should be inline (for email templates). But I cannot get my head around why this is happening.
Does gulp create the css file and then read the class names from there? EDIT, Ah I guess it must because it has to convert the sass to readable css first before stripping out the class names and injecting the inline styles.
Years ago I worked with grunt a fair bit, and webpack, but haven't done much with gulp.
I hope it is obvious, but if you need more information just let me know.

Browserify: Exported function not found after bundling Bootstrap

I'm using Browserify to create a bundle which contains an exported function that I want to call within a <script> tag. Everything works fine until I require Bootstrap, at which point the function is no longer accessible and I get the error:
TypeError: mainBundle.greeting is not a function
Here's the code:
JavaScript (main.js):
window.jQuery = require('jquery');
window.$ = global.jQuery;
module.exports = {
greeting
};
function greeting (name) {
return `Hello ${name}!`;
}
HTML
<script src="js/bundle.js"></script>
<script>
// Update greeting
$('#greeting').text(mainBundle.greeting('Foo'));
</script>
Gulpfile:
Taken pretty much from the Gulp Browserify recipe. You can see I've added the standalone option to customOpts to generate a standalone module as well as require to add Bootstrap. The issue occurs when the require line is commented in.
const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const concat = require('gulp-concat');
const watchify = require('watchify');
const browserify = require('browserify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const log = require('gulplog');
// add custom browserify options here
const customOpts = {
entries: ['./src/js/main.js'],
// require: ['bootstrap', 'jquery'], // UNCOMMENT CAUSES ISSUE
standalone: 'mainBundle',
debug: true
};
const opts = {...watchify.args, ...customOpts};
const b = watchify(browserify(opts));
console.log('Browserify options: ', opts);
// add transformations here
// i.e. b.transform(coffeeify);
exports.js = bundle; // so you can run `gulp js` to build the file
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', log.info); // output build logs to terminal
function bundle() {
return b.bundle()
// log errors if they happen
.on('error', log.error.bind(log, 'Browserify Error'))
.pipe(source('bundle.js'))
// optional, remove if you don't need to buffer file contents
.pipe(buffer())
// optional, remove if you dont want sourcemaps
.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
// Add transformation tasks to the pipeline here.
.pipe(sourcemaps.write('./')) // writes .map file
.pipe(gulp.dest('./dist/js'));
}
Hmm, putting the require in main.js resolves the issue:
const bootstrap = require('bootstrap');
window.jQuery = require('jquery');
window.$ = global.jQuery;
module.exports = {
greeting
};
function greeting (name) {
return `Hello ${name}!`;
}
If anyone has a better answer that would allow me to use the Browserify require option, I will happily accept your answer. I would prefer to use the config option to avoid importing things that are not explicitly required in my scripts.

gulp task to process files that are writable

I'm using Gulp in a VS2015 project to run jscs on JavaScript files with the fix option set. The intention is to modify the same file that is read (viz., source and destination are the same).
var gulp = require('gulp');
var jscs = require('gulp-jscs');
var chmod = require('gulp-chmod');
var exec = require('gulp-exec');
var ourJsFiles = // an array of files and globbed paths
gulp.task('jscs', function (callback) {
ourJsFiles.forEach(function (fn) {
gulp.src(fn, { base: './' })
.pipe(jscs({
"preset": "google",
"maximumLineLength": 160,
"validateIndentation": 3,
"fix": true
}))
.pipe(gulp.dest('./'));
});
callback();
});
But I do not want to process any files that are read-only. Is there already a way to detect this in Gulp on Windows?
There is a plugin which allows you to work with subset of files: gulp-filter.
One of options is to pass filter function which will receive vinyl file object, so for e.g. you could use stat.mode property of that object which holds permissions and do something like:
var filter = require('gulp-filter');
...
var writableFiles = filter(function (file) {
//https://github.com/nodejs/node-v0.x-archive/issues/3045
var numericPermission = '0'+(e.stat.mode & parseInt('777', 8)).toString(8);
return numericPermission[1]==='6'
});
...
gulp.src(....)
.pipe(writableFiles)

Set working directory in gulpfile.js?

Is there a way to set the working directory for Gulp within a gulpfile, so that I can run a gulp command from a subdirectory without running into any issues? I ran a search for this and didn't find what I was looking for.
To clarify, I'm aware of adding a prefix to the files I'm using. However, instead of this -
var gulp = require('gulp');
var jshint = require('gulp-jshint');
...
var paths = {
js: [__dirname + 'app/*/*.js', __dirname + '!app/lib/**'],
css: __dirname + 'app/*/*.styl',
img: __dirname + 'app/img/*',
index: __dirname + '*.html',
dist: __dirname + 'dist'
};
I'd like to do something like this:
var gulp = require('gulp');
var jshint = require('gulp-jshint');
...
gulp.cwd(__dirname); // This would be much easier to understand, and would make future edits a bit safer.
var paths = {
js: ['app/*/*.js', '!app/lib/**'],
css: 'app/*/*.styl',
img: 'app/img/*',
index: '*.html',
dist: 'dist'
};
I'm wondering if Gulp exposes this functionality. Perhaps node itself allows this.
(I realize that there is likely a way to do command line itself when I run the command, but I would like to include it in the gulp file, especially for distribution purposes. I want the working directory for gulp to match the directory in which the gulpfile resides.)
Thanks!
Besides option.cwd, you can also use process.chdir(yourDir)
it could be used anywhere in a gulpfile. e.g.
process.chdir(yourDir);
var gulp = require('gulp');
Make sure your gulp is up-to-date( > 3.8.10), this may not work in older gulp.
Instead of concatenating strings by yourself, you should be using path.join since it will take care of the proper slash, and following that path you can add a shorcut:
var path = require('path'),
p = function () {
Array
.prototype
.unshift
.call(arguments, __dirname);
return path.join.apply(path, arguments);
};
console.log(p('a', 'b', 'c'));
Or, well, you can just:
gulp.src(..., {cwd: __dirname})
gulp.dest(..., {cwd: __dirname})
Something like:
var src = function (globs, options) {
options = options || {};
options.cwd = __dirname;
return gulp.src(globs, options);
};
var dest = function (folder, options) {
options = options || {};
options.cwd = __dirname;
return gulp.dest(folder, options);
};
Look here and here.

How to add a path prefix to globs passed to gulp.src?

Consider the following two files:
config.json
{
"vendorFiles": [
"vendor/angular/angular.js",
"vendor/angular-ui-router/release/angular-ui-router.js",
"vendor/angular-ui-utils/modules/utils.js"
]
}
gulpfile.js
var gulp = require("gulp"),
concat = require("gulp-concat"),
config = require("./config");
gulp.task("build-vendor", function() {
gulp.src(config.vendorFiles)
.pipe(concat("vendor.js"))
.pipe(gulp.dest("build"));
});
How can I eliminate the need to specify vendor/ for each file in config.json? That file is one that is manually edited by other developers by hand, so I want to make it as hassle-free on them as possible.
Ideally I'd like my gulpfile.js to take care of adding that prefix (somehow), and for my config.json to look like this:
{
"vendorFiles": [
"angular/angular.js",
"angular-ui-router/release/angular-ui-router.js",
"angular-ui-utils/modules/utils.js"
]
}
There may be a better way with a Gulp specific solution, but this should work.
var gulp = require("gulp"),
concat = require("gulp-concat"),
config = require("./config");
gulp.task("build-vendor", function() {
gulp.src(config.vendorFiles.map(function(a) {return 'vendor/' + a}))
.pipe(concat("vendor.js"))
.pipe(gulp.dest("build"));
});
Demo:
http://jsfiddle.net/AK4tP/
Can't you just do
var gulp = require("gulp"),
concat = require("gulp-concat"),
config = require("./config");
gulp.task("build-vendor", function() {
gulp.src(config.vendorFiles, {root: 'vendor/'})
.pipe(concat("vendor.js"))
.pipe(gulp.dest("build"));
});
Gulp should accept root option in src() although it's not documented.

Categories