Va1 / browser-sync-webpack-plugin

Easily use BrowserSync in your Webpack project.
MIT License
370 stars 42 forks source link

CSS injecting doesn't work, the page just reloads #4

Closed yantakus closed 9 years ago

yantakus commented 9 years ago

Here is my gulpfile:

import gulp from 'gulp';
import gulpLoadPlugins from 'gulp-load-plugins';
import del from 'del';
import source from 'vinyl-source-stream';
import runSequence from 'run-sequence';
import {reload} from 'browser-sync';
import historyApiFallback from 'connect-history-api-fallback';
import webpack from 'webpack-stream';
import BrowserSyncPlugin from 'browser-sync-webpack-plugin';

const AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

const $ = gulpLoadPlugins()
  , env = 'dev';

global.isWatching = false;

gulp.task('clean:dev', del.bind(null, ['.tmp'], {dot: true}));
gulp.task('clean:dist', del.bind(null, ['dist'], {dot: true}));

gulp.task('webpack', () => {
  return gulp.src('./app/scripts/app.jsx')
    .pipe(webpack({
      node: {
        net: "empty",
        dns: "empty"
      },
      debug: true,
      devtool: 'source-map',
      watch: true,
      module: {
        loaders: [
          { test: /\.jsx?$/, loader: 'babel-loader?stage=0', exclude: /node_modules/ }
        ]
      },
      output: {
        filename: "bundle.js"
      },
      resolve: {
        modulesDirectories: [
          'node_modules',
          'app/scripts'
        ],
        extensions: [ "", '.js', '.jsx' ]
      },
      plugins: [
        new BrowserSyncPlugin({
          port: 8000,
          open: false,
          minify: false,
          host: "127.0.0.1",
          server: {
            baseDir: ["app", ".tmp"],
            middleware: [ historyApiFallback() ]
          }
        })
      ]
    }))
    .pipe(gulp.dest('.tmp/scripts/'))
    .pipe(reload({stream: true}));
});

gulp.task('imagemin', () => {
  return gulp.src('app/images/*')
    .pipe($.imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}]
    }))
    .pipe(gulp.dest('dist/images'));
});

gulp.task('copy', () => {
  return gulp.src(['app/*.txt', 'app/*.ico'])
    .pipe(gulp.dest('dist'));
});

gulp.task('bundle', ['webpack'], () => {
  const assets = $.useref.assets();
  const jsFilter = $.filter(['**/*.js']);
  const htmlFilter = $.filter(['*.html']);

  return gulp.src('app/*.html')
    .pipe(assets)
    .pipe($.rev())
    .pipe(jsFilter)
    .pipe($.uglify({mangle: false}))
    .pipe(jsFilter.restore())
    .pipe(cssFilter)
    .pipe($.autoprefixer({
      browsers: ['last 5 versions']
    }))
    .pipe(cssFilter.restore())
    .pipe(assets.restore())
    .pipe($.useref())
    .pipe($.revReplace())
    .pipe(gulp.dest('dist'))
    .pipe($.size());
});

gulp.task('sass', () => {
  return gulp.src([
    'app/scss/all.scss'
  ])
    .pipe($.sourcemaps.init())
    .pipe($.sass({
      precision: 10
    }).on('error', $.sass.logError))
    .pipe($.cssInlineImages({
      webRoot: 'app/scss/mdl/'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    //.pipe($.if('*.css', $.csso()))
    //.pipe($.concat('all.min.css'))
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('.tmp/css'))
    .pipe(reload({stream: true}))
    .pipe($.size({title: 'styles'}));
});

gulp.task('setWatch', () => {
  global.isWatching = true;
});

gulp.task('watch', ['setWatch'], () => {
  gulp.watch("app/scss/**/*.scss", ['sass']);
  gulp.watch(['app/*.html'], reload);
});

gulp.task('csscomb', function() {
  return gulp.src('app/scss/app.scss')
    .pipe($.csscomb())
    .pipe(gulp.dest('app/scss'))
});

gulp.task('serve', ['clean:dev'], () => {
  runSequence(
    ['webpack', 'watch', 'sass']
  )
});

gulp.task('build', ['clean:dev', 'clean:dist'], () => {
  runSequence(
    ['webpack', 'imagemin', 'copy', 'bundle']
  )
});

gulp.task('default', ['serve']);

Browsersync starts, but it doesn't inject CSS, the page just reloads.

Did I do smth wrong?

Va1 commented 9 years ago

I am not sure what are you trying to do and what is happening. Can you explain it wider?

(Using webpack with gulp seems like a weird idea to me, though)

yantakus commented 9 years ago

I'm new to webpack. I used gulp with browserify before, then switched to webpack.

Va1 commented 9 years ago

If you want to use some build system for ES6 with gulp, then use browserify/systemjs+babel/traceur. Webpack is quite more then build system, it should work standalone, because in a way you're trying to use it, you're missing some important things like caching, etc.

So, stay with browserify if you want to use gulp. Or, If you want to switch, google for webpack+babel (or any other loader) tutorials.

dlmr commented 9 years ago

Along this same topic, seems that hot reload does not work when using Browsersync. The page will reload instead.

My assumption is that something in Browsersync triggers a reload when Webpack pushes a change and I assume this problem is in Browsersync itself and not this plugin.

dlmr commented 9 years ago

I have now looked at this more closely and found the problem on why the page reloads when it should just hot swap the code and it's with this plugin.

if (self.browserSyncIsRunning) {
  self.browserSync.reload();
} else {

https://github.com/Va1/browser-sync-webpack-plugin/blob/master/index.js#L23

What does this code do, why is it needed?

Va1 commented 9 years ago

Obviously, if webpack is in watch mode and browserSync is running, the page is being reloaded after compilation is finished (on webpack "done" event). This is what those lines are about.

I still not quite understand what are you guys trying to do. Can you @DLMR please give me detailed description? I can not get what approach you go with so you don't need reload after compilation (smth connected with this maybe?).

Also, feel free to submit a PR if you know how to fix this.

Thank you.

dlmr commented 9 years ago

I'm building a React application that is using React Hot Loader during development along with hot reload of CSS using Webpack. It's related to what you linked to.

What this does is that CSS and React components can be updated in the browser without needing to reload the page.

How is your use case where this is needed, do you use Webpack Dev Server?

I'm not entirely sure what the best approach is here but I see two options.

I can gladly create a PR for this, I just need to know what you think is the best solution to handle this.

Va1 commented 9 years ago

Aha, hm. I think, now I see.

The thing is - browserSync here is serving stuff instead Webpack Dev Server. So, yeah, since those things about code update without page reload require Webpack Dev Server (as far as I know), they won't work.

Regarding proposed solutions:

Removing browserSync from the plugin scenario is wrong imo, because the idea of this plugin was to use browserSync with webpack. Adding Hot Loader/Hot Module Replacement tricks support is a nice idea as long as browserSync serve and reload functionality is the main objective of the plugin.

So far, I can't see an easy way to do this. Probably, I just lack the understanding of Hot Loader/Hot Module Replacement stuff and should try 'em out myself.

dlmr commented 9 years ago

What I want from Browsersync is that it syncs the state between all browsers as well debugging tools. The reloading part is provided by Webpack Dev Server as you say.

I would do the following change to make my use case work, and potentially also work for @web2style.

if (self.browserSyncIsRunning && self.options.reload) {
  self.browserSync.reload();
} else {
  self.browserSync(self.options.browsersyncOptions);

https://github.com/Va1/browser-sync-webpack-plugin/blob/master/index.js#L22-L25

It would then be included in the following way.

var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new BrowserSyncPlugin({
          reload: false,
          browsersyncOptions: {
            host: 'localhost',
            port: 3000,
            server: { baseDir: ['public'] }
          }
        })
    ]
}

This is just a example and not a completed solution.

This change would require bumping the version number with a major. If this is something one really wants to avoid a backwards compatible variant of this is also possible.

dlmr commented 9 years ago

An update around this would be great when you have time @Va1

Va1 commented 9 years ago

@DLMR Hey, sorry that I did not give any update, but I am kinda on vacation during last days. I am going to finally deal with this issue on Wednesday when I am back.

dlmr commented 9 years ago

Sounds great! :+1:

Va1 commented 9 years ago

OK, so!

if (self.browserSyncIsRunning && self.options.reload) {
  self.browserSync.reload();
} else {
  self.browserSync(self.options.browsersyncOptions);

Regarding this piece of code. If I change stuff this way, the use case you are approaching would work fine? @DLMR

dlmr commented 9 years ago

I have not tested that approach but I do believe so! However I do think we also need change the else to an else if.

Like this:

if (self.browserSyncIsRunning && self.options.reload) {
  self.browserSync.reload();
} else if (!self.browserSyncIsRunning) {
  self.browserSync(self.options.browsersyncOptions);

So we not initiate it more than once. Forgot that in my example previously. And as I said before, this change would bring the version up to 1.0.0 to follow SemVer correctly.

Va1 commented 9 years ago

OK, makes sense. I'll do this way now.

Va1 commented 9 years ago

Done. Please, take a look and test it with your use case.

yantakus commented 9 years ago

@DLMR, could you please share your webpack config? I'd like to compare it with my own.

dlmr commented 9 years ago

@Va1 Works like a charm, thanks for the update! :+1:

@web2style I'm unfortunately not allowed to share directly since it is for a project at work. Does it still not work for you, even with the new version? Maybe you could ask a more direct question and I will try to answer.

Va1 commented 9 years ago

@DLMR You're welcome! Thanks for the assistance.

I am not closing the issue because @web2style is still having troubles.

Va1 commented 9 years ago

Reopening by @web2style request.

yantakus commented 9 years ago

@DLMR, do you use browserSync with webpack-dev-server? I get EADDRINUSE error when I try. I'm using both of them at 3000 port, this is the reason I think. But it doesn't work at all if I change browserSync's port (an empty page opens without bundle.js, etc.).

Here is my config:

var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var path = require('path');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

var devFlagPlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});

var AUTOPREFIXER_BROWSERS = '"ie >= 10","ie_mob >= 10","ff >= 30","chrome >= 34","safari >= 7","opera >= 23","ios >= 7","android >= 4.4","bb >= 10"';

var entry = [
  './app/scripts/index.jsx',
  './app/scss/all.scss',
];
if (process.env.DEV) {
  entry = [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
  ].concat(entry);
}

var plugins = [

];
if (process.env.DEV) {
  plugins = plugins.concat([
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    devFlagPlugin,
    new BrowserSyncPlugin({
      host: 'localhost',
      port: 8000,
      server: { baseDir: ['app'] }
    })
  ]);
}

config = {
  devtool: 'source-map',
  entry: entry,
  output: {
    path: __dirname + '/build',
    filename: 'bundle.js',
    hot: true,
  },
  plugins: plugins,
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        loaders: ['react-hot', 'babel?stage=0'],
        exclude: /node_modules/,
      },
      {
        test: /\.json?$/,
        loaders: ['json-loader']
      },
      {
        test: /\.css$/,
        loaders: [
          "style-loader",
          "css-loader?sourceMap",
          "autoprefixer-loader?{browsers:[" + AUTOPREFIXER_BROWSERS + "]}",
        ],
      },
      {
        test: /\.scss$/,
        loaders: [
          "style-loader",
          "css-loader?sourceMap",
          "autoprefixer-loader?{browsers:[" + AUTOPREFIXER_BROWSERS + "]}",
          "sass-loader?sourceMap",
        ],
      },
      {
        test: /\.png$/,
        loader: "url-loader?limit=100000",
      },
      {
        test: /\.jpg$/,
        loader: "url-loader?limit=100000",
      },
      {
        test: /\.svg$/,
        loader: "url-loader?limit=100000",
      },
    ]
  },
  resolve: {
    root: [
      path.resolve(__dirname, 'app/scripts'),
    ],
    extensions: ['', '.js', '.jsx', '.json', '.scss'],
  },
  devServer: {
    contentBase: './app',
  },
};

module.exports = config;
SimenB commented 9 years ago

@web2style We proxy the webpack server, it seems to work fine

new BrowserSyncPlugin(
  {
    host: 'localhost',
    port: 3050, // or something else not in use
    proxy: 'http://0.0.0.0:3030/' // location of webpack-dev-server
  }, {
    reload: false // You can drop this if you want, of course
  }
);
dlmr commented 9 years ago

@web2style Yes, it works fine and I'm using 3000 for BrowserSync.

new BrowserSyncPlugin({
  host: 'localhost',
  port: 3000,
  logFileChanges: false
}, {
  reload: false
});

However BrowserSync starts on port 3002 with the GUI at 3003 and Koa runs on 3000 with Webpack Dev Server runs on 3001. A bit strange that I'm not getting the same error as you...

If it still does not work for you I can take another look at it, sorry for the late reply.

Va1 commented 9 years ago

This one is resolved, I assume. Thanks everyone.

BerndWessels commented 9 years ago

Hi @Va1

I can't get it to work - and I actually don't see how it possible since webpack serves from memory and not the file system. Webpack serves well on localhost:8080 but BrowserSyncPlugin on localhost:3000 returns 404.

This is the config I use with BUILD = false and TEST = false:

// Modules
var webpack = require('webpack');
var autoprefixer = require('autoprefixer');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var BrowserSyncPlugin = require('browser-sync-webpack-plugin');

module.exports = function makeWebpackConfig (options) {
  /**
  * Environment type
  * BUILD is for generating minified builds
  * TEST is for generating test builds
  */
  var BUILD = !!options.BUILD;
  var TEST = !!options.TEST;

  /**
  * Config
  * Reference: http://webpack.github.io/docs/configuration.html
  * This is the object where all configuration gets set
  */
  var config = {};

  /**
  * Entry
  * Reference: http://webpack.github.io/docs/configuration.html#entry
  * Should be an empty object if it's generating a test build
  * Karma will set this when it's a test build
  */
  if (TEST) {
    config.entry = {}
  } else {
    config.entry = {
      app: './src/app.js'
    }
  }

  /**
  * Output
  * Reference: http://webpack.github.io/docs/configuration.html#output
  * Should be an empty object if it's generating a test build
  * Karma will handle setting it up for you when it's a test build
  */
  if (TEST) {
    config.output = {}
  } else {
    config.output = {
      // Absolute output directory
      path: __dirname + '/public',

      // Output path from the view of the page
      // Uses webpack-dev-server in development
      publicPath: BUILD ? '/' : 'http://localhost:8080/',

      // Filename for entry points
      // Only adds hash in build mode
      filename: BUILD ? '[name].[hash].js' : '[name].bundle.js',

      // Filename for non-entry points
      // Only adds hash in build mode
      chunkFilename: BUILD ? '[name].[hash].js' : '[name].bundle.js'
    }
  }

  /**
  * Devtool
  * Reference: http://webpack.github.io/docs/configuration.html#devtool
  * Type of sourcemap to use per build type
  */
  if (TEST) {
    config.devtool = 'inline-source-map';
  } else if (BUILD) {
    config.devtool = 'source-map';
  } else {
    config.devtool = 'source-map'; //'eval';
  }

  /**
  * Loaders
  * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
  * List: http://webpack.github.io/docs/list-of-loaders.html
  * This handles most of the magic responsible for converting modules
  */

  // Initialize module
  config.module = {
    preLoaders: [],
    loaders: [{
      // JS LOADER
      // Reference: https://github.com/babel/babel-loader
      // Transpile .js files using babel-loader
      // Compiles ES6 and ES7 into ES5 code
      test: /\.js$/,
      loader: 'babel',
      exclude: /(node_modules|bower_components)/
    }, {
      // ASSET LOADER
      // Reference: https://github.com/webpack/file-loader
      // Copy png, jpg, jpeg, gif, svg, woff, woff2, ttf, eot files to output
      // Rename the file using the asset hash
      // Pass along the updated reference to your code
      // You can add here any file extension you want to get copied to your output
      test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
      loader: 'file'
    }, {
      // HTML LOADER
      // Reference: https://github.com/webpack/raw-loader
      // Allow loading html through js
      test: /\.html$/,
      loader: 'raw'
    }]
  };

  // ISPARTA LOADER
  // Reference: https://github.com/ColCh/isparta-instrumenter-loader
  // Instrument JS files with Isparta for subsequent code coverage reporting
  // Skips node_modules and files that end with .test.js
  if (TEST) {
    config.module.preLoaders.push({
      test: /\.js$/,
      exclude: [
        /(node_modules|bower_components)/,
        /\.test\.js$/
      ],
      loader: 'isparta-instrumenter'
    })
  }

  // CSS LOADER
  // Reference: https://github.com/webpack/css-loader
  // Allow loading css through js
  //
  // Reference: https://github.com/postcss/postcss-loader
  // Postprocess your css with PostCSS plugins
  var cssLoader = {
    test: /\.css$/,
    // Reference: https://github.com/webpack/extract-text-webpack-plugin
    // Extract css files in production builds
    //
    // Reference: https://github.com/webpack/style-loader
    // Use style-loader in development for hot-loading
    loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss')
  };

  // Skip loading css in test mode
  if (TEST) {
    // Reference: https://github.com/webpack/null-loader
    // Return an empty module
    cssLoader.loader = 'null'
  }

  // Add cssLoader to the loader list
  config.module.loaders.push(cssLoader);

  /**
  * PostCSS
  * Reference: https://github.com/postcss/autoprefixer-core
  * Add vendor prefixes to your css
  */
  config.postcss = [
    autoprefixer({
      browsers: ['last 2 version']
    })
  ];

  /**
  * Plugins
  * Reference: http://webpack.github.io/docs/configuration.html#plugins
  * List: http://webpack.github.io/docs/list-of-plugins.html
  */
  config.plugins = [
    // Reference: https://github.com/webpack/extract-text-webpack-plugin
    // Extract css files
    // Disabled when in test mode or not in build mode
    new ExtractTextPlugin('[name].[hash].css', {
      disable: !BUILD || TEST
    })
  ];

  // Skip rendering index.html in test mode
  if (!TEST) {
    // Reference: https://github.com/ampedandwired/html-webpack-plugin
    // Render index.html
    config.plugins.push(
      new HtmlWebpackPlugin({
        template: './src/index.html',
        inject: 'body'
      })
    )
  }

  // Add build specific plugins
  if (BUILD) {
    config.plugins.push(
      // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
      // Only emit files when there are no errors
      new webpack.NoErrorsPlugin(),

      // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin
      // Dedupe modules in the output
      new webpack.optimize.DedupePlugin(),

      // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
      // Minify all javascript, switch loaders to minimizing mode
      new webpack.optimize.UglifyJsPlugin()
    )
  }

  // Add dev specific plugins
  if (!TEST && !BUILD) {
    config.plugins.push(
      // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
      // Only emit files when there are no errors
      new BrowserSyncPlugin({
          host: 'localhost',
          port: 3000,
          server: { baseDir: ['public'] }
        })
    )
  }

  /**
  * Dev server configuration
  * Reference: http://webpack.github.io/docs/configuration.html#devserver
  * Reference: http://webpack.github.io/docs/webpack-dev-server.html
  */
  config.devServer = {
    contentBase: './public',
    stats: {
      modules: false,
      cached: false,
      colors: true,
      chunk: false
    }
  };

  return config;
};
hasnatbabur commented 7 years ago

after passing several time I found that, when you select file you need to give extension .css or .js etc. to work browsersync properly, and it worked with reload : false

new BrowserSyncPlugin(
    // BS Options
    {
      files: ['public/css/**/*.css', '**/*.php', '!resources/assets/**/*'],
      proxy: "http://chatapp.dev",
      port: 3000
    },
    // plugin options
    {
        // prevent BrowserSync from reloading the page
        // and let Webpack Dev Server take care of this
        reload: false
    }
  )