Running multiple transforms on gulp/browserify bundle - node.js

I have a React component that I've been exporting to a bundle file. I've been successfully transforming it using babelify, however, I now would like to run envify on it. I can't seem to figure out how to run multiple transforms using browserify. I think it must be possible, but I can't tell if my syntax is slightly off or if I need to write a custom transform or if I should specify the transforms in my package.json. Here is the code in my gulp file:
var bundleComponent = function(bundler, source, component) {
//bundler is just browserify('./client/components/TVScheduleTab/render.js')
gutil.log('Bundling ' + component)
return bundler
//this throws an error
.transform(babelify, envify({
NODE_ENV: 'production'
}))
.bundle()
.on('error', function(e){
gutil.log(e);
})
.pipe(source)
.pipe(gulp.dest('output/'));
};

Have you tried chaining? Correct solution is in comments
var bundleComponent = function(bundler, source, component) {
//bundler is just browserify('./client/components/TVScheduleTab/render.js')
gutil.log('Bundling ' + component)
return bundler
//this throws an error
.transform(babelify)
.transform(envify({
NODE_ENV: 'production'
}))
.bundle()
.on('error', function(e){
gutil.log(e);
})
.pipe(source)
.pipe(gulp.dest('output/'));
};

Although this answer appears after the accepted answer, and the accepted somewhat covers the question, I wanted to make it clear without the need to navigate through the linked github issue.
Chaining, in particular with envify, should look like:
// NOTE: the "custom" part
var envify = require('envify/custom');
gulp.task('build-production', function() {
browserify(browserifyOptions)
.transform(babelify.configure(babelifyOptions))
.transform(envify({
NODE_ENV: 'production'
}))
.bundle()
.on('error', handleErrors)
.pipe(source('app.js'))
.pipe(buffer())
.pipe(uglify({ mangle: false }))
.pipe(gulp.dest('./build/production/js'));
});

Related

Can't use Worker-Loader with Vuejs and Webpack

I am trying to get web workers up and running with Vue cli3 and I'm having trouble getting it to work.
I want to use the following package, worker-loader (and not vue-worker), as it looks well maintained and with more contributions.
Following their tutorial I attempted to modify webpack using the vue cli as follows:
module.exports = {
chainWebpack: config => {
config.module
.rule('worker-loader')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end()
}
}
which I hope should match their
{
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' }
}
]
}
}
which can be read here (https://github.com/webpack-contrib/worker-loader). I tried to follow the documentation for vue cli3 as best I could (found here: https://cli.vuejs.org/guide/webpack.html#simple-configuration).
My component is pretty simple:
import Worker from 'worker-loader!./../../sharedComponents/equations/recurringTimeComposer.js';
<...>
watch:{
recurringPaymentReturnObj: function(newVal, oldVal){
const myWorker = new Worker;
myWorker.postMessage({ hellothere: 'sailor' });
myWorker.onmessage = (e) => {
console.log('value of e from message return', e.data);
}
}
<...>
and in my ./../../sharedComponents/equations/recurringTimeComposer.js file I have:
onmessage = function(e) {
console.log('Message received from main script: ', e.data);
// var workerResult = 'Result: ' + e.data;
// console.log('Posting message back to main script');
postMessage('hello back handsome');
close();
}
I keep getting the error message:
ReferenceError: window is not defined a162426ab2892af040c5.worker.js:2:15
After some googling I came across this post: https://github.com/webpack/webpack/issues/6642, which suggests that the best way to fix this is to add the following to webpack:
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
publicPath: 'http://localhost:3000',
globalObject: 'this'
},
After modifying my vue.config.js file I have:
module.exports = {
chainWebpack: config => {
config.module
.rule('worker-loader')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end()
config
.output
.path(path.join(__dirname, 'dist'))
.filename('bundle.js')
.publicPath('http://localhost:8080')
.globalObject('this')
}
}
...but still I am getting the window is not defined error.
Does anyone know what is going wrong? It seems to be a weird error in webpack.
Thanks!
EDIT: oh yeah, here is the MDN page for webworker as well: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers.
Being new to Javascript I kept coming back to this issue when trying to use web workers with VueJS. I never managed to make it work with vue-worker or worker-loader.
It is now 2020 and Google has released worker-plugin.
To use it create a module my-worker with two files index.js and worker.js.
index.js creates the module:
const worker = new Worker('./worker.js', { type: 'module' });
const send = message => worker.postMessage({
message
})
export default {
worker,
send
}
worker.js contains the logic:
import _ from 'lodash'
addEventListener("message", async event => {
let arrayToReverse = event.data.message.array
let reversedArray = _.reverse(arrayToReverse)
// Send the reversed array
postMessage(reversedArray)
});
You will also need to update your vue.config.js to use the WorkerPlugin:
const WorkerPlugin = require('worker-plugin')
module.exports = {
configureWebpack: {
output: {
globalObject: "this"
},
plugins: [
new WorkerPlugin()
]
}
};
Now you can use you worker in your components:
Import it with import worker from '#/my-worker'.
Setup a listener in the mounted() lifecycle hook with worker.worker.onmessage = event => { // do something when receiving postMessage }
Start the worker with worker.send(payload).
I set up a starter code on github. I still haven't managed to make HMR work though...
This works for me (note the first line):
config.module.rule('js').exclude.add(/\.worker\.js$/)
config.module
.rule('worker-loader')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
The first line excludes worker.js files, so two loaders wouldn't fight over the same js file
is this what you need ? Vue issue with worker-loader
Updating from the classic vue & webpack config, I found out that to make this one work, I needed to deactivate parallelization.
// vue.config.js
module.exports = {
parallel: false,
chainWebpack: (config) => {
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader')
.loader('worker-loader')
.end();
}
};
I tried add web worker to a vue-cli4 project, and here is what I found:
using worker-loader and make configs in chainWebpack:
HMR works fine, but sourcemap broke, it show babel transformed code.
using worker-plugin as #braincoke mentioned:
HMR broke, but sourcemap works fined. and eslint broke while suggested disable all worker js file eslint instead.
Finally, My solution is tossing vue-cli away, and embrace vite.It support worker natively, and all just go fine now. (I think upgrade webpack to v5 can solve this, but i never tried.)

sourceComment in Gulp is showing path relative to computer, rather than folder

I have been doing sourceComments for a project I am working with and the comments that get outputted in the CSS so that I can find the correct scss file are showing paths that are relatvie to my computer
e.g.
/* line 39, /Applications/MAMP/htdocs/usaa/bare-minimum/framework/scss/settings/_typography.scss */
I would like this to be
/* line 39, settings/_typography.scss */
How would I accomplish this when this is my gulpfile.js (showing here the sass function)
var sassSrc = './framework/scss/*.scss';
var watchSrc = './framework/**/*.scss';
function doSass(cb) {
gulp.src([sassSrc, watchSrc], {
base: 'framework/scss'
})
.pipe(sourcemaps.init())
.pipe(sass({
errLogToConsole: true,
soureMap: 'scss',
sourceComments: 'map'
}).on('error', sass.logError))
.pipe(cmq())
.pipe(autoprefixer())
.pipe(sourcemaps.write())
.pipe(gulp.dest('dev/css/'))
.pipe(cssmin())
.pipe(rename({
extname: '.min.css'
}))
.pipe(gulp.dest('dev/css/'))
.on('end', function() {
if (cb && typeof cb === 'function')
cb();
});
}
gulp.task('sass', doSass);
Unfortunately it's not possible with LibSass. Answer from node-sass member:
With sourceComments option, libsass emits diagnostics information for each selector it encounters.
It is supposed to be a raw debug info, hence the paths are not (relatively) resolved.
On a more serious note, this info emerges from core abstract syntax tree, where resolving canonical file paths will slow down the performance of the overall compilation.
For the browser dev tools aid, please use sourceMap option instead, as source-map carries relative paths.
Link to Github discussion
Discussion on libsass: Link

Using Gulp to build requireJS project - gulp-requirejs

I am trying to use gulp-requirejs to build a demo project. I expect result to be a single file with all js dependencies and template included. Here is my gulpfile.js
var gulp = require('gulp');
var rjs = require('gulp-requirejs');
var paths = {
scripts: ['app/**/*.js'],
images: 'app/img/**/*'
};
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
// The default task (called when you run `gulp` from cli)
gulp.task('default', ['requirejsBuild']);
The above build file works with no error, but the result.js only contains the content of main.js and config.js. All the view files, jquery, underscore, backbone is not included.
How can I configure gulp-requirejs to put every js template into one js file?
If it is not the right way to go, can you please suggest other method?
Edit
config.js
require.config({
paths: {
"almond": "/bower_components/almond/almond",
"underscore": "/bower_components/lodash/dist/lodash.underscore",
"jquery": "/bower_components/jquery/dist/jquery",
"backbone": "/bower_components/backbone/backbone",
"text":"/bower_components/requirejs-text/text",
"book": "./model-book"
}
});
main.js
// Break out the application running from the configuration definition to
// assist with testing.
require(["config"], function() {
// Kick off the application.
require(["app", "router"], function(app, Router) {
// Define your master router on the application namespace and trigger all
// navigation from this instance.
app.router = new Router();
// Trigger the initial route and enable HTML5 History API support, set the
// root folder to '/' by default. Change in app.js.
Backbone.history.start({ pushState: false, root: '/' });
});
});
The output is just a combination this two files, which is not what I expected.
gulp-requirejs has been blacklisted by the gulp folks. They see the RequireJS optimizer as its own build system, incompatible with gulp. I don't know much about that, but I did find an alternative in amd-optimize that worked for me.
npm install amd-optimize --save-dev
Then in your gulpfile:
var amdOptimize = require('amd-optimize');
var concat = require('gulp-concat');
gulp.task('bundle', function ()
{
return gulp.src('**/*.js')
.pipe(amdOptimize('main'))
.pipe(concat('main-bundle.js'))
.pipe(gulp.dest('dist'));
});
The output of amdOptimize is a stream which contains the dependencies of the primary module (main in the above example) in an order that resolves correctly when loaded. These files are then concatenated together via concat into a single file main-bundle.js before being written into the dist folder.
You could also minify this file and perform other transformations as needed.
As an aside, in my case I was compiling TypeScript into AMD modules for bundling. Thinking this through further I realized that when bundling everything I don't need the asynchronous loading provided by AMD/RequireJS. I am going to experiment with having TypeScript compile CommonJS modules instead, then bundling them using webpack or browserify, both of which seem to have good support within gulp.
UPDATE
My previous answer always reported taskReady even if requirejs reported an error. I reconsidered this approach and added error logging. Also I try to fail the build completely as described here gulp-jshint: How to fail the build? because a silent fail really eats your time.
See updated code below.
Drew's comment about blacklist was very helpfull and gulp folks suggest using requirejs directly. So I post my direct requirejs solution:
var DIST = './dist';
var requirejs = require('requirejs');
var requirejsConfig = require('./requireConfig.js').RJSConfig;
gulp.task('requirejs', function (taskReady) {
requirejsConfig.name = 'index';
requirejsConfig.out = DIST + 'app.js';
requirejsConfig.optimize = 'uglify';
requirejs.optimize(requirejsConfig, function () {
taskReady();
}, function (error) {
console.error('requirejs task failed', JSON.stringify(error))
process.exit(1);
});
});
The file at ./dist/app.js is built and uglified. And this way gulp will know when require has finished building. So the task can be used as a dependency.
My solution works like this:
./client/js/main.js:
require.config({
paths: {
jquery: "../vendor/jquery/dist/jquery",
...
},
shim: {
...
}
});
define(["jquery"], function($) {
console.log($);
});
./gulpfile.js:
var gulp = require('gulp'),
....
amdOptimize = require("amd-optimize"),
concat = require('gulp-concat'),
...
gulp.task('scripts', function(cb) {
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
configFile: "./client/js/main.js",
baseUrl: './client/js'
}
))
.pipe(concat('main.js'));
.pipe(gulp.dest(path.destScripts));
}
...
This part was important:
configFile: "./client/js/main.js",
baseUrl: './client/js'
This allowed me to keep my configuration in one place. Otherwise I was having to duplicate my paths and shims into gulpfile.js.
This works for me. I seems that one ought to add in uglification etc via gulp if desired. .pipe(uglify()) ...
Currently I have to duplicate the config in main.js to run asynchronously.
....
var amdOptimize = require("amd-optimize");
...
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
paths: {
jquery: "client/vendor/jquery/dist/jquery",
jqueryColor: "client/vendor/jquery-color/jquery.color",
bootstrap: "client/vendor/bootstrap/dist/js/bootstrap",
underscore: "client/vendor/underscore-amd/underscore"
},
shim: {
jqueryColor : {
deps: ["jquery"]
},
bootstrap: {
deps: ["jquery"]
},
app: {
deps: ["bootstrap", "jqueryColor", "jquery"]
}
}
}
))
.pipe(concat('main.js'));
Try this code in your gulpfile:
// Node modules
var
fs = require('fs'),
vm = require('vm'),
merge = require('deeply');
// Gulp and plugins
var
gulp = require('gulp'),
gulprjs= require('gulp-requirejs-bundler');
// Config
var
requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('app/config.js') + '; require;'),
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
name: 'main',
baseUrl: './app',
out: 'result.js',
paths: {
requireLib: 'bower_modules/requirejs/require'
},
insertRequire: ['main'],
// aliases from config.js - libs will be included to result.js
include: [
'requireLib',
"almond",
"underscore",
"jquery",
"backbone",
"text",
"book"
]
});
gulp.task('requirejsBuild', ['component-scripts', 'external-scripts'], function (cb) {
return gulprjs(requireJsOptimizerConfig)
.pipe(gulp.dest('app/dist'));
});
Sorry for my english. This solution works for me. (I used gulp-requirejs at my job)
I think you've forgotten to set mainConfigFile in your gulpfile.js. So, this code will be work
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
mainConfigFile: 'path_to_config/config.js',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
In addition, I think when you run that task in gulp, require can not find its config file and
This is not gulp-requirejs fault.
The reason why only main.js and config.js is in the output is because you're not requiring/defining any other files. Without doing so, the require optimizer wont understand which files to add, the paths in your config-file isn't a way to require them!
For example you could load a main.js file from your config file and in main define all your files (not optimal but just a an example).
In the bottom of your config-file:
// Load the main app module to start the app
requirejs(["main"]);
The main.js-file: (just adding jquery to show the technique.
define(["jquery"], function($) {});
I might also recommend gulp-requirejs-optimize instead, mainly because it adds the minification/obfuscation functions gulp-requirejs lacks: https://github.com/jlouns/gulp-requirejs-optimize
How to implement it:
var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('requirejsoptimize', function () {
return gulp.src('src/js/require.config.js')
.pipe(requirejsOptimize(function(file) {
return {
baseUrl: "src/js",
mainConfigFile: 'src/js/require.config.js',
paths: {
requireLib: "vendor/require/require"
},
include: "requireLib",
name: "require.config",
out: "dist/js/bundle2.js"
};
})).pipe(gulp.dest(''));
});

gulp-sass, watch stops when invalid property name

watch stops when error messages occur.
stream.js:94
throw er; // Unhandled stream error in pipe.
^
source string:51: error: invalid property name
How I can keep watch running and just to tell me where is the error located.
grunt could deal with errors and doesn't need to stop,
styleSheet.scss:41: error: invalid property name
otherwise, I need to keep typing "gulp" in the command-line when an error occurs.
This answer has been appended to reflect recent changes to Gulp. I've retained the original response, for relevance to the OPs question. If you are using Gulp 2.x, skip to the second section
Original response, Gulp 1.x
You may change this default behavior by passing errLogToConsole: true as an option to the sass() method.
Your task might look something like this, right now:
gulp.task('sass', function () {
gulp.src('./*.scss')
.pipe(sass())
.pipe(gulp.dest('./'));
});
Change the .pipe(sass()) line to include the errLogToConsole: true option:
.pipe(sass({errLogToConsole: true}))
This is what the task, with error logging, should look like:
gulp.task('sass', function () {
gulp.src('./*.scss')
.pipe(sass({errLogToConsole: true}))
.pipe(gulp.dest('./'));
});
Errors output will now be inline, like so:
[gulp] [gulp-sass] source string:1: error: invalid top-level expression
You can read more about gulp-sass options and configuration, on nmpjs.org
Gulp 2.x
In Gulp 2.x errLogToConsole may no longer be used. Fortunately, gulp-sass has a method for handling errors. Use on('error', sass.logError):
gulp.task('sass', function () {
gulp.src('./sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css'));
});
If you need more fine-grained control, feel free to provide a callback function:
gulp.task('sass', function () {
gulp.src('./sass/**/*.scss')
.pipe(sass()
.on('error', function (err) {
sass.logError(err);
this.emit('end');
})
)
.pipe(gulp.dest('./css'));
});
This is a good thread to read if you need more information on process-control: https://github.com/gulpjs/gulp/issues/259#issuecomment-55098512
Actually above anwsers doesn't work for me (Im using gulp-sass 3.XX). What really worked:
gulp.task('sass', function () {
return gulp.src(config.scssPath + '/styles.scss')
.pipe(sourcemaps.init())
.pipe(sass({ outputStyle: 'compressed' })
.on('error', sass.logError)
)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(config.cssPath))
});
In gulp-sass 3.x.x when I was using "sass.logError(err);" I constantly recive error that "this.emit('end'); is not a function". Now when I'm using:
.pipe(sass({ outputStyle: 'compressed' })
.on('error', sass.logError)
)
everything is working like a charm
In gulp "^2.0.0" the option errLogToConsole will no longer work. Instead gulp-sass has a built in error logging callback that uses gulp-util under the hood. Also, because gulp has some problems with killing the process on errors, if you are using with watch you will have to call this.emit('end')
https://github.com/gulpjs/gulp/issues/259#issuecomment-55098512
var sass = require('gulp-sass');
//dev
sass(config.sassDev)
.on('error', function(err) {
sass.logError(err);
this.emit('end'); //continue the process in dev
})
)
//prod
sass(config.sassProd).on('error', sass.logError)
A heads up for Gulp 3 users:
I liked #dtotheftp solution above, regarding gulp 2.x. Interestingly, it doesn't work unter Gulp3, at least not under #3.9.1:
.on('error', function(err){
sass.logError(err);
this.emit('end'); //continue the process in dev
})
gets me
TypeError: this.emit is not a function
at Function.logError (/depot/myproject/node_modules/gulp-sass/index.js:181:8)
Note, that the complaint is not coming from his this.emit() in the gulpfile but rather from the sass node-module, hence from the prior line.
This works for me:
.on('error', function(err){
gutil.log(err);
this.emit('end');
})
I do get all errors², and the watch never ends ;) (I am also using plumber() right after gulp.src(), which might help with that).
(Yes, the fix might be highly illogical, since sass.logError is said to be based on gutil...)
—
²also on undefined macros which went silent before on my setup for whatever reason.

Crossroads and requirejs

I'm having trouble getting a route to match using crossroads with requirejs. Well, it probably has nothing to do with requirejs, just thought I'd mention it.
This is what my code looks like:
require.config({
shim: {
/* use shims to define dependencies for modules. e.g.,
'jquery.colorize': ['jquery'],
'jquery.scroll': ['jquery'],
*/
'crossroads': ['signals', 'can']
},
paths: {
"jquery": "http://code.jquery.com/jquery-1.8.2",
"can": "/scripts/can/amd/can",
"can.fixture": "/scripts/can/amd/can/util/fixture",
"signals": "/scripts/signals/signals",
"crossroads": "/scripts/crossroads/crossroads"
}
});
require(['jquery', 'crossroads', 'controllers/project'], function ($, crossroads, projectController) {
var projectRoute = crossroads.addRoute('projects', function () {
$(document).ready(function () {
console.log('projects ready');
$.when(ProjectModel.findAll()).then(function (projectResponse) {
var projects = new SortList(projectResponse);
console.log('doc ready projects=', projects);
new ProjectsControl('#projects', {
projects: projects,
defaultSort: 'priority'
});
});
});
})
console.log('matched: ', projectRoute.match(window.location.href));
});
The url that it's trying to match is http://localhost:34382/projects and the output of console.log is "matched: false"
Any suggestions?
I figured out that I need to change what I'm matching on. I've changed this line
console.log('matched: ', projectRoute.match(window.location.href));
to this
console.log('matched: ', projectRoute.match(window.location.pathname + window.location.search));
and now it works.
Also realized that you have to explicitly call crossroads.parse() somewhere for it to work (or am I missing something?)
so at the end of my require function I call this and my route is found
crossroads.parse(window.location.pathname + window.location.search);
Also, in case anyone is wondering, I'm using CanJS as the client-side MVC framework and ASP.NET MVC4 as the server-side.

Resources