{"id":14957610,"url":"https://github.com/leifoolsen/webpack2-boilerplate","last_synced_at":"2025-07-17T04:42:06.819Z","repository":{"id":92047584,"uuid":"62511157","full_name":"leifoolsen/webpack2-boilerplate","owner":"leifoolsen","description":"A Webpack boilerplate project.","archived":false,"fork":false,"pushed_at":"2018-04-07T09:59:07.000Z","size":10640,"stargazers_count":64,"open_issues_count":0,"forks_count":9,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-10-29T23:14:54.376Z","etag":null,"topics":["boilerplate","cucumber","ecmascript2017","es6","express-js","husky","hyperapp","selenium-webdriver","webdriverio","webpack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leifoolsen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-07-03T19:05:35.000Z","updated_at":"2023-08-20T11:24:45.000Z","dependencies_parsed_at":"2024-03-05T08:47:23.205Z","dependency_job_id":null,"html_url":"https://github.com/leifoolsen/webpack2-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leifoolsen%2Fwebpack2-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leifoolsen%2Fwebpack2-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leifoolsen%2Fwebpack2-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leifoolsen%2Fwebpack2-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leifoolsen","download_url":"https://codeload.github.com/leifoolsen/webpack2-boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224305843,"owners_count":17289446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boilerplate","cucumber","ecmascript2017","es6","express-js","husky","hyperapp","selenium-webdriver","webdriverio","webpack"],"created_at":"2024-09-24T13:15:14.231Z","updated_at":"2024-11-12T16:04:48.978Z","avatar_url":"https://github.com/leifoolsen.png","language":"JavaScript","funding_links":[],"categories":["webpack 优秀中文文章"],"sub_categories":[":mortar_board: webpack 入门"],"readme":"# Webpack boilerplate\n\n![webpack-boilerplate](./webpack-boilerplate.png)\n\n## Features\n* ES2015/ES2016/ES2017\n* Npm as a task/build runner\n* [Webpack](https://webpack.js.org/) with tree-shaking and hot module replacement (HMR)\n* Webpack [DLL plugin](https://github.com/webpack/docs/wiki/list-of-plugins#dllplugin) for [faster builds](https://robertknight.github.io/posts/webpack-dll-plugins/)\n* Load polyfills on demand using [dynamic import](https://webpack.js.org/guides/code-splitting-import/#dynamic-import)\n* Lazy loading (System.import) of (large) libraries that is not needed at application startup\n* Node Express middleware\n* Linting with eslint and stylelint\n* Unit tests with Mocha, Chai, Sinon and JSDom\n* Integration tests with Node Express server \n* Acceptance testing with WebdriverIO, Cucumber.js, and Node Express\n* Code coverage and reporting with Istanbul\n* PostCSS/css-next/CSS Modules\n* Self hosting Google Material Icons and Font Roboto\n* CSS reset using [normalize.css](https://github.com/necolas/normalize.css)\n* Example application built with [HyperApp](https://hyperapp.js.org/)\n* [Responsive Font Size And Fluid Typography With vh And vw Units](https://www.smashingmagazine.com/2016/05/fluid-typography/)\n* Framework agnostic. No dependencies to frameworks like React or Angular\n* Uses [husky](https://github.com/typicode/husky) to prevent bad commits\n\n## Get Started\n* Install [Node7, Node8 or Node9](https://nodejs.org/en/), preferably using [nvm](https://github.com/creationix/nvm)\n* Optionally install [Yarn](https://yarnpkg.com/en/)\n* CD to your dev directory, e.g. `cd ~/dev`\n* Clone this repository: `git clone https://github.com/leifoolsen/webpack2-boilerplate.git` (or download zip)\n* CD to project root: `cd webpack2-boilerplate`\n* Install dependencies: `yarn install` or `npm install` \n* Build dll: `npm run build:dll`\n* Start dev server: `npm start`\n* Open a browser at `http://localhost:8084`\n\n## Start coding\n* Modify `package.json`, e.g. `name, author, description, repository` \n* Rename project directory to reflect package name\n* Add your own 3'rd party dependencies\n* Add those 3'rd party dependencies to `./src/vendor.js`, or use System.import() to import 3'rd party dependencies on demand\n* Every time you add or update third party dependenecies, remember to build dll: `npm run build:dll`\n\n\u003e**Note:** Also remember to add your own repo to package.json \n```\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/\u003cyour-git\u003e/\u003cyour-project\u003e.git\"\n  },\n```\n\n* Wipe commit history\n```bash\nrm -rf .git\ngit init\ngit add .\ngit commit -m \"initial commit\"\n```\n* Add your remote: `git remote add origin \u003cssh_or_https_url\u003e`\n* Open a console (shell) and type: `npm start`\n* Open a browser at `http://localhost:8084`\n* Start coding\n\n#### Verify that CSS HMR works\n\n* Modify some CSS code, e.g. in `./src/app/styles/base/color.css`, modify the `background-color` property\n\n```css\n.primary {\n  @mixin primary;\n}\n```\n\n* Change background color to e.g. `red` and save\n\n```css\n.primary {\n  @mixin primary;\n  background-color: red;\n}\n```\n\n* Switch to your browser and click the `Colors` menu\n* The box with the primary color should be red\n* Delete the `background-color` property and switch back to the browser again\n* The box with the primary color should have the initial color\n \n#### Verify that JS HMR works\n\n* Switch to your browser and click the `Home` menu\n* Click the `Ping` button and verify that the response is displayed with a date, e.g.\u003cbr/\u003e`2018-01-05 17:44:24: {\"status\":200, ...\"}` \n* Modify `./src/app/actions/pingServer.js`\n\n```javascript\nimport request from '../../utils/request';\n\nasync function determineTime() {\n  const moment = await import('moment');\n  return moment().format('YYYY-MM-DD HH:mm:ss');\n}\nconst pingServer = (url) =\u003e {\n  return request(url)\n    .then(response =\u003e {\n      return determineTime()\n        .then(time =\u003e {\n          return `${time}: ${JSON.stringify(response)}`;\n        });\n    })\n    .catch(err =\u003e err);\n};\nexport default pingServer;\n```\n\n* Remove date, `YYYY-MM-DD`, from format and save\n\n```javascript\nasync function determineTime() {\n  const moment = await import('moment');\n  return moment().format('HH:mm:ss');\n}\n```\n* Switch to browser and click the `Ping` button again\n* The response should be displayed without a date, e.g.\u003cbr/\u003e`17:47:24: {\"status\":200, ...\"}`  \n\n### Try the bundle\nThe bundled code is minified and optimized code ready for deployment to your \npreferred webserver. Before deployment you can verify that the code behaves as \nexpected by running the following npm commands.      \n\n* `npm run build:prod`\n* `npm run server`\n* Open a browser at `http://localhost:8000`\n\n### Webpack DLL plugin\nThe Dll Plugin lets you pre-build the parts of your code that don't often change (such as third party library code).\nAdd code that should be built by the Dll plugin to `vendor.js`. Code built by the the Dll plugin\ndoes not utilize tree shaking and is therefore only used for development. For a production build,\nthe same `vendor.js` file is added as a entry in your webpack project for minification and elimination of dead code.\n\n* See: [OPTIMIZING WEBPACK FOR FASTER REACT BUILDS](http://engineering.invisionapp.com/post/optimizing-webpack/)\n* See: [Optimizing Webpack build times and improving caching with DLL bundles](https://robertknight.github.io/posts/webpack-dll-plugins/)\n* See: [Webpack Plugins we been keepin on the DLL](https://medium.com/@soederpop/webpack-plugins-been-we-been-keepin-on-the-dll-cdfdd6cb8cd7)\n\n### Lazy loading, System.import()\nIf you have (large) libraries that is not needed at application startup, you can use\nlazy loading. Lazy loading (System.import()) is demonstrated in code above.\n\n* See: [Webpack: Code Splitting - Async](https://webpack.js.org/guides/code-splitting-async/)\n* See: [Lazy Loading](https://webpack.js.org/guides/lazy-loading/)\n\n### Polyfills\nAdd your polyfills to `polyfill.js`\n* See: [Webpack: Code Splitting - Async](https://webpack.js.org/guides/code-splitting-async/)\n* See: [WE DON'T NEED YOUR POLYFILLS!](http://anzorb.com/we-dont-need-your-polyfills/)\n* See: [Polyfills: everything you ever wanted to know, or maybe a bit less](https://hackernoon.com/polyfills-everything-you-ever-wanted-to-know-or-maybe-a-bit-less-7c8de164e423)\n* See: [Conditionally load multiple Polyfills using Webpack, Promises and Code Splitting](http://anujnair.com/blog/13-conditionally-load-multiple-polyfills-using-webpack-promises-and-code-splitting)\n\n## Running tests\nTests are divided into three categories; unit tests, integration tests and \nacceptance tests. Unit tests and integration tests uses Moca as a test runner. \nThe acceptance tests uses WebdriverIO as a test runner. Istanbul is used\nfor code coverage and reporting. \n\n### Unit tests\nThe following libraries are used:\n* Mocha\n* Chai\n* Sinon\n* JsDom headless browser\n* Istanbul\n\nTo run the unit tests type: `npm run test:unit`\n\n### Integration tests\nThe following libraries are used:\n* Mocha\n* Chai\n* Supertest\n* ExpressJS\n* Istanbul\n \nTo run the ingtegration tests type: `npm run test:it`\n \n### Acceptance tests\nThe following libraries are used:\n* WebdriverIO\n* WDIO Selenium standalone service\n* Selenium standalone\n* Cucumber\n* Chai\n* ExpressJS\n\nFor now, the (standalone) acceptance tests must be run manually. The only way to \nro run standalone acceptance tests on a CI server, is to use a headless browser \nlike PhantomJS (I think). Unfortunatley I have so far had no success running \nacceptance tests using PhantomJS.\n\n#### Required steps to run acceptance tests\n```bash\n# yarn, or npm install - just in case\nyarn install / npm install\n\n# Make bundle\nnpm run build:prod\n\n# Fetch actual Seleninum distro\nNODE_TLS_REJECT_UNAUTHORIZED=0 ./node_modules/.bin/selenium-standalone install\n\n# Run acceptance tests\nnpm run wdio\n\n# Expected output\n\n------------------------------------------------------------------\n[chrome #0-0] Session ID: e0bf7b24-2bfa-4053-ad47-1e87d5fe409a\n[chrome #0-0] Spec: ~/dev/webpack2-boilerplate/test/features/example.feature\n[chrome #0-0] Running: chrome\n[chrome #0-0]\n[chrome #0-0] Title check\n[chrome #0-0]\n[chrome #0-0]     Get the title of webpage\n[chrome #0-0]       ✓ I open the url \"http://localhost:8082/\"\n[chrome #0-0]       ✓ I expect the title of the page to be \"Webpack2 Boilerplate\"\n[chrome #0-0]\n[chrome #0-0]     Click the Ping button\n[chrome #0-0]       ✓ I open the url \"http://localhost:8082/\"\n[chrome #0-0]       ✓ I click the Ping button\n[chrome #0-0]       ✓ I expect the response to be \"pong!\"\n[chrome #0-0]\n[chrome #0-0]\n[chrome #0-0] 5 passing (5s)\n\n```\n\n\u003e**Chromedriver**\u003cbr/\u003e\n\u003eAccording to the [WebdriverIO Get Stared guide](http://webdriver.io/guide/getstarted/install.html),\n\u003ethe [Chromedriver standalone server](https://sites.google.com/a/chromium.org/chromedriver/home)\n\u003eis required for running Chrome browser tests on a local machine. On \n\u003elatest Ubuntu and OSX I have run the tests **without installing the Chromedriver**. \n\u003eSo far I have not experienced any problems running the tests without \n\u003ethe Chrome Driver. If you must install Chromedriver, instructions can be found\n\u003ee.g. [here](https://sites.google.com/a/chromium.org/chromedriver/home), \n\u003e[here](https://fossies.org/linux/electron/docs/tutorial/using-selenium-and-webdriver.md)\n\u003eand [here](https://github.com/Matt-B/cucumber-js-selenium-webdriver-example). \n\n### Test coverage\n`npm run build:prod`, then browse `./coverage/lcov-report/index.html`, \n`./coverage/unit/lcov-report/index.html`, `./coverage/integration/lcov-report/index.html`  \n\n### e2e tests\ne2e tests are not implemented in this boilerplate, but basically they are equal \nto the integration tests. The main difference is that you use a proxy to connect\nto a \"real\" api server before running your client api tests. A sample e2e test\nusing a proxy can be found in the\n[./test/integration/proxy](./test/integration/proxy) directory.\n\nTo see it in action, run the `test:proxy-example` script.\n\n## Hot Module Reloading, HMR\nRead [Hot Module Replacement - React](https://webpack.js.org/guides/hmr-react/), \nReact Hot Loader [Getting Started](https://gaearon.github.io/react-hot-loader/getstarted/),\n[Tree-shaking with webpack 2 and Babel 6](http://2ality.com/2015/12/webpack-tree-shaking.html)\nand [http://andrewhfarmer.com/webpack-hmr-tutorial/](http://andrewhfarmer.com/webpack-hmr-tutorial/).\n\n## How to use the boilerplate with React\nThe boilerplate may, with a few modifications, be used with React. \nMore details can be found [here](https://webpack.js.org/guides/hmr-react/) and \n[here](https://gaearon.github.io/react-hot-loader/getstarted/).\n \n### Install required packages\n```bash\n# dependencies\nyarn add -E react / npm i -S react\nyarn add -E react-dom / npm i -S react-dom\n\n# devdependencies\nyarn add -D -E babel-preset-react / npm i -D babel-preset-react \nyarn add -D -E react-hot-loader@next / npm i -D react-hot-loader@next\nyarn add -D -E eslint-plugin-react / npm i -D eslint-plugin-react\n```\n\n### Add React dependencies to `src/vendor.js`\n```javascript\nimport 'react';\nimport 'react-dom';\n```\n\n### Modify `.babelrc`\nAdd \"react\" to presets and \"react-hot-loader/babel\" to development plugins.\n\n```json\n{\n  \"plugins\": [\n    \"syntax-async-functions\",\n    \"syntax-dynamic-import\",\n    \"transform-async-to-generator\",\n    \"transform-regenerator\",\n    \"transform-runtime\",\n    \"transform-class-properties\"\n  ],\n  \"presets\": [\n    [\"env\", {\n      \"targets\": {\n        \"browsers\": [\"last 2 versions\", \"ie \u003e= 11\"]\n      }\n    }],\n    \"react\",\n    \"stage-0\"\n  ],\n  \"env\": {\n    \"development\": {\n      \"plugins\": [\n        \"react-hot-loader/babel\"\n      ]\n    },\n    \"test\": {\n    },\n    \"production\": {\n    }\n  }\n}\n```\n\n### Modify `.eslintrc`\nAdd \"react\" to \"plugins\"\n\n```javascript\n{\n  \"plugins\": [\n    \"compat\",  // Allow configuration of target browser/s (npm i -D eslint-plugin-compat)\n    \"react\"    // React specific linting rules (npm i -D eslint-plugin-react)\n  ],\n}\n```\n\nEnable all of the rules that you would like to use\n\n```javascript\n{\n  \"rules\": {\n    \"react/jsx-uses-react\": \"error\",\n    \"react/jsx-uses-vars\": \"error\",\n  }\n}\n```\n\n### Modify `webpack.config.babel.js`\n\n#### entry.app\nAdd 'react-hot-loader/patch'\n\n```javascript\napp: (!isHot ? [] : [\n  './webpack-public-path.js',\n  // Put react-hot-loader/patch before webpack-hot-middleware,\n  // see: https://github.com/gaearon/react-hot-loader/issues/243\n  'react-hot-loader/patch',\n  'webpack-hot-middleware/client',\n]).concat([\n  './styles.scss',\n  './index.js'\n]),\n```\n\n### Create ./src/components/App.js\n```javascript\nimport React from 'react';\n\nconst superStyles = {\n  backgroundColor: 'green'\n};\n\nconst App = () =\u003e (\n  \u003cdiv style={superStyles}\u003e\n    \u003ch1\u003eHello React!\u003c/h1\u003e\n  \u003c/div\u003e\n);\n\nexport default App;\n```\n\n### Modify ./src/index.js\n```javascript\nimport polyfill from './polyfill';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport './config/config';\nimport logger, {LOG_LEVEL} from './logger/logger';\nimport App from './components/App';\n\nimport './styles.scss';\n\nif(window) {\n  /**\n   * An event handler for the error event.\n   * When an error is thrown, the following arguments are passed to the function:\n   * @param msg The message associated with the error, e.g. “Uncaught ReferenceError: foo is not defined”\n   * @param url The URL of the script or document associated with the error, e.g. “/dist/app.js”\n   * @param lineNo The line number (if available)\n   * @param columnNo The column number (if available)\n   * @param error The Error object associated with this error (if available)\n   * @return {boolean}\n   * @see https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onerror\n   * @see https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror.html\n   */\n  window.onerror = function (msg, url, lineNo, columnNo, error) {\n    const err = error || {};\n    const detail = {\n      name: err.name || msg || '',\n      line: lineNo,\n      column: columnNo,\n      url: url || '',\n      stack: err.stack || 'See browser console for detail',\n    };\n\n    logger.remoteLogger.log(LOG_LEVEL.error, msg, detail);\n    return false;\n  };\n\n  /**\n   * Flush logger\n   */\n  window.addEventListener('beforeunload', () =\u003e {\n    logger.debug('Before unload. Flushing remote logger');\n    logger.remoteLogger.flush();\n  });\n}\n\nconst run = () =\u003e {\n  if (module.hot) {\n    // AppContainer is a necessary wrapper component for HMR\n    const AppContainer = require('react-hot-loader').AppContainer;\n  \n    const render = (Component) =\u003e {\n      ReactDOM.render(\n        \u003cAppContainer\u003e\n          \u003cComponent/\u003e\n        \u003c/AppContainer\u003e,\n        document.getElementById('app')\n      );\n    };\n  \n    render(App);\n  \n    // Hot Module Replacement API\n    module.hot.accept('./components/App', () =\u003e {\n      const NextApp = require('./components/App').default;\n      render(NextApp);\n    });\n  }\n  else {\n    ReactDOM.render(\n      \u003cApp/\u003e,\n      document.getElementById('app')\n    );\n  }\n};\n\n// Add polyfills\ntry {\n  polyfill()\n    .then( () =\u003e run()); // Start the app\n}\ncatch(err) {\n  console.log('Error loading polyfills:', err); // eslint-disable-line no-console\n}\n```\n\n### Modify ./src/index.html, e.g.\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"\u003e\n  \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003e\n  \u003ctitle\u003eReact Webpack\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cmain id=\"app\"\u003e\n\n  \u003c!-- Display a message if JS has been disabled on the browser. --\u003e\n  \u003cnoscript\u003eIf you're seeing this message, that means\n    \u003cstrong\u003eJavaScript has been disabled on your browser\u003c/strong\u003e,\n    please \u003cstrong\u003eenable JS\u003c/strong\u003e to make this app work.\n  \u003c/noscript\u003e\n\n\u003c/main\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Start coding React\nEnjoy your React coding :-)\n\nComplete react-webpack-bolierplate example code can be found \n[here](https://github.com/leifoolsen/my-playground/tree/master/react-webpack2-boilerplate)\n\n\n## NPM Scripts\n* `start`: run Express sever with Hot Module Reloading (HMR), eslint and stylelint, serving files at http://localhost:8084\n* `test`: run unit tests and integration tests\n* `test:watch`: run unit tests in watch mode\n* `test:single`: run a single test file in watch mode, e.g.\u003cbr/\u003e`npm run test:single test/unit/logger/logger.spec.js`\u003cbr/\u003e`npm run test:single test/integration/server/server.spec.js`\n* `test:pattern`: will run tests and suites with names matching the given pattern, e.g.\u003cbr/\u003e`pattern=logger npm run test:pattern` will run only the `logger` tests\n* `lint`: lint according to rules in `.eslintrc` and `.stylelintrc`\n* `analyze`: run webpack-bundle-size-analyzer to analyze the output bundle sizes\u003cbr/\u003e**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\n* `clean`: remove dist and coverage directory\n* `build`: bundle the app to the dist dir using development settings\n* `build:prod`: bundle the app to the dist dir using production settings\n* `server`: run Express sever with the generated bundle, serving files at http://localhost:8000\n* `precommit`: husky run command for the git pre-commit hook\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleifoolsen%2Fwebpack2-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleifoolsen%2Fwebpack2-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleifoolsen%2Fwebpack2-boilerplate/lists"}