I have a library lib.js that I want to create from lib/a.js and lib/b.js and to be able to use it from a script client.js using var a = require('lib/a.js'); and that it works when I just include the compiled lib.js library before client.js (therefore, lib.js has to declare a require function that knows about lib/a.js)
I guess I have to use external and alias but I am not sure what is the proper way to do it
Also, is it possible to have a Gulp file that creates all the alias automatically for the folders in my library? eg. creates an alias for all the files in the lib/ dir?
Here are a couple of gulp tasks that would help to build your common lib.js and the client.js bundles separately.
Note that you have to tell browserify to b.require() lib/*.js when bundling lib.js, and you have to tell it to b.external() the libraries that will be loaded separately when bundling client.js
var path = require('path');
var gulp = require('gulp');
var browserify = require('browserify');
var concat = require('gulp-concat');
var transform = require('vinyl-transform');
gulp.task('build-lib', function () {
// use `vinyl-transform` to wrap around the regular ReadableStream returned by b.bundle();
// so that we can use it down a vinyl pipeline as a vinyl file object.
// `vinyl-transform` takes care of creating both streaming and buffered vinyl file objects.
var browserified = transform(function(filename) {
// basename, for eg: 'a.js'
var basename = path.basename(filename);
// define the exposed name that your client.js would use to require();
// for eg: require('lib/a.js'); // -> exposed name should be 'lib/a.js'
var expose = 'lib/' + basename;
return browserify(filename)
.require(filename, { expose: expose})
.bundle();
});
return gulp.src(['./lib/*.js'])
.pipe(browserified)
.pipe(concat('lib.js'))
.pipe(gulp.dest('./dist'));
});
gulp.task('build-client', function () {
var browserified = transform(function(filename) {
// filename = './client.js'
// let browserify know that lib/a.js and and lib/b.js are external files
// and will be loaded externally (in your case, by loading the bundled lib.js
// for eg: <script src='dist/lib.js'>)
return browserify(filename)
.external('lib/a.js')
.external('lib/b.js')
.bundle();
});
return gulp.src(['./client.js'])
.pipe(browserified)
.pipe(gulp.dest('./dist'));
});
gulp.task('default', ['build-lib', 'build-client']);
Are you looking for external requires?
To use with gulp-browserify, check the README
.on('prebundle', function(bundle) {
bundle.external('domready');
bundle.external('react');
})
Should work with bundle.require as well.
Related
I am using gulp to concatenate and minify a number of standalone *.js scripts used on my web site. Basically this is just a catchall folder where I place little utility scripts that run on page load. For example, one of them starts a carousel slider, another adds a class to the header that shrinks it on scroll, etc. Each of these "features" has its own standalone *.js file.
Now, I would like to use the popular js-cookie library in one of those scripts. Unfortunately, since my project is not set up as an ES6 module, I am not able to able to import the js-cookie library the way it's specified in the docs, like this:
import Cookies from 'js-cookie'
When I do this, I get the error message Uncaught SyntaxError: Cannot use import statement outside a module.
I tried changing it to this:
window.Cookies = require('js-cookie')
but that gave me this error:
Uncaught ReferenceError: require is not defined
Here is my gulpfile, followed by the feature.js script in which I'm trying to use the js-cookie library:
gulpfile.js
// Initialize modules
const { src, dest, watch, series, parallel } = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
var replace = require('gulp-replace');
var merge = require('merge-stream');
// File paths (note that src paths are arrays)
const files = {
scssSrcPath: [
'scss/*.scss',
'scss/_pageContentModules/*.scss'
],
jsSrcPath: [
'js/*.js',
'node_modules/slick-carousel/slick/slick.js'
],
scssDstPath: '../web/css',
jsDstPath: '../web/js'
}
// Sass task: compiles SCSS files into style.css
function scssTask(){
return merge(files.scssSrcPath.map(function (file) {
return src(file)
}))
.pipe(sourcemaps.init()) // initialize sourcemaps first
.pipe(sass()) // compile SCSS to CSS
.pipe(postcss([ autoprefixer(), cssnano() ])) // PostCSS plugins
.pipe(sourcemaps.write('.'))
.pipe(dest(files.scssDstPath));
}
// JS task: concatenates and uglifies JS files to script.js
function jsTask(){
return merge(files.jsSrcPath.map(function (file) {
return src(file)
}))
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(dest(files.jsDstPath));
}
// Watch task: watch SCSS and JS files for changes
// If any change, run scss and js tasks simultaneously
function watchTask(){
watch(files.scssSrcPath, scssTask);
watch(files.jsSrcPath, jsTask);
}
// Export the default Gulp task so it can be run
// Runs the scss and js tasks simultaneously
// then watch task
exports.default = series(
parallel(scssTask, jsTask),
watchTask
);
js/feature.js
import Cookies from 'js-cookie';
const rs = cookies.get('referral_source');
if (typeof rs !== 'undefined') {
console.log('referral_source = ' + rs);
}
How can I get this working? Is there a way to do it using my simple Gulp setup, or do I need to go beyond and set up a full-on Webpack setup (with all the complexity that adds)?
Unfortunately, as far as I know, Gulp does not support the ability to use ES6 modules. If you want to use them, you will need to use Webpack.
But js-cookie does have a jsDelivr CDN: <script src="https://cdn.jsdelivr.net/npm/js-cookie#3.0.1/dist/js.cookie.min.js"></script>. By including this before your JS script, like this:
<script src="https://cdn.jsdelivr.net/npm/jscookie#3.0.1/dist/js.cookie.min.js"></script>
<script src="./js/feature.js"></script>
I am using Gulp and Browserify to build a TypeScript librariy which depends on some other libraries like for example jQuery via NPM. This is my current gulpfile:
var Gulp = require("gulp");
var Browserify = require("browserify");
var Buffer = require("vinyl-buffer");
var Rename = require("gulp-rename");
var Source = require("vinyl-source-stream");
var Tsify = require("tsify");
var Uglify = require("gulp-uglify");
var Manifest = require("./package.json");
Gulp.task("build-Debug", function () {
return Browserify({
debug: true,
standalone: Manifest.name
})
.add(Manifest.main)
.plugin(Tsify)
.bundle()
.on("error", function (error) { console.error(error.toString()); })
.pipe(Source(Manifest.name + ".js"))
.pipe(Buffer())
.pipe(Gulp.dest("./dist"));
});
The bundled JS-File gets included in my site and everything works fine. I am just missing one thing: on some sites, I need to define some custom JS which shouldn't get part of the library. These custom script should be able to use jQuery for instance, but it can't since it isn't accessible from outside of the bundle.
Now, I could include it again, but this does not make sense to me. So i'd like to know:
Is there a way to make all of my dependecies from package.json accessible from outside the bundle, so that I would be able to call
$("#my_id")....
in my custom client-scripts?
You can use Browserify's require method to expose modules as external requires:
return Browserify({
debug: true,
standalone: Manifest.name
})
.add(Manifest.main)
.require("some-module") // Some module in node_modules that you want to expose.
.plugin(Tsify)
.bundle()
...
You'd obtain the module in a <script> element like this:
<script>
var someModule = require("some-module");
/* ... */
</script>
To verify that this does in fact work, you could test it with the following files:
build.js
const browserify = require("browserify");
const path = require("path");
browserify("app.js")
.require("jquery")
.bundle()
.pipe(process.stdout);
app.js
const jquery = require("jquery");
console.log("Hello, world.")
index.html
<!doctype html>
<html>
<head>
<title>so-41095676</title>
</head>
<body>
<script src="./bundle.js"></script>
<script>
console.log(require("jquery"));
</script>
</body>
</html>
Create the files in a directory and run these commands:
npm install browserify jquery
node build.js > bundle.js
Then open index.html and you should see the jQuery function logged to the console.
I am using gulp to uglify and make ready my javascript files for production. What I have is this code:
var concat = require('gulp-concat');
var del = require('del');
var gulp = require('gulp');
var gzip = require('gulp-gzip');
var less = require('gulp-less');
var minifyCSS = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var js = {
src: [
// more files here
'temp/js/app/appConfig.js',
'temp/js/app/appConstant.js',
// more files here
],
gulp.task('scripts', ['clean-js'], function () {
return gulp.src(js.src).pipe(uglify())
.pipe(concat('js.min.js'))
.pipe(gulp.dest('content/bundles/'))
.pipe(gzip(gzip_options))
.pipe(gulp.dest('content/bundles/'));
});
What I need to do is to replace the string:
dataServer: "http://localhost:3048",
with
dataServer: "http://example.com",
In the file 'temp/js/app/appConstant.js',
I'm looking for some suggestions. For example perhaps I should make a copy of the appConstant.js file, change that (not sure how) and include appConstantEdited.js in the js.src?
But I am not sure with gulp how to make a copy of a file and replace a string inside a file.
Any help you give would be much appreciated.
Gulp streams input, does all transformations, and then streams output. Saving temporary files in between is AFAIK non-idiomatic when using Gulp.
Instead, what you're looking for, is a streaming-way of replacing content. It would be moderately easy to write something yourself, or you could use an existing plugin. For me, gulp-replace has worked quite well.
If you want to do the replacement in all files it's easy to change your task like this:
var replace = require('gulp-replace');
gulp.task('scripts', ['clean-js'], function () {
return gulp.src(js.src)
.pipe(replace(/http:\/\/localhost:\d+/g, 'http://example.com'))
.pipe(uglify())
.pipe(concat('js.min.js'))
.pipe(gulp.dest('content/bundles/'))
.pipe(gzip(gzip_options))
.pipe(gulp.dest('content/bundles/'));
});
You could also do gulp.src just on the files you expect the pattern to be in, and stream them seperately through gulp-replace, merging it with a gulp.src stream of all the other files afterwards.
You may also use module gulp-string-replace which manages with regex, strings or even functions.
Example:
Regex:
var replace = require('gulp-string-replace');
gulp.task('replace_1', function() {
gulp.src(["./config.js"]) // Every file allown.
.pipe(replace(new RegExp('#env#', 'g'), 'production'))
.pipe(gulp.dest('./build/config.js'))
});
String:
gulp.task('replace_1', function() {
gulp.src(["./config.js"]) // Every file allown.
.pipe(replace('environment', 'production'))
.pipe(gulp.dest('./build/config.js'))
});
Function:
gulp.task('replace_1', function() {
gulp.src(["./config.js"]) // Every file allown.
.pipe(replace('environment', function () {
return 'production';
}))
.pipe(gulp.dest('./build/config.js'))
});
I think that the most correct solution is to use the gulp-preprocess module. It will perform the actions you need, depending on the variable PRODUCTION, defined or not defined during the build.
Source code:
/* #ifndef PRODUCTION */
dataServer: "http://localhost:3048",
/* #endif */
/* #ifdef PRODUCTION **
dataServer: "http://example.com",
/* #endif */
Gulpfile:
let preprocess = require('gulp-preprocess');
const preprocOpts = {
PRODUCTION: true
};
gulp.task('scripts', ['clean-js'], function () {
return gulp.src(js.src)
.pipe(preprocess({ context: preprocOpts }))
.pipe(uglify())
.pipe(concat('js.min.js'))
.pipe(gulp.dest('content/bundles/'));
}
This is the best solution because it allows you to control the changes that are made during the build phase.
There I have a versioning specific example for your reference.
let say you have version.ts file and it contains the version code inside it. You now can do as the follows:
gulp.task ('version_up', function () {
gulp.src (["./version.ts"])
.pipe (replace (/(\d+)\.(\d+)(?:\.(\d+))?(?:\-(\w+))?/, process.env.VERSION))
.pipe (gulp.dest ('./'))
});
the above regex works for many situation on any conventional version formats.
I am enumerating the subdirectories in a directory. For each sub directory I would like to apply a number of gulp activities like less compilation, and then create an output file specific to that subdirectory.
I would like the gulp process to continue, as further transformation steps need to be performed later.
Can someone help me understand how I can create these files half way through the "gulp pipeline"?
This seems quite interesting to achieve and gulp has no limitations at all.
I will give you detailed example how I have managed to accomplish such a task a while ago.
Let assume that you have directoryA. Subdirectories childA, childB and childC are contained into directoryA. So basically your tree structure looks like:
directoryA
--childA
--childB
--childC
I am always looking for a flexible solutions so I would suggest to include a JSON file in each subdirectory naming the tasks you would like to running. Using fs you can access these files. You can also use run-sequence to execute gulp tasks synchronously.
For demo purposes place a file named manifest.json inside childA subdirectory.
Manifest.json contains the following declarations:
{
"filesToProccess" : ["./childA/*.js", "./childB/*.js"],
"tasksToRun" :["taskA", "taskB"]
}
Finally gulpfile would like this:
'use strict';
//dependencies declared into package.json
//install them using npm
var gulp = require('gulp'),
fs = require('fs'),
runSequence = require('run-sequence'),
path = require('path');
//these two array will keep the actions you have included into manifest file.
var filesHolder = [], tasksHolder = [];
gulp.task('taskA', function () {
return gulp.src(filesHolder)
.pipe(whatever)
.pipe(gulp.dest('whatever')); //chailed actions
});
gulp.task('taskB', function () {
return gulp.src(filesHolder)
.pipe(whatever)
.pipe(gulp.dest('whatever'));
});
//a simple utility function to read all subdirectories of directoryA
function getDirectories(srcpath) {
return fs.readdirSync(srcpath).filter(function(file) {
return fs.statSync(path.join(srcpath, file)).isDirectory();
});
}
//finally insert the default gulp task
gulp.task('default', function(){
var manifest;
//map directory's A subdirectories
var availableDirs = getDirectories("./directoryA");
//finally loop the available subdirectories, load each manifest file and
availableDirs.forEach(function(subdir) {
manifest = require("./directoryA/"+subdir+"manifest.json");
filesHolder = manifest.filesToProccess;
tasksHolder = manifest.tasksToRun;
runSequence( tasksHolder , function () {
console.log( " Task ended :" + tasksHolder + " for subdirectory : " + subdir);
});
});
});
I've split my gulpfile.js into several files in a /gulp folder to organize the code a little better. But now I want to pass a variable debug (boolean) into the files that will switch behaviour of the gulp command being included (eventually I will use command-line arguments, but for now I just want to make it work with a variable).
The way I've got this setup, using a method I saw in a yeoman angular/gulp package, is using an npm module called require-dir (which loads all *.js files in the /gulp folder, where each has a set of gulp tasks that are set).
gulpfile.js:
var gulp = require('gulp'),
run = require('run-sequence'),
debug = true;
require('require-dir')('./gulp');
gulp.task('default', ['build', 'server', 'watch', '...']);
Which would load something like...
gulp/build.js:
var gulp = require('gulp'),
plumber = require('gulp-plumber'),
...;
gulp.task('build', function () {
console.log(debug);
});
So when I run command gulp, which runs the default task, it will load build.js and then execute the build gulp task. But unfortunately, it seems that debug returns undefined.
How could I get this to work?
I was looking at using just module.exports() and node's require() method, but I couldn't figure out how to get the gulp task inside the included file to declare, so that it could then be run from the main gulpfile.js file (in the desired sequence).
Can anyone offer some assistance? Thanks
The normal module way, really. Just change that gulp/build.js file from not actually exporting anything to a proper require-able module:
module.exports = function(debug) {
"use strict";
var gulp = require('gulp'),
plumber = require('gulp-plumber'),
...;
gulp.task('build', function () {
console.log(debug);
});
};
and then call it in your main file as:
...
var loadGulp = require('require-dir/gulp');
...
var debug = true;
loadGulp(debug);
Node.js offers a single global variable named global which is, in fact, the same instance in all modules (unlike module which is different in each module). By setting values on global they become truly global. As an added bonus, you don't need the prefix access to global variables with global.. Both global.foo and just foo are equivalent so long as you don't have another variable named foo in scope.
I just create one object with all variables in it that I export as a module so I can use it throughout all my task files. For example
js/tasks/variables.js
module.exports = {
myBool: true
};
js/tasks/sass.js
var gulp = require('gulp'),
$ = require('gulp-load-plugins')(),
log = $.util.log,
vars = require('./variables');
gulp.task('sass', function () {
log('myBool: ' + vars.myBool); // logs "myBool: true"
vars.myBool = false;
});
js/tasks/build.js
var gulp = require('gulp'),
$ = require('gulp-load-plugins')(),
log = $.util.log,
vars = require('./variables');
gulp.task('build', function () {
log('myBool: ' + vars.myBool); // logs "myBool: false"
});
/gulpfile.js
Then you can get/set those variables from anywhere:
var gulp = require('gulp'),
$ = require('gulp-load-plugins')(),
runSequence = require('run-sequence'),
vars = require('./js/tasks/variables'),
requireDir = require('require-dir')('./js/tasks');
gulp.task('production', function(){
runSequence('sass', 'build');
});