# beidou-webpack

webpack dev server for local environment

[中文文档](./README-ZH.md)

## Features

- Serve jsx/js source
- Serve less/scss/css source
- Support hot module replacement(hmr)
- Fast restart immediately support
- Custom config support

## Breaking Changes

- Configuration fields changed since `v1.0.0`.

  We move to `WebpackDevServer` from `webpackDevMiddleware` since `v1.0.0`. Now, configuration is more similar with what we did in normal react project. For more details, see [issue#21](https://github.com/alibaba/beidou/issues/21).

## Configuration

Only available in non production mode, you must build the assets before server online.

- config/plugin.js:

```js
exports.webpack = {
  enable: true,
  package: 'beidou-webpack',
  env: ['local', 'unittest'],
};
```

- config/config.default.js **default config as below**

```js
exports.webpack = {
  custom: {
    // configPath: 'path/to/webpack/config/file',
    // depth: 1,
    // proxy: null,
    cssExtract: true,  // default enable css-mini-plugin config
  },
  output: {
    path: './build',
    filename: '[name].js?[hash]',
    chunkFilename: '[name].js',
    publicPath: './build',
  },

  resolve: {
    extensions: ['.json', '.js', '.jsx'],
  },

  devServer: {
    contentBase: false,
    port: 6002,
    noInfo: true,
    quiet: false,
    clientLogLevel: 'warning',
    lazy: false,
    watchOptions: {
      aggregateTimeout: 300,
    },
    headers: { 'X-Custom-Header': 'yes' },
    stats: {
      colors: true,
      chunks: false,
    },
    publicPath: '/build',
    hot: true,
  },
};
```

Config fields `output`, `resolve`, `devServer` are same as what you know in [webpack](https://webpack.js.org). You can set any valid webpack configs not only those three keys.Changing these fields would take effects in **default webpack config** which generated by beidou-webpack plugin as default. Configs which this plugin self needs must under the `custom` key. e.g. you need to custom webpack configs, you can set `'webpack.custom.configPath'` to your file path.

- **devServer.port** defined the port webpack listen to, you can visit webpack dev server through node server (usually at port 6001) because the plugin provide a proxy to do this automatically. Otherwise, you can directly visit webpack dev server (such as http://localhost:6002/webpack-dev-server) if anything else needed.

- **devServer.contentBase** must be set to `false`, because a static server works if any not `false` value provided, which means webpack responses any request sent to server.

## Custom webpack configuration

**custom.configPath**: defined where your custom webpack config located. An function exported in this file.

```js
// webpack.config.js
// Example 1:
module.exports = (app, defaultConfig, dev, target) => {
  return {
    ...defaultConfig,
    entry: {
      // your entry
    },
    module: {
      // your module
    },
    plugins: [
      // your plugins
    ],
    //something else to override
  };
};

// Example 2:
// Note: The config will cover the default config
module.exports = {
  output: {
    path: './build',
    filename: '[name].js?[hash]',
    chunkFilename: '[name].js',
    publicPath: './build',
  },

  resolve: {
    extensions: ['.json', '.js', '.jsx'],
  },

  devServer: {
    contentBase: false,
    port: 6002,
    noInfo: true,
    quiet: false,
    clientLogLevel: 'warning',
    lazy: false,
    watchOptions: {
      aggregateTimeout: 300,
    },
    headers: { 'X-Custom-Header': 'yes' },
    stats: {
      colors: true,
      chunks: false,
    },
    publicPath: '/build',
    hot: true,
  },
};
```

**custom.depth**: define the depth of entry scanning

**custom.proxy**: define the url match for webpack proxy

e.g.：

```json
{
  "webpack": {
    "custom": {
      "proxy": "/foo*"
    }
  }
}
```

All of the requests start with `/foo` will be redirected to webpack server directly. Useful when enable devServer.proxy.

**custom.cssExtract**: whether enable css-mini-plugin for webpack

#### FAQ:

Custom configurate method by `app.webpackFactory`:

```js

module.exports = (app, defaultConfig, dev, target) => {

  // get the webpack factory
  const factory = app.webpackFactory;

  // set the value of output in webpack :
  factory.set('output',{
      path: outputPath,
      filename: '[name].js?[hash]',
      chunkFilename: '[name].js',
      publicPath: '/build/',
  })

  // if type of the new value is the same, it will emit deep merge by default;
  // string type will overrided;
  // obj:
  factory.set('output',{
      hashDigestLength:10
  })
  // factory.get('output'): {
  //     path: outputPath,
  //     filename: '[name].js?[hash]',
  //     chunkFilename: '[name].js',
  //     publicPath: '/build/',
  //     hashDigestLength: 10
  // }

  // array:
  // suppose the old entry value is ['./src/main.js']
  factory.set('entry',['./src/index.js'])
  // factory.get('entry'): ['./src/main.js','./src/index.js'];

  // want to override the old value? just set the 3rd param true;
  factory.set('entry',['./src/app.js'],true)
  // factory.get('entry'): ['./src/app.js'];

  // modify the output value
  factory.get('output').chunkFilename = '[name].modify.js';


  // add webpack plugin config list
  // add UglifyJsPlugin config into plugin of webpack
  // TODO: fix
  // factory.addPlugin(
  //   webpack.optimize.UglifyJsPlugin,
  //   {
  //     compress: {
  //       warnings: false,
  //     }
  //   },
  //   'UglifyJsPlugin' ,
  // )

  // modify the UglifyJsPlugin config
  // TODO: fix
  // factory.setPlugin(
  //   webpack.optimize.UglifyJsPlugin,
  //   {
  //     compress: {
  //       warnings: true,
  //     }
  //   },
  //   'UglifyJsPlugin'
  // );
  // or another method
  factory.getPlugin('UglifyJsPlugin').options = {
    compress: {
        warnings: true,
      }
  }

  // modify the rule of webpack
  const ruleObj = factory.getRule('.ts');
  ruleObj.options = {
      test: /\.tsx?$/,
      exclude: /node_modules/,
      use: {
        loader:
        app.webpackFactory.useLoader('babel-loader')/** if had the defined loader，use it **/ || 'babel-loader',
        options: {
          babelrc: false,
          presets: ['preset-typescript'],
        },
      },
  }
  // define the loader
  app.webpackFactory.defineLoader({
    'babel-loader',
    require.resolve('babel-loader')
  })

  // generate prod webpack factory
  const factoryInProd = factory.env('prod');
  factoryInProd.addPlugin(new webpack.optimize.UglifyJsPlugin({
    compress: {
      warnings: false,
    },
  }))

  return factory.getConfig(); // return webpack config
  // or return webpack config for prod
  // return factoryInProd.getConfig()

};


```

#### Class Struct

> Class Struct for Plugin

```js
class Plugin {
  object ,      // instance object for webpack plugin
  class,        // class for webpack plugin
  opitons,      // initialize config
  alias
}

```

> Class Struct for Rule

```js
class Rule {
  opitons,      // initialize config for rule
  alias
}

```

> app.webpackFactory methods list:

### reset(value)

#### Parameters

- [value] {Object}

#### return

- this

### set(key,value)

#### Parameters

- key {String}
- value {\*}

#### return

- this

### get(key)

#### Parameters

- key {String}

#### return

- {\*}

### generate {key} factory for webpack: env(key)

#### Parameters

- key {String} factory flag

#### return

- {Object}

### Get the final config for webpack : getConfig()

#### Parameters

#### return

- {Object}

### addPlugin(args, options,alias)

#### Parameters

- args {Object|Class|String}
- [options] {Object}
- [alias] {String}

#### return

- this

### getPlugin(filter)

#### Parameters

- filter {String|Function}

#### return

- {Plugin}

### setPlugin(args, options,alias)

#### Parameter

- args {Object|Class}
- [options] {Object}
- [alias] {String}

#### return

- this

### definePlugin(args, options,alias)

#### Parameters

- args {Object|Class}
- [options] {Object}
- [alias] {String}

#### return

- this

### usePlugin(alias)

#### Parameters

- alias {String}

#### return

- {Plugin}

### addRule(options,alias)

#### Parameters

- options {Object|Rule}
- [alias] {String}

#### return

- this

### setRule(options,alias)

#### Parameters

- options {Object|Rule}
- [alias] {String}

#### return

- this

### getRule(filter)

#### Parameters

- filter {String|Function}

#### return

- {Rule}

### defineRule(options,alias)

#### Parameters

- options {Object}
- [alias] {String}

#### return

- this

### useRule(alias)

#### Parameters

- alias {String}

#### return

- {Rule}

### defineLoader(alias,loader)

#### Parameters

- alias {String}
- [loader] {String}

#### return

- this

### useLoader(alias)

#### Parameters

- alias {String}

#### return

- {String}

* **app**: the `BeidouApplication` instance, usually used to access server config.

- **defaultConfig**: default webpack config generated by beidou-webpack.

- **dev**: `true` in local environment or `false` in production.

- **target**: `browser` as default, defined by `--target` argument when running `beidou build`.

## Entry

Webpack entries in default webpack config is generated through a file scanning in `client` directory.

Scanning behavior becomes much different for different config values of `router.root` and `router.entry` in [beidou-router](../beidou-router/).

- **router.root**: the directory scanning begin at.
- **router.entry**: only files with name match the `router.entry` will be treat as valid entry.

> **Notice**: The Scanning only process at most depth 1, ( only ${router.root}/${router.entry}.jsx and ${router.root}/${dir}/\${router.entry}.jsx will be found)

## Building

**Usage**: beidou-build [--target=browser][--dev]

## Fast restart

Type `rs`, then type `enter`, WebpackDevServer restart immediately.

## License

[MIT](LICENSE)
