项目作者: leifoolsen

项目描述 :
A Webpack boilerplate project.
高级语言: JavaScript
项目地址: git://github.com/leifoolsen/webpack2-boilerplate.git
创建时间: 2016-07-03T19:05:35Z
项目社区:https://github.com/leifoolsen/webpack2-boilerplate

开源协议:MIT License

下载


Webpack boilerplate

webpack-boilerplate

Features

  • ES2015/ES2016/ES2017
  • Npm as a task/build runner
  • Webpack with tree-shaking and hot module replacement (HMR)
  • Webpack DLL plugin for faster builds
  • Load polyfills on demand using dynamic import
  • Lazy loading (System.import) of (large) libraries that is not needed at application startup
  • Node Express middleware
  • Linting with eslint and stylelint
  • Unit tests with Mocha, Chai, Sinon and JSDom
  • Integration tests with Node Express server
  • Acceptance testing with WebdriverIO, Cucumber.js, and Node Express
  • Code coverage and reporting with Istanbul
  • PostCSS/css-next/CSS Modules
  • Self hosting Google Material Icons and Font Roboto
  • CSS reset using normalize.css
  • Example application built with HyperApp
  • Responsive Font Size And Fluid Typography With vh And vw Units
  • Framework agnostic. No dependencies to frameworks like React or Angular
  • Uses husky to prevent bad commits

Get Started

  • Install Node7, Node8 or Node9, preferably using nvm
  • Optionally install Yarn
  • CD to your dev directory, e.g. cd ~/dev
  • Clone this repository: git clone https://github.com/leifoolsen/webpack2-boilerplate.git (or download zip)
  • CD to project root: cd webpack2-boilerplate
  • Install dependencies: yarn install or npm install
  • Build dll: npm run build:dll
  • Start dev server: npm start
  • Open a browser at http://localhost:8084

Start coding

  • Modify package.json, e.g. name, author, description, repository
  • Rename project directory to reflect package name
  • Add your own 3’rd party dependencies
  • Add those 3’rd party dependencies to ./src/vendor.js, or use System.import() to import 3’rd party dependencies on demand
  • Every time you add or update third party dependenecies, remember to build dll: npm run build:dll

Note: Also remember to add your own repo to package.json

  1. "repository": {
  2. "type": "git",
  3. "url": "https://github.com/<your-git>/<your-project>.git"
  4. },
  • Wipe commit history
    1. rm -rf .git
    2. git init
    3. git add .
    4. git commit -m "initial commit"
  • Add your remote: git remote add origin <ssh_or_https_url>
  • Open a console (shell) and type: npm start
  • Open a browser at http://localhost:8084
  • Start coding

Verify that CSS HMR works

  • Modify some CSS code, e.g. in ./src/app/styles/base/color.css, modify the background-color property
  1. .primary {
  2. @mixin primary;
  3. }
  • Change background color to e.g. red and save
  1. .primary {
  2. @mixin primary;
  3. background-color: red;
  4. }
  • Switch to your browser and click the Colors menu
  • The box with the primary color should be red
  • Delete the background-color property and switch back to the browser again
  • The box with the primary color should have the initial color

Verify that JS HMR works

  • Switch to your browser and click the Home menu
  • Click the Ping button and verify that the response is displayed with a date, e.g.
    2018-01-05 17:44:24: {"status":200, ..."}
  • Modify ./src/app/actions/pingServer.js
  1. import request from '../../utils/request';
  2. async function determineTime() {
  3. const moment = await import('moment');
  4. return moment().format('YYYY-MM-DD HH:mm:ss');
  5. }
  6. const pingServer = (url) => {
  7. return request(url)
  8. .then(response => {
  9. return determineTime()
  10. .then(time => {
  11. return `${time}: ${JSON.stringify(response)}`;
  12. });
  13. })
  14. .catch(err => err);
  15. };
  16. export default pingServer;
  • Remove date, YYYY-MM-DD, from format and save
  1. async function determineTime() {
  2. const moment = await import('moment');
  3. return moment().format('HH:mm:ss');
  4. }
  • Switch to browser and click the Ping button again
  • The response should be displayed without a date, e.g.
    17:47:24: {"status":200, ..."}

Try the bundle

The bundled code is minified and optimized code ready for deployment to your
preferred webserver. Before deployment you can verify that the code behaves as
expected by running the following npm commands.

  • npm run build:prod
  • npm run server
  • Open a browser at http://localhost:8000

Webpack DLL plugin

The Dll Plugin lets you pre-build the parts of your code that don’t often change (such as third party library code).
Add code that should be built by the Dll plugin to vendor.js. Code built by the the Dll plugin
does not utilize tree shaking and is therefore only used for development. For a production build,
the same vendor.js file is added as a entry in your webpack project for minification and elimination of dead code.

Lazy loading, System.import()

If you have (large) libraries that is not needed at application startup, you can use
lazy loading. Lazy loading (System.import()) is demonstrated in code above.

Polyfills

Add your polyfills to polyfill.js

Running tests

Tests are divided into three categories; unit tests, integration tests and
acceptance tests. Unit tests and integration tests uses Moca as a test runner.
The acceptance tests uses WebdriverIO as a test runner. Istanbul is used
for code coverage and reporting.

Unit tests

The following libraries are used:

  • Mocha
  • Chai
  • Sinon
  • JsDom headless browser
  • Istanbul

To run the unit tests type: npm run test:unit

Integration tests

The following libraries are used:

  • Mocha
  • Chai
  • Supertest
  • ExpressJS
  • Istanbul

To run the ingtegration tests type: npm run test:it

Acceptance tests

The following libraries are used:

  • WebdriverIO
  • WDIO Selenium standalone service
  • Selenium standalone
  • Cucumber
  • Chai
  • ExpressJS

For now, the (standalone) acceptance tests must be run manually. The only way to
ro run standalone acceptance tests on a CI server, is to use a headless browser
like PhantomJS (I think). Unfortunatley I have so far had no success running
acceptance tests using PhantomJS.

Required steps to run acceptance tests

  1. # yarn, or npm install - just in case
  2. yarn install / npm install
  3. # Make bundle
  4. npm run build:prod
  5. # Fetch actual Seleninum distro
  6. NODE_TLS_REJECT_UNAUTHORIZED=0 ./node_modules/.bin/selenium-standalone install
  7. # Run acceptance tests
  8. npm run wdio
  9. # Expected output
  10. ------------------------------------------------------------------
  11. [chrome #0-0] Session ID: e0bf7b24-2bfa-4053-ad47-1e87d5fe409a
  12. [chrome #0-0] Spec: ~/dev/webpack2-boilerplate/test/features/example.feature
  13. [chrome #0-0] Running: chrome
  14. [chrome #0-0]
  15. [chrome #0-0] Title check
  16. [chrome #0-0]
  17. [chrome #0-0] Get the title of webpage
  18. [chrome #0-0] ✓ I open the url "http://localhost:8082/"
  19. [chrome #0-0] ✓ I expect the title of the page to be "Webpack2 Boilerplate"
  20. [chrome #0-0]
  21. [chrome #0-0] Click the Ping button
  22. [chrome #0-0] ✓ I open the url "http://localhost:8082/"
  23. [chrome #0-0] ✓ I click the Ping button
  24. [chrome #0-0] ✓ I expect the response to be "pong!"
  25. [chrome #0-0]
  26. [chrome #0-0]
  27. [chrome #0-0] 5 passing (5s)

Chromedriver

According to the WebdriverIO Get Stared guide,
the Chromedriver standalone server
is required for running Chrome browser tests on a local machine. On
latest Ubuntu and OSX I have run the tests without installing the Chromedriver.
So far I have not experienced any problems running the tests without
the Chrome Driver. If you must install Chromedriver, instructions can be found
e.g. here,
here
and here.

Test coverage

npm run build:prod, then browse ./coverage/lcov-report/index.html,
./coverage/unit/lcov-report/index.html, ./coverage/integration/lcov-report/index.html

e2e tests

e2e tests are not implemented in this boilerplate, but basically they are equal
to the integration tests. The main difference is that you use a proxy to connect
to a “real” api server before running your client api tests. A sample e2e test
using a proxy can be found in the
./test/integration/proxy directory.

To see it in action, run the test:proxy-example script.

Hot Module Reloading, HMR

Read Hot Module Replacement - React,
React Hot Loader Getting Started,
Tree-shaking with webpack 2 and Babel 6
and http://andrewhfarmer.com/webpack-hmr-tutorial/.

How to use the boilerplate with React

The boilerplate may, with a few modifications, be used with React.
More details can be found here and
here.

Install required packages

  1. # dependencies
  2. yarn add -E react / npm i -S react
  3. yarn add -E react-dom / npm i -S react-dom
  4. # devdependencies
  5. yarn add -D -E babel-preset-react / npm i -D babel-preset-react
  6. yarn add -D -E react-hot-loader@next / npm i -D react-hot-loader@next
  7. yarn add -D -E eslint-plugin-react / npm i -D eslint-plugin-react

Add React dependencies to src/vendor.js

  1. import 'react';
  2. import 'react-dom';

Modify .babelrc

Add “react” to presets and “react-hot-loader/babel” to development plugins.

  1. {
  2. "plugins": [
  3. "syntax-async-functions",
  4. "syntax-dynamic-import",
  5. "transform-async-to-generator",
  6. "transform-regenerator",
  7. "transform-runtime",
  8. "transform-class-properties"
  9. ],
  10. "presets": [
  11. ["env", {
  12. "targets": {
  13. "browsers": ["last 2 versions", "ie >= 11"]
  14. }
  15. }],
  16. "react",
  17. "stage-0"
  18. ],
  19. "env": {
  20. "development": {
  21. "plugins": [
  22. "react-hot-loader/babel"
  23. ]
  24. },
  25. "test": {
  26. },
  27. "production": {
  28. }
  29. }
  30. }

Modify .eslintrc

Add “react” to “plugins”

  1. {
  2. "plugins": [
  3. "compat", // Allow configuration of target browser/s (npm i -D eslint-plugin-compat)
  4. "react" // React specific linting rules (npm i -D eslint-plugin-react)
  5. ],
  6. }

Enable all of the rules that you would like to use

  1. {
  2. "rules": {
  3. "react/jsx-uses-react": "error",
  4. "react/jsx-uses-vars": "error",
  5. }
  6. }

Modify webpack.config.babel.js

entry.app

Add ‘react-hot-loader/patch’

  1. app: (!isHot ? [] : [
  2. './webpack-public-path.js',
  3. // Put react-hot-loader/patch before webpack-hot-middleware,
  4. // see: https://github.com/gaearon/react-hot-loader/issues/243
  5. 'react-hot-loader/patch',
  6. 'webpack-hot-middleware/client',
  7. ]).concat([
  8. './styles.scss',
  9. './index.js'
  10. ]),

Create ./src/components/App.js

  1. import React from 'react';
  2. const superStyles = {
  3. backgroundColor: 'green'
  4. };
  5. const App = () => (
  6. <div style={superStyles}>
  7. <h1>Hello React!</h1>
  8. </div>
  9. );
  10. export default App;

Modify ./src/index.js

  1. import polyfill from './polyfill';
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. import './config/config';
  5. import logger, {LOG_LEVEL} from './logger/logger';
  6. import App from './components/App';
  7. import './styles.scss';
  8. if(window) {
  9. /**
  10. * An event handler for the error event.
  11. * When an error is thrown, the following arguments are passed to the function:
  12. * @param msg The message associated with the error, e.g. “Uncaught ReferenceError: foo is not defined”
  13. * @param url The URL of the script or document associated with the error, e.g. “/dist/app.js”
  14. * @param lineNo The line number (if available)
  15. * @param columnNo The column number (if available)
  16. * @param error The Error object associated with this error (if available)
  17. * @return {boolean}
  18. * @see https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onerror
  19. * @see https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror.html
  20. */
  21. window.onerror = function (msg, url, lineNo, columnNo, error) {
  22. const err = error || {};
  23. const detail = {
  24. name: err.name || msg || '',
  25. line: lineNo,
  26. column: columnNo,
  27. url: url || '',
  28. stack: err.stack || 'See browser console for detail',
  29. };
  30. logger.remoteLogger.log(LOG_LEVEL.error, msg, detail);
  31. return false;
  32. };
  33. /**
  34. * Flush logger
  35. */
  36. window.addEventListener('beforeunload', () => {
  37. logger.debug('Before unload. Flushing remote logger');
  38. logger.remoteLogger.flush();
  39. });
  40. }
  41. const run = () => {
  42. if (module.hot) {
  43. // AppContainer is a necessary wrapper component for HMR
  44. const AppContainer = require('react-hot-loader').AppContainer;
  45. const render = (Component) => {
  46. ReactDOM.render(
  47. <AppContainer>
  48. <Component></Component>
  49. </AppContainer>,
  50. document.getElementById('app')
  51. );
  52. };
  53. render(App);
  54. // Hot Module Replacement API
  55. module.hot.accept('./components/App', () => {
  56. const NextApp = require('./components/App').default;
  57. render(NextApp);
  58. });
  59. }
  60. else {
  61. ReactDOM.render(
  62. <App></App>,
  63. document.getElementById('app')
  64. );
  65. }
  66. };
  67. // Add polyfills
  68. try {
  69. polyfill()
  70. .then( () => run()); // Start the app
  71. }
  72. catch(err) {
  73. console.log('Error loading polyfills:', err); // eslint-disable-line no-console
  74. }

Modify ./src/index.html, e.g.

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>React Webpack</title>
  8. </head>
  9. <body>
  10. <main id="app">
  11. <!-- Display a message if JS has been disabled on the browser. -->
  12. <noscript>If you're seeing this message, that means
  13. <strong>JavaScript has been disabled on your browser</strong>,
  14. please <strong>enable JS</strong> to make this app work.
  15. </noscript>
  16. </main>
  17. </body>
  18. </html>

Start coding React

Enjoy your React coding :-)

Complete react-webpack-bolierplate example code can be found
here

NPM Scripts

  • start: run Express sever with Hot Module Reloading (HMR), eslint and stylelint, serving files at http://localhost:8084
  • test: run unit tests and integration tests
  • test:watch: run unit tests in watch mode
  • test:single: run a single test file in watch mode, e.g.
    npm run test:single test/unit/logger/logger.spec.js
    npm run test:single test/integration/server/server.spec.js
  • test:pattern: will run tests and suites with names matching the given pattern, e.g.
    pattern=logger npm run test:pattern will run only the logger tests
  • lint: lint according to rules in .eslintrc and .stylelintrc
  • analyze: run webpack-bundle-size-analyzer to analyze the output bundle sizes
    Note: There is a console.log statement at the top of the webpack.config file that must be removed before this script can be run
  • clean: remove dist and coverage directory
  • build: bundle the app to the dist dir using development settings
  • build:prod: bundle the app to the dist dir using production settings
  • server: run Express sever with the generated bundle, serving files at http://localhost:8000
  • precommit: husky run command for the git pre-commit hook