Gulp plugin to do a JavaScript "include" - javascript

Is there a Gulp plugin that allows me to include/concatenate JavaScript files together?
I'm trying to have a way in which I can "include" the contents of one JavaScript file in to others. By include, I mean having something like this:
// main.js
var a = 2;
///include an-include-file.import.js
// an-include-file.import.js
var b = 5;
"Compile" to something like this:
// compiled.js
var a = 2;
var b = 5;
Or, probably even better, something like this:
// compiled.js v2
var a = 2;
// wrapped in an anonymous, self-calling function to isolate scope
(function () {
var b = 5;
})();
I wrote a plugin myself to do just that, but I'd like to be able to use source maps. Implementing source maps myself is a bit more effort than I'd like to devote to this little project.
Now, I know I could use something like gulp-concat, but there isn't an easy way to control their order. I'd have to modify the gulpfile every time I add a new file, and manually list them all out (or have lots of complicated patterns), which is a rather large pain.
I'd prefer something where I can use an import or include to precisely control where the file goes, and control it from the scripts themselves, not the build tool. Very similar to how LESS or something does it.
For LESS, what I do is I name suffix files with ".import.less" if I don't want them to generate their own standalone file, and then #import them where I want them in other files. This makes it very easy to only generate the files I want, without simply creating one giant file.

I ended up taking Mike's idea and using WebPack, in addition to Babel, to create ES6-style modules.
The basics look like this:
// math.js
export function sum (a, b) { return a + b; }
// main.js
import sum from "math";
console.log(sum(1, 2));
I use Gulp to handle my build process, which in a simplified manner looks like this:
var gulp = require('gulp'),
webpack = require('webpack-stream');
gulp.task('build', function () {
return gulp.src('main.js')
.pipe(webpack())
.pipe(gulp.dest('dist'));
});
My actual usage is much more complex, but that's the basic idea. I have to use babel-loader (with the ES2015 preset) in my webpack config to have it process the ES6 in to ES5, then WebPack puts them together in to one file.
Related reading:
Babel - https://babeljs.io/
Babel ES2015 Preset - https://babeljs.io/docs/plugins/preset-es2015/
WebPack - https://webpack.github.io/
WebPack with Gulp - https://webpack.github.io/docs/usage-with-gulp.html
babel-loader - https://github.com/babel/babel-loader

Related

How to set up gulp to bundle several files into one?

This seems like a very simple question, but spent the last 3 hours researching it, discovering it can be slow on every save on a new file if not using watchify.
This is my directory tree:
gulpfile.js
package.json
www/
default.htm
<script src="toBundleJsHere/file123.js"></script>
toBundletheseJs/
componentX/
file1.js
componentY/
file2.js
componentZ/
file3.js
toPutBundledJsHere/
file123.js
Requirements.
On every creation or save of a file within the folder toBundleTheseJs/ I want this file to be rebundled into toBundleJsHere/
What do I need to include in my package.json file?
And whats the minimum I need to write into my gulp file?
This should be as fast as possible so think I should be using browserify and watchify. I want to understand the minimum steps so using package manager like jspm is overkill a this point.
thanks
First you should listen to changes in the desired dir:
watch(['toBundletheseJs/**/*.js'], function () {
gulp.run('bundle-js');
});
Then the bundle-js task should bundle your files. A recommended way is gulp-concat:
var concat = require('gulp-concat');
var gulp = require('gulp');
gulp.task('bundle-js', function() {
return gulp.src('toBundletheseJs/**/*.js')
.pipe(concat('file123.js'))
.pipe(gulp.dest('./toPutBundledJsHere/'));
});
The right answer is: there is no legit need for concatenating JS files using gulp. Therefore you should never do that.
Instead, look into proper JS bundlers that will properly concatenate your files organizing them according to some established format, like commonsjs, amd, umd, etc.
Here's a list of more appropriate tools:
Webpack
Rollup
Parcel
Note that my answer is around end of 2020, so if you're reading this in a somewhat distant future keep in mind the javascript community travels fast so that new and better tools may be around.
var gulp = require('gulp');
var concat = require('gulp-concat');
gulp.task('js', function (done) {
// array of all the js paths you want to bundle.
var scriptSources = ['./node_modules/idb/lib/idb.js', 'js/**/*.js'];
gulp.src(scriptSources)
// name of the new file all your js files are to be bundled to.
.pipe(concat('all.js'))
// the destination where the new bundled file is going to be saved to.
.pipe(gulp.dest('dist/js'));
done();
});
Use this code to bundle several files into one.
gulp.task('scripts', function() {
return gulp.src(['./lib/file3.js', './lib/file1.js', './lib/file2.js']) //files separated by comma
.pipe(concat('script.js')) //resultant file name
.pipe(gulp.dest('./dist/')); //Destination where file to be exported
});

JavaScript Module pattern files concatenation in a one file and using their functions

I write different files under JavaScript Module Patern like this:
file 1.js
var Module = (function (module) {
module.function = function () {
console.log(this.thirdFunction) // This is my issue. I cannot access the function from the third file because it is concatenated in a way that the function still not exist.
};
return module;
}(Module || {}));
some-folder/file 2.js
var Module = (function (module) {
module.somethingElse = function () {
};
return module;
}(Module || {}));
whatever/file 3.js
var Module = (function (module) {
module.thirdFunction = function () {
};
}(Module || {}));
I put all these files in different directories, names in a different time.
Then I am using concatenating tool to have one file and then I use it in my html file. But I am facing trouble that I cannot resolve.
To have all these working, I have to include them in a specific way and order to call functions from whatever I need and to re-order files when something is not yet defined/created in the returned final object. If I have 100 files and folders it will be a trouble for me again.
Do I understand this right: http://gruntjs.com/configuring-tasks#globbing-patterns
that I have manually to order files, folders and everything in my grunt tasks?
I do not want to use AMD tools, just plain JavaScript files and some approach to hack the order requirements. If there is no any easy idea for me to you, I would try the AMD tools like require.js but I am not sure if these kind of tools can help with this lame problem.
I would appreciate some grunt tool, some files/folders names conventions, anything that would not force me to install more and more tools.
Thank you in advance!
Another thing that bothers me is the following:
If I want to isolate code but I do not have to return object property in the final object, is it alright to do something like this:
file 4.js
var Module = (function (module) {
var someThing = Module.somethingElse() // from file 2.js
and then using someThing here for clicking, DOM rendering, etc, etc
}(Module || {}));
Is it stupid to stick to the same var Module conventions for files where I actually do not return anything? I just think of way how to avoid the object name and using this again
Indeed, AMD tools were created just for this kind of problem. However you can work around this to some extent with grunt. You could simply organize the files that need to go first into a folder and list them out in the order you want, and then have another folder containing all files who's order doesn't matter, which will include everything.
'js/main/First.js',
'js/main/Second.js',
'js/rest/*.js'
No matter what you choose for this project, you might want to look into Require.js or Browserify for future work.

Node.js utility module structure

While working on many differente Node.js projects, I began having common code that I want to move out in a new Node.js package in order to not rewrite the same code multiple times. I've a new Node.js module and I'm using it in my projects using npm link.
Now, I'm a bit confused as to how to structure this common library in order to properly modularize it. This is what I have right now in my common library:
// "my-common-lib"'s app.js
module.exports = {
math: require("./math/mathLib"),
network: require("./network/networkLib")
};
--
//mathLib.js
exports.pi = 3.14;
This works, I can do the following in another node.js project:
var commonLibrary = require("my-common-lib");
var commonMath = commonLibrary.Math;
console.log("Pi: " + commonMath.pi);
While this solves the issue, I would prefer something similar to how lodash does it:
var commonMath = require("my-common-lib/math");
console.log("Awesome pi: " + commonMath.pi);
I can't quite figure out how lodash does it, and I would definitely like to avoid having a humongous main js file.
TL;DR I want to modularize a node.js module so I can require submodules (require("my-common-lib\myCommonMathLib")), how can I do this?
lodash does it with a dedicated modular build. Look at the ES6 build for example. Every "sub-project" has a dedicated module, in a dedicated '.js' file. The aggregating file (lodash.js) simply imports all other modules.
If you want the nice lib/module convention, simply have a your lib.js file (aggregator) at the top level, next to a directory by the same name where all internal modules are kept.
Another option for the require("lib") part is to have a "main": "lib.js" configuration in your package.json
If you want to use lodash/array for example, LoDash has an array.js file, with the following:
module.exports = {
'chunk': require('./array/chunk'),
'compact': require('./array/compact'),
So you can easily have math.js inside your main folder, which has something like:
module.exports = {
pi: 3.14
// OR
pi: require('./math/pi'); // and have file pi.js inside math folder
}
This way you can use it as a short:
var math = require('my-common-lib/math');
math.pi; // 3.14

How Do You Get Around Javascript File Order Using Gulp Or A Javascript Framework?

I'm using gulp to build a single javascript file with gulp-concat and gulp-uglify.
Original Files
//File 1
var Proj = Proj || {};
//File 2
Proj.Main = (function() {
var Method = function(){ /*Code*/ };
return { "Method":Method };
})();
//File 3
Proj.Page = (function() {
var Method = Proj.Main.Method;
return { "Method":Method };
})();
Gulp returns a bad minified file because these files are being concatenated in the wrong order. I know I can specify the order in .src([]) but I don't want to maintain the array as I add javascript files.
Is there a way to create references to these "namespaces" without having to worry about the order of the files concatenated? Or, is there a way for gulp to handle concatenation with the knowledge of these namespaces auto-magically?
EDIT:
I know I can specify the file order inside the .src([]). I want to develop without having to worry about the file order, whether it be through a gulp package or a javascript framework. Thank you for responses that help but I need a definitive "No. You cannot do this." or "Yes. Here's how..." to mark the thread as answered.
Well, one option is to try gulp-order.
Also, check out this answer to "gulp concat scripts in order?".
Basically, it mentions what you already said, about having to explicitly name the files in the order you want them to come in. I know you don't want to do that, but how else would gulp know which order you want your files in?
One thing worth pointing out, though, is that you have a group of files where the order doesn't matter, and then, say, 2 files where the order does matter, you can do something like this:
gulp.src([
'utils/*.js',
'utils/some-service.js',
'utils/something-that-depends-on-some-service'
])
gulp-concat doesn't repeat files, so everything that's not some-service.js or something-that-depends-on-some-service.js will get concatenated first, and then the last two files will be concatenated in the proper order.
Since it hasn't been mentioned, implementing webpack or browserify will absolutely solve this problem without implementing some sort of hacky feeling solution.
Here is a simple example of how to use it:
var source = require('vinyl-source-stream'), //<--this is the key
browserify = require('browserify');
function buildEverything(){
return browserify({
//do your config here
entries: './src/js/index.js',
})
.bundle()
.pipe(source('index.js')) //this converts to stream
//do all processing here.
//like uglification and so on.
.pipe(gulp.dest('bundle.js'));
}
}
gulp.task('buildTask', buildEverything);
And inside your files you use require statements to indicate which files require others.

Is there a way to dynamically set src and dest in gulpfile.js?

I have multiple sets of js modules that I would like to concat into separate files. I don't want to have to create a seperate concat task for each file. It would make more sense to be able to pass arguments into the gulp task "concat". Unfortunately gulp doesn't allow arguments to be passed into tasks(I'm sure for good reason).
Any ideas of how I can accomplish this?
Use Case
A specific scenario would be website that has a global.js file for all pages as well as page specific js files.
Creating a task for each page specific js file will quickly make the gulpfile.js hard to manage as the site grows.
My dev invironment:
I have a dev/js/ directory which has multiple sub-directories. Each sub-directory contains modules for a specific js file. So each sub-directory needs to be concatenated into it's own file within lib/js/.
Perhaps requirejs?
Maybe I should just look into using a module loader like requirejs.
I needed to take modules from my source sub-directory (src/modules/), concatenate a specific file to each individually (src/distribution), then pipe the result to a sub-directory in my distribution folder (dist/js/modules/).
I wasn't sure how many modules would end up being written for this project so I wanted to do it dynamically and found this to be the best (simplest) solution:
gulp.task("modules:js", () => {
let modules = fs.readdirSync("src/modules");
let concatModule = (module) => {
return gulp.src([
'src/distribution',
module
])
.pipe(concat(module))
.pipe(gulp.dest("build/js/modules"));
}
for (let module of modules) {
concatModule(module);
};
});
You could make concatJS a higher-order function:
var concatJS = function (src, filename, dest) {
return function() {
gulp.src(src)
.pipe(concat(filename))
.pipe(gulp.dest(dest));
};
};
gulp.task('concat-1', concatJS('src/module-1', 'module-1.js', 'build/module-1'));
gulp.task('concat-2', concatJS('src/module-2', 'module-2.js', 'build/module-2'));
//etc...
Note: You'd probably be better off using a bundler like browserify or webpack. Since asking this question I have switched to browserify rather than trying to roll my own solution.
Improved Solution:
var fs = require("fs");
/* other requires omitted */
/* Set JS dev directory in one place */
var jsSrc = "dev/js/";
var jsDest = "lib/js/";
var concat = function (path) {
path = path.replace(/\\/g, "/");
var src = path.replace(/(\/[^\/]+?\.js$)|(\/$)/, "/*.js";
var filename = src.match(/\/([^\/]+?)(\/[^\/]+?\.js$)/)[1] + ".js";
gulp.src(src)
.pipe(concat(filename)
.pipe(gulp.dest(jsDest));
});
/* The concat task just runs the concat function for
* each directory in the javascript development directory.
* It will take a performance hit, but allows concat to be
* run as a dependency in a pinch.
*/
gulp.task("concat", function () {
var dirArr = fs.readdirSync(jsDev);
for (var d in dirArr) {
var path = jsDev+dirArr[d]+"/";
concat(path);
}
});
/* Run "concat" as a dependency of the default task */
gulp.taks("default", ["concat"], function () {
var JSWatcher = gulp.watch([jsSrc+"**/*.js"]);
JSWatcher.on("change", function (event) {
concat(event.path);
});
});
Alright, I think this works. It's a little bit of a hack though, and doesn't work for all use cases.
... removed previous example to save space ...

Categories