Configuring watchify and browserify to re-run only the required files - javascript

I started working on a chrome extension.
So far I setup the project using gulp to watch the folder containing the code for background, popup, content and a few other pages.
Each component shares some some code with the others.
The problem is that every time I edit a file watchify will trigger a full rebuild.
I have tried to limit the browserify process to only handle the files that have changed. It works very well for root scripts (popup.js, background.js, content.js)...
But unfortunately I have no way of tracking dependencies (files required by the root scripts or their dependencies) and this strategy fails when a dependency is edited.
So here is my question is there a good strategy to automatically update any dependent script upon change while avoiding a full browserify of the entire tree?
gulp.task('babel', () => {
buildingTasks.start('babel');
return gulp.src(scriptSrc)
.pipe(through2.obj(function (file, enc, next){
var b = browserify(file.path, {
debug: (process.env.NODE_ENV === 'development')
});
b.transform(babelify, {presets: ['es2015', 'react']});
b.bundle(function(err, res){
if (err) return next(err);
file.contents = res;
next(null, file);
});
}))
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('map'))
.pipe(gulp.dest(scriptDest))
});
I have found this answer to access the dependencies list, but it would require building a dependency tree by hand, storing it somewhere and updating it every time a build is triggered. Is there a better solution?

For Browserify to watch the bundled files, you need to configure Watchify:
var browserify = require('browserify');
var watchify = require('watchify');
...
var options = Object.assign({}, watchify.args, {
debug: (process.env.NODE_ENV === 'development')
});
var b = watchify(browserify(file.path, options));
The watchify methods wrap the Browserify bundler and provides some shared arguments via options that are used to determine which files need to be watched. It returns the same bundler that would have been returned by browserify.

Related

Webpack 5 IgnorePlugin - Not ignoring JS file from output on CSS files only?

I am trying to use the Webpack's IngorePlugin. I am using my Webpack file only to create a CSS file. On build, it outputs a JS file. But I don't want that. Tried ignoring JS files but still outputs it.
new webpack.IgnorePlugin(/^\.\/js\/(?!admin)/),
Outputs in the ROOT folder. So I want to disable all JS files from the output in the root folder. "admin" is the file being created.
How can I do this?
To properly answer your question, it'd be helpful if you posted a link to the full WP config file and an example of the file that's being processed.
Also, you mentioned you're only using WP to create a CSS file, does that mean you're just trying to use something like SASS, Stylus, Less, etc? If so, you could probably just set up a package.json script to compile your CSS without WP.
For example, if you have a .scss file, you could install node-sass, and create a simple Node script to compile what file you pass in as an arg.
bin/
- build-css.js
src/
- styles.sass
Within build-css.js
#!/usr/bin/env node
const { basename, resolve } = require('path');
const sass = require('node-sass');
const [...files] = process.argv.slice(2);
if (files.length) {
files.forEach((relativeFilePath) => {
const fileName = basename(relativeFilePath, '.scss');
sass.render(
{
file: resolve(__dirname, relativeFilePath),
outFile: resolve(__dirname, `./public/css/${fileName}.css`),
},
(err, result) => { console.log(err); }
);
});
}
else {
console.log('No files were provided to process');
}
Within package.json
"scripts": {
"build:css": "node ./bin/build-css.js"
}
The above has the benefit of giving you the control of how your files are processed at a more granular level, and you're only locked in to any SCSS changes, instead of Webpack and SCSS.
If you're using WP for it's file watching capabilities, you could instead wire up chokidar to run the new script when you change files.

Bundling with webpack from script

I am using webpack to bundle my Javascript files in my project:
webpack --config myconfig.webpack.config.
From commandline it is ok.
Building
However I would like to create a build task, I am using jake, so in order to create the bundle I need to invoke webpack from Javascript.
I could not find the API online, I basically need something like this:
// Jakefile.js
var webpack = require("webpack");
desc('This is the default build task which also bundles stuff.');
task('default', function (params) {
webpack.bundle("path-to-config"); // Something like this?
});
How do I achieve this?
Attempt 1
I have tried the following:
// Jakefile.js
var webpack = require("webpack");
var config = require("./webpack.config.js");
desc('This is the default build task which also bundles stuff.');
task('default', function (params) {
webpack(config);
});
webpack.config.js is my config for webpack. When I use from commandline and reference that file the bundle is correctly created. But when using the above code it does not work. When I execute it, no errors, but the bundle is not emitted.
In your Attempt 1, you seem to be consuming the webpack's Node.js API by passing the config to webpack method. If you take this approach, webpack method will return a compiler object and you need to handle it correctly.
For e.g.,
import webpack from 'webpack';
var config = {}; // Your webpack config
var wpInstanceCompiler = webpack(config);
wpInstanceCompiler.run(function(err, stats) {
if (stats.hasErrors()) {
console.log(stats.toJson("verbose");
}
});
This is how you execute a webpack config via the Node.js API. Unless you run the compiler instance, the output will not get generated.
This worked for me as well:
var webpack = require("webpack");
var lib = require(path.join(__dirname, "webpack.config.js"));
desc('Builds the projects and generates the library.');
task('default', function() {
webpack(lib, function() {
console.log("Bundle successfully created!");
});
});

ReactJS with Flux architecture generating large JS files, what is the best practice?

I've started using React with Flux architecture for full functional frontend application, and I really liked approach JSX and Flux , but the main issue is that when I'm building JSX files using Gulp, Babel and Uglyfy I'm getting about 1mb minified JS file, without minified mode it is giving almost 8mb of JS file.
And that's not the end !! for making AJAX requests React don't have built in functionality, so I need also include jQuery
I's working well, development process is faster and code is nicer than with other frameworks thanks to JSX. But how to make production files smaller ?
I'm including just a few libs Dispatcher and EventEmmiter for Flux architecture. So it's not about having unused libs in my code. I think it is because I'm writing JSX and now I have HTML+JS in one single file.
What is the best practice to split files or make JS output more smaller ?
Thanks !
There are some steps to reduce production size:
Use production version of ReactJS, which includes extra performance optimizations and strips all error messages.
You don't have to include the whole jQuery library to just use Ajax, I suggest use other lightweight library for handling ajax only e.g. reqwest or superagent.
When build for production, separate to two js files (or more), usually we will have one file called vendor.js for all libraries and app.js for just the code we made. This will leverage cache on browser as vendor.js won't change much each built.
I saw that there is a little information about using React for different pages, so I learned a lot from Gulp documentation and I found a lot of very small JS libraries MicroJS which could replace Dispatcher and EventEmmiter with just a 4-6 KB in size of course you need to do some manual work with them, but they saving about 20 times in JS file size.
Here is my Gulp file for generating minified react bundle for each page. I'm using Django for backend.
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var htmlreplace = require('gulp-html-replace');
var source = require('vinyl-source-stream');
var browserify = require('gulp-browserify');
var reactify = require('reactify');
var babelify = require('babelify');
var streamify = require('gulp-streamify');
var fs = require("fs");
var rename = require("gulp-rename");
var path = {
APP_DIR: './apps/*.jsx',
OUT_DIR: './../static/apps',
};
process.env.NODE_ENV = 'development';
gulp.task('jsx', function() {
return gulp.src([path.APP_DIR])
.pipe(browserify({
insertGlobals : true,
debug : true,
transform: [reactify, babelify.configure({
presets: ["es2015", "react"]
})],
}))
.pipe(rename({
extname: ".min.js"
}))
.pipe(gulp.dest(path.OUT_DIR));
});
gulp.task('jsx_min', function() {
return gulp.src([path.APP_DIR])
.on('error', function (error) {
console.log(error);
})
.pipe(browserify({
insertGlobals : true,
debug : false,
transform: [reactify, babelify.configure({
presets: ["es2015", "react"]
})],
}))
.pipe(streamify(uglify().on('error', function (e) {
console.log(e);
})))
.pipe(rename({
extname: ".min.js"
}))
.pipe(gulp.dest(path.OUT_DIR));
});
gulp.task('build', ['jsx_min']);
gulp.task('default', ['jsx'], function () {
return gulp.watch([path.APP_DIR], ['jsx', function () {
var current_date = new Date();
var time = current_date.getHours() + ":" + current_date.getMinutes() + ":" + current_date.getSeconds();
console.log(time, " -> Rebuilding");
}]);
});
Now for each logical page I got about 40KB minified JS file for handling all JavaScript including AJAX functionality.
So I'm marking my question answered :)
Thanks for help.

How can I exclude code path when bundling with webpack/browserify?

I have a library that can be used with both node.js and the browser. I am using CommonJS then publishing for the web version using webpack. My code looks like this:
// For browsers use XHR adapter
if (typeof window !== 'undefined') {
// This adapter uses browser's XMLHttpRequest
require('./adapters/xhr');
}
// For node use HTTP adapter
else if (typeof process !== 'undefined') {
// This adapter uses node's `http`
require('./adapters/http');
}
The problem I am encountering is that when I run webpack (browserify would do the same) the generated output includes http along with all it's dependencies. This results in a HUGE file which is not optimal for browser performance.
My question is how can I exclude the node code path when running a module bundler? I solved this temporarily by using webpack's externals and just returning undefined when including './adapters/http'. This doesn't solve the use case where other developers depend on my library using CommonJS. Their build will end up with the same problem unless they use similar exclude config.
I've looked at using envify, just wondering if there is another/better solution.
Thanks!
You may use IgnorePlugin for Webpack. If you are using a webpack.config.js file, use it like this:
var webpack = require('webpack')
var ignore = new webpack.IgnorePlugin(/^(canvas|mongoose|react)$/)
module.exports = {
//other options goes here
plugins: [ignore]
}
To push it further, you may use some flags like process.env.NODE_ENV to control the regex filter of IgnorePlugin
In order to exclude node_modules and native node libraries from being bundled, you need to:
Add target: 'node' to your webpack.config.js. This will exclude native node modules (path, fs, etc.) from being bundled.
Use webpack-node-externals in order to exclude all other node_modules.
So your result config file should look like:
var nodeExternals = require('webpack-node-externals');
...
module.exports = {
...
target: 'node', // in order to ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
...
};
This worked best for me
var _process;
try {
_process = eval("process"); // avoid browserify shim
} catch (e) {}
var isNode = typeof _process==="object" && _process.toString()==="[object process]";
as Node will return true and not only will the browser return false, but browserify will not include its process shim when compiling your code. As a result, your browserified code will be smaller.
Edit: I wrote a package to handle this more cleanly: broquire
You can use require.ensure for bundle splitting. For example
if (typeof window !== 'undefined') {
console.log("Loading browser XMLHttpRequest");
require.ensure([], function(require){
// require.ensure creates a bundle split-point
require('./adapters/xhr');
});
} else if (typeof process !== 'undefined') {
console.log("Loading node http");
require.ensure([], function(require){
// require.ensure creates a bundle split-point
require('./adapters/http');
});
}
See code splitting for more information and a sample demo usage here

how to output multiple bundles with browserify and gulp

I have browserify bundling up files and it's working great. But what if I need to generate multiple bundles?
I would like to end up with dist/appBundle.js and dist/publicBundle.js
gulp.task("js", function(){
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest("./dist"));
});
Obviously this isn't going to work since I am only specifying one output (bundle.js). I can accomplish this by repeating the above statement like so (but it doesn't feel right, because of the repetition):
gulp.task("js", function(){
browserify([
"./js/app.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest("./dist"));
browserify([
"./js/public.js"
])
.bundle()
.pipe(source("publicBundle.js"))
.pipe(gulp.dest("./dist"));
});
Is there a better way to tackle this? Thanks!
I don't have a good environment to test this in right now, but my guess is that it would look something like:
gulp.task("js", function(){
var destDir = "./dist";
return browserify([
"./js/app.js",
"./js/public.js"
])
.bundle()
.pipe(source("appBundle.js"))
.pipe(gulp.dest(destDir))
.pipe(rename("publicBundle.js"))
.pipe(gulp.dest(destDir));
});
EDIT: I just realized I mis-read the question, there should be two separate bundles coming from two separate .js files. In light of that, the best alternative I can think of looks like:
gulp.task("js", function(){
var destDir = "./dist";
var bundleThis = function(srcArray) {
_.each(srcArray, function(source) {
var bundle = browserify(["./js/" + source + ".js"]).bundle();
bundle.pipe(source(source + "Bundle.js"))
.pipe(gulp.dest(destDir));
});
};
bundleThis(["app", "public"]);
});
gulp.task("js", function (done) {
[
"app",
"public",
].forEach(function (entry, i, entries) {
// Count remaining bundling operations to track
// when to call done(). Could alternatively use
// merge-stream and return its output.
entries.remaining = entries.remaining || entries.length;
browserify('./js/' + entry + '.js')
.bundle()
// If you need to use gulp plugins after bundling then you can
// pipe to vinyl-source-stream then gulp.dest() here instead
.pipe(
require('fs').createWriteStream('./dist/' + entry + 'Bundle.js')
.on('finish', function () {
if (! --entries.remaining) done();
})
);
});
});
This is similar to #urban_racoons answer, but with some improvements:
That answer will fail as soon as you want the task to be a dependency of another task in gulp 3, or part of a series in gulp 4. This answer uses a callback to signal task completion.
The JS can be simpler and doesn't require underscore.
This answer is based on the premise of having a known list of entry files for each bundle, as opposed to, say, needing to glob a list of entry files.
Multiple bundles with shared dependencies
I recently added support for multiple bundles with shared dependencies to https://github.com/greypants/gulp-starter
Here's the array of browserify config objects I pass to my browserify task. At the end of that task, I iterate over each config, browserifying all the things.
config.bundleConfigs.forEach(browserifyThis);
browserifyThis takes a bundleConfig object, and runs browserify (with watchify if dev mode).
This is the bit that sorts out shared dependencies:
// Sort out shared dependencies.
// b.require exposes modules externally
if(bundleConfig.require) b.require(bundleConfig.require)
// b.external excludes modules from the bundle, and expects
// they'll be available externally
if(bundleConfig.external) b.external(bundleConfig.external)
This browserify task also properly reports when all bundles are finished (the above example isn't returning streams or firing the task's callback), and uses watchify when in devMode for super fast recompiles.
Brian FitzGerald's last comment is spot on. Remember that it's just JavaScript!

Categories

Resources