gulp-nodemon onChange event triggered twice - javascript

I'm trying to set my project's gulp tasks in order and this how my gulpfile.js code looks currently:
var gulp = require('gulp'),
nodemon = require('gulp-nodemon'),
webpack = require('gulp-webpack');
gulp.task('nodemon', ['webpack'], function(){
console.log('NODEMON');
return nodemon({
script: 'server/server.js',
ext: 'js',
ignore: [
'client/game.js'
],
tasks: function(changedFiles){
var tasks = ['webpack'];
changedFiles.forEach(function(file){
console.log('changed file: ' + file);
});
return tasks;
},
env: { 'NODE_ENV': 'development' }
});
});
gulp.task('webpack', function(){
console.log('WEBPACK');
return gulp.src('src/main.js')
.pipe(webpack({
entry: `${__dirname}/src/main.js`,
output: {
path: `${__dirname}/client/`,
filename: 'game.js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['es2015']
}
}
]
}
}))
.pipe(gulp.dest('client/'));
});
gulp.task('default', ['webpack', 'nodemon'], function(){
console.log('DEFAULT');
});
First of all, everything works and that's fine. But when I modify my server.js file, it always triggers the onChange event twice causing the 'webpack' run and the server restart twice. What am I doing wrong?
Edit: The strange thing is that every time I modify a script, not only does it trigger the 'webpack' task run as it's supposed to be, but it also re-runs my gulpfile.js script, duplicating every task it runs, which is why the server starts twice. My problem now is why would gulpfile.js be run all over each time. This doesn't happen if I skip the tasks parameter.

Related

webpack watch mode with CopyWebpackPlugin causes infinite loop

In webpack, CopyWebpackPlugin causes infinite loop when webpack is in watch mode. I tried to add watchOptions.ignored option but it doesn't seem to work.
My webpack config is following:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = {
entry: {
'res': './src/index.js'
},
output: {
filename: '[name].min.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new CopyWebpackPlugin([
{ from: 'dist', to: path.resolve(__dirname, 'docs/js') }
], {})
],
watchOptions: {
ignored: path.resolve(__dirname, 'docs/js')
}
};
module.exports = config;
Any help would be appreciated.
With CopyWebpackPlugin, I've experienced the infinite loop too. I tried all kinds of CopyWebpackPlugin configurations with no luck yet. After hours of wasted time I found I could hook into the compiler and fire off my own copy method.
Running Watch
I'm using webpack watch to watch for changes. In the package.json, I use this config so I can run npm run webpackdev, and it will watch for file changes.
"webpackdev": "cross-env webpack --env.environment=development --env.basehref=/ --watch"
My Workaround
I've added an inline plugin with a compiler hook, which taps into AfterEmitPlugin. This allows me to copy after my sources have been generated after the compile. This method works great to copy my npm build output to my maven target webapp folder.
// Inline custom plugin - will copy to the target web app folder
// 1. Run npm install fs-extra
// 2. Add fix the path, so that it copies to the server's build webapp folder
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
// Debugging
console.log("########-------------->>>>> Finished Ext JS Compile <<<<<------------#######");
let source = __dirname + '/build/';
// TODO Set the path to your webapp build
let destination = __dirname + '/../dash-metrics-server/target/metrics-dash';
let options = {
overwrite: true
};
fs.copy(source, destination, options, err => {
if (err) return console.error(err) {
console.log('Copy build success!');
}
})
});
}
}
The Workaround Source
Here's my webpack.config.js in total for more context. (In this webpack configuration, I'm using Sencha's Ext JS ExtWebComponents as the basis.)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BaseHrefWebpackPlugin } = require('base-href-webpack-plugin');
const ExtWebpackPlugin = require('#sencha/ext-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const fs = require('fs-extra');
module.exports = function(env) {
function get(it, val) {if(env == undefined) {return val;} else if(env[it] == undefined) {return val;} else {return env[it];}}
var profile = get('profile', '');
var emit = get('emit', 'yes');
var environment = get('environment', 'development');
var treeshake = get('treeshake', 'no');
var browser = 'no'; // get('browser', 'no');
var watch = get('watch', 'yes');
var verbose = get('verbose', 'no');
var basehref = get('basehref', '/');
var build_v = get('build_v', '7.0.0.0');
const isProd = environment === 'production';
const outputFolder = 'build';
const plugins = [
new HtmlWebpackPlugin({template: 'index.html', hash: false, inject: 'body'}),
new BaseHrefWebpackPlugin({ baseHref: basehref }),
new ExtWebpackPlugin({
framework: 'web-components',
toolkit: 'modern',
theme: 'theme-material',
emit: emit,
script: './extract-code.js',
port: 8080,
packages: [
'renderercell',
'font-ext',
'ux',
'd3',
'pivot-d3',
'font-awesome',
'exporter',
'pivot',
'calendar',
'charts',
'treegrid',
'froala-editor'
],
profile: profile,
environment: environment,
treeshake: treeshake,
browser: browser,
watch: watch,
verbose: verbose,
inject: 'yes',
intellishake: 'no'
}),
new CopyWebpackPlugin([{
from: '../node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js',
to: './webcomponents-bundle.js'
}]),
new CopyWebpackPlugin([{
from: '../node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js.map',
to: './webcomponents-bundle.js.map'
}]),
// Debug purposes only, injected via script: npm run-script buildexample -- --env.build_v=<full version here in format maj.min.patch.build>
new webpack.DefinePlugin({
BUILD_VERSION: JSON.stringify(build_v)
}),
// This causes infinite loop, so I can't use this plugin.
// new CopyWebpackPlugin([{
// from: __dirname + '/build/',
// to: __dirname + '/../dash-metrics-server/target/test1'
// }]),
// Inline custom plugin - will copy to the target web app folder
// 1. Run npm install fs-extra
// 2. Add fix the path, so that it copies to the server's build webapp folder
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
// Debugging
console.log("########-------------->>>>> Finished Ext JS Compile <<<<<------------#######");
let source = __dirname + '/build/';
// TODO Set the path to your webapp build
let destination = __dirname + '/../dash-metrics-server/target/metrics-dash';
let options = {
overwrite: true
};
fs.copy(source, destination, options, err => {
if (err) return console.error(err)
console.log('Copy build success!');
})
});
}
}
];
return {
mode: environment,
devtool: (environment === 'development') ? 'inline-source-map' : false,
context: path.join(__dirname, './src'),
//entry: './index.js',
entry: {
// ewc: './ewc.js',
app: './index.js'
},
output: {
path: path.join(__dirname, outputFolder),
filename: '[name].js'
},
plugins: plugins,
module: {
rules: [
{ test: /\.(js)$/, exclude: /node_modules/,
use: [
'babel-loader',
// 'eslint-loader'
]
},
{ test: /\.(html)$/, use: { loader: 'html-loader' } },
{
test: /\.(css|scss)$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'sass-loader' }
]
}
]
},
performance: { hints: false },
stats: 'none',
optimization: { noEmitOnErrors: true },
node: false
};
};
So I know this question is very old at this point, but I was running into this endless loop issue again recently and found a couple of solutions.
Not the cleanest method, but if you add an "assets" folder, which can be completely empty, to the root of your project, it seems to only compile after your sources folder changes.
The better method I have found is within the webpack config. The original poster mentioned about using ignored which does seem to fix the issue if you instruct webpack to watch file changes after the initial build and to ignore your dist/output folder...
module.exports = {
//...
watch: true,
watchOptions: {
ignored: ['**/dist/**', '**/node_modules'],
},
};

Redux NODE_ENV errors with Gulp/Browserify

I'm getting this error message on a React/Redux app that is minified and packaged with Browserify and Gulp and deployed to Heroku.
bundle.js:39 You are currently using minified code outside of NODE_ENV === 'production'. This means that you are running a slower development build of Redux.
But it seems the build step is being done in NODE_ENV = 'production'.
I've a task that set the env variables like so
gulp.task('apply-prod-environment', function() {
return process.env.NODE_ENV = 'production';
});
And the logs on Heroku show the ENV is production:
To guarantee the apply-prod-environment runs before the other tasks, I'm using RunSequence Gulp plugin.
gulp.task('buildProd', cb => {
runSequence(
'apply-prod-environment',
'task-1',
'task-2',
'etc',
cb
);
});
EDIT
Second Try..
import envify from 'envify/custom';
function buildJS(sourceFile, {setEnv}) {
return browserify(sourceFile)
.transform(babelify, {
presets: ['es2015', 'react', 'stage-2']
})
.transform(envify({
NODE_ENV: setEnv
}))
.bundle()
.on('error', (e) => {
gutil.log(e);
});
}
Still Getting same Error
Third Try..
function buildJS(sourceFile, {setEnv}) {
return browserify(sourceFile)
.transform(babelify, {
presets: ['es2015', 'react', 'stage-2']
})
.transform(
{global: true},
envify({
NODE_ENV: setEnv
})
)
.bundle()
.on('error', (e) => {
gutil.log(e);
});
}
Still Getting same Error
I struggled with this same problem and I ended up using loose-envify to mock the environment variables that I wanted to override.
Then, my gulp task looked like this:
gulp.task('javascript:prod', function() {
return browserify("app/main.js", { debug: !IS_PROD })
.transform("babelify", { presets: [ "es2015", "react" ], plugins: [ "transform-object-rest-spread", "transform-function-bind", "transform-object-assign" ] })
.transform('loose-envify', { NODE_ENV: 'production' })
.bundle()
.pipe(source('app.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(rev())
.pipe(gulp.dest("./public/javascripts/"))
.pipe(rev.manifest({merge:true, base: 'build/assets'}))
.pipe(gulp.dest('build/assets'));
});

Switching to webpack from gulp to handle SCSS files, need help setting up

I have been using a gulp task to build my scss files and hot-reload them as they changed.
Just moved over to webpack and trying to set up a similar environment but I'm not really getting anywhere with it...
here is my gulp task:
gulp.task('sass:watch', function() {
gulp.src('./assets/scss/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer({
browsers: ['last 2 versions']
}))
.pipe(sourcemaps.write())
.pipe(gulp.dest('app/public'))
.pipe(browserSync.stream());
});
and this is how far I am with webpack...
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'sourcemap',
entry: {},
module: {
loaders: [
{ test: /\.js$/, exclude: [/app\/lib/, /node_modules/], loader: 'ng-annotate!babel' },
{ test: /\.html$/, loader: 'raw' },
{ test: /\.scss$/, loader: 'style!css?sourceMap!sass?sourceMap' },
{ test: /\.css$/, loader: 'style!css' }
]
},
plugins: [
// Injects bundles in your index.html instead of wiring all manually.
// It also adds hash to all injected assets so we don't have problems
// with cache purging during deployment.
new HtmlWebpackPlugin({
template: 'client/index.html',
inject: 'body',
hash: true
}),
// Automatically move all modules defined outside of application directory to vendor bundle.
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
return module.resource && module.resource.indexOf(path.resolve(__dirname, 'client')) === -1;
}
})
]
};
So in summary, I want to set up webpack to watch my sass files for changes, hot-reload, and build them into a style.css in a public directory.
I trust that you have sass-loader in there, else it won't work, if not install it
npm install sass-loader node-sass webpack --save-dev
You should have something like this in your config
loaders: [
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
]

Sourcemap generated from Gulp, not working as expected

So my app is running off a concatenated admin.bundle.js file. I'm using webpack to manage the modules, and then using gulp-webpack to import my webpack config, then run the sourcemap code:
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
.pipe(sourcemaps.init())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('app/assets/js'));
});
My Webpack config
var webpack = require('webpack');
module.exports = {
entry: "./entry.js",
output: {
pathinfo: true,
path: __dirname,
filename: "admin.bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style!css" }
]
}
};
The problem is when I'm testing my app with ChromeDev tools, the break points in the individual app modules won't work. They only work when I look at the source for admin.bundle.js this isn't ideal as I need to search for a specific line in the code to goto :( instead of just having the break point happen inside of the module itself, which makes it easier and faster to debug.
Below the debugger is stopped on a function inside of the concatenated admin.bundle.js file
There is the tagsDirective.js module, this is where I expect the break point to happen :(
Anyone run into this problem before with Webpack and Gulp?
Screenshot of part of the admin.bundle and the map file:
Ah figured it out, I moved the sourcemap generation code back into webpack, and out of Gulp:
var webpack = require('webpack');
var PROD = JSON.parse(process.env.PROD_DEV || '0');
module.exports = {
entry: "./entry.js",
devtool: "source-map",
output: {
devtoolLineToLine: true,
sourceMapFilename: "admin.bundle.js.map",
pathinfo: true,
path: __dirname,
filename: PROD ? "admin.bundle.min.js" : "admin.bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style!css" }
]
},
plugins: PROD ? [
new webpack.optimize.UglifyJsPlugin({minimize: true})
] : []
};
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
// .pipe(sourcemaps.init())
// .pipe(sourcemaps.write('./'))
.pipe(gulp.dest('app/assets/js'));
});

Webpack HotModuleReplacement css

I am trying to use webpack as a replacement for gulp and livereload workflow. I have set up HotModuleReplacement Plugin and it works correctly for JS files but I can't get it to work with SCSS files. It is compiling the SCSS to CSS properly but I have to manually refresh the browser each time to get the style changes to show. I am thinking it maybe a mistake in how I have the config set up or something like that.
I have this server.js file that is running things:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
noInfo: true,
stats: { colors: true }
}).listen(3000, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:3000');
});
and this webpack.config.js
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var cssLoaders = ['css-loader'];
var jsLoaders = ['react-hot', 'babel'];
var scssLoaders = cssLoaders.slice(0);
scssLoaders.push('sass-loader?includePaths[]=' + path.resolve(__dirname, './styles'));
module.exports = {
devtool: 'sourcemap',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./scripts/index'
],
output: {
path: path.join(__dirname, './build'),
filename: 'bundle.js',
publicPath: '/build/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new ExtractTextPlugin('styles.css')
],
resolve: {
extensions: ['', '.js', '.jsx', '.scss']
},
module: {
loaders: [
{ test: /\.js?$/, loaders: jsLoaders, include: path.join(__dirname, 'scripts'), exclude: /node_modules/},
{ test: /\.css$/ ,loader: ExtractTextPlugin.extract('style-loader', cssLoaders.join('!')) },
{ test: /\.js$/, loader: "eslint-loader", exclude: /node_modules/ },
{ test: /\.scss$/, loader: ExtractTextPlugin.extract('style-loader', scssLoaders.join('!')) }
]
}
};
In one of my js files I just have a call to the SCSS file like this:
require('../styles/app');
I have looked into the docs for this and there are some instructions that suggest that you need to manually opt in for each module but I am not sure why that is, where to add that code etc
What I am trying to do seems like a pretty straight forward use case so is this supported or will I still also have to use gulp and live reload just for styles?
Unfortunately the extract-text-webpack-plugin, which is extracting all your styles to a CSS file, doesn't work well with hot-reloading (see https://github.com/webpack/extract-text-webpack-plugin/issues/30).
This hacky bit of JavaScript will reload all stylesheets any time it detects any hot reload event happening. It can get annoying, though, and works better on Firefox than on Chrome - Chrome seems to delay applying the new stylesheet until you focus the browser tab.
if (module.hot) {
$(window).on("message onmessage", function(event) {
if (typeof event.originalEvent.data === "string" && event.originalEvent.data.indexOf("webpackHotUpdate") === 0) {
console.log("Reloading style sheets...");
document.styleSheets.forEach(function (sheet) {
if ((sheet.href || "").indexOf('localhost') !== -1) {
sheet.ownerNode.href = sheet.href;
}
});
}
});
}
There may be some way to hook further into the guts of the hot reloading code, but I haven't looked into it.
As mentioned above, extract-text-webpack-plugin is not working properly with hot replacement, unfortunately :( But there's one more option how to solve this issue while development. That's disable property in extract plugin settings.
Modify a bit your config as described below:
plugins: [
new ExtractTextPlugin('styles.css', {disable: process.env.NODE_ENV !== 'production'})
]
That will disable usage of ExtractTextPlugin while development (I mean if you run it anyhow but NODE_ENV=production webpack, or any other rule you prefer) and bundle your styles within js.
Also mind importing your style file in the entry point. Hope that works. Cheers ;)
PS. Also you can avoid combining your entry point with 'webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server' and adding new webpack.HotModuleReplacementPlugin() to plugins manually just by running webpack dev server with additional params
webpack-dev-server --inline --hot
inline stands for webpack-dev-server/client and --hot for webpack-dev-server/client

Categories