From b36dc3b5c20dc4c0784d190dafaac946c69fb89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Cevallos?= Date: Sat, 13 Jan 2018 18:51:57 -0300 Subject: [PATCH] Merge from vuejs-templates/webpack#develop preserving PWA configuration --- .gitignore | 1 + README.md | 19 +- docs/README.md | 2 +- docs/backend.md | 27 +- docs/commands.md | 9 +- docs/e2e.md | 2 +- docs/env.md | 2 +- docs/linter.md | 24 +- docs/pre-processors.md | 18 +- docs/proxy.md | 2 +- docs/static.md | 8 +- docs/structure.md | 18 +- docs/unit.md | 30 ++- meta.js | 230 ++++++++++++------ template/.babelrc | 9 +- template/.eslintrc.js | 54 ++-- template/.gitignore | 5 +- template/.postcssrc.js | 4 +- template/README.md | 2 +- template/build/build.js | 10 +- template/build/check-versions.js | 8 +- template/build/dev-client.js | 11 - template/build/dev-server.js | 93 ------- template/build/utils.js | 34 ++- template/build/vue-loader.conf.js | 12 +- template/build/webpack.base.conf.js | 39 ++- template/build/webpack.dev.conf.js | 86 +++++-- template/build/webpack.prod.conf.js | 42 +++- template/build/webpack.test.conf.js | 5 +- template/config/dev.env.js | 1 - template/config/index.js | 69 ++++-- template/config/prod.env.js | 1 + template/config/test.env.js | 1 - template/package.json | 155 ++++++------ template/src/App.vue | 10 +- template/src/components/Hello.vue | 105 ++++++-- template/src/main.js | 16 +- template/src/router/index.js | 16 +- .../e2e/custom-assertions/elementCount.js | 23 +- template/test/e2e/runner.js | 47 ++-- template/test/e2e/specs/test.js | 8 +- template/test/unit/.eslintrc | 9 +- template/test/unit/index.js | 12 +- template/test/unit/karma.conf.js | 12 +- template/test/unit/specs/Hello.spec.js | 14 +- utils/index.js | 146 +++++++++++ 46 files changed, 972 insertions(+), 479 deletions(-) delete mode 100644 template/build/dev-client.js delete mode 100644 template/build/dev-server.js create mode 100644 utils/index.js diff --git a/.gitignore b/.gitignore index 80c45fb..38e1975 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .DS_Store docs/_book +test/ diff --git a/README.md b/README.md index 0f4227a..5b7302a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,19 @@ # vue-pwa-boilerplate -> A full-featured [PWA](https://developers.google.com/web/progressive-web-apps/) template with webpack, hot-reload, lint-on-save, unit testing & css extraction. +> A full-featured [PWA](https://developers.google.com/web/progressive-web-apps/) Webpack setup with hot-reload, lint-on-save, unit testing & css extraction. + +> This template is Vue 2.0 compatible. For Vue 1.x use this command: `vue init webpack#1.0 my-project` ## Documentation - This template builds on top of the main webpack template, so please refer to the [webpack template docs](http://vuejs-templates.github.io/webpack). - -- Check out the [official Vue.js guide](http://vuejs.org/guide/) for general information about Vue that is not specific to this template. +- [For Vue 2.0](http://vuejs.org/guide/): general information about how to work with Vue, not specific to this template ## Usage -This is a project template for [vue-cli](https://github.com/vuejs/vue-cli). **It is recommended to use npm 3+ or [yarn](https://yarnpkg.com) for a more efficient dependency tree.** +This is a project template for [vue-cli](https://github.com/vuejs/vue-cli). **It is recommended to use npm 3+ for a more efficient dependency tree.** -```bash +``` bash $ npm install -g vue-cli $ vue init pwa my-project $ cd my-project @@ -57,14 +58,14 @@ See ["Configuring your Web server to prevent caching"](docs/prevent_caching.md) - JavaScript minified with [UglifyJS v3](https://github.com/mishoo/UglifyJS2/tree/harmony). - HTML minified with [html-minifier](https://github.com/kangax/html-minifier). - CSS across all components extracted into a single file and minified with [cssnano](https://github.com/ben-eb/cssnano). - - All static assets compiled with version hashes for efficient long-term caching, and a production `index.html` is auto-generated with proper URLs to these generated assets. + - Static assets compiled with version hashes for efficient long-term caching, and an auto-generated production `index.html` with proper URLs to these generated assets. - Use `npm run build --report`to build with bundle size analytics. - Generates a Service Worker for offline caching your static assets using [sw-precache-webpack-plugin](https://www.npmjs.com/package/sw-precache-webpack-plugin) -- `npm run unit`: Unit tests run in PhantomJS with [Karma](http://karma-runner.github.io/0.13/index.html) + [Mocha](http://mochajs.org/) + [karma-webpack](https://github.com/webpack/karma-webpack). +- `npm run unit`: Unit tests run in [JSDOM](https://github.com/tmpvar/jsdom) with [Jest](https://facebook.github.io/jest/), or in PhantomJS with Karma + Mocha + karma-webpack. - Supports ES2015+ in test files. - Supports all webpack loaders. - - Easy mock injection. + - Easy mocking. - `npm run e2e`: End-to-end tests with [Nightwatch](http://nightwatchjs.org/). - Run tests in multiple browsers in parallel. @@ -76,7 +77,7 @@ See ["Configuring your Web server to prevent caching"](docs/prevent_caching.md) You can fork this repo to create your own boilerplate, and use it with `vue-cli`: -```bash +``` bash vue init username/repo my-project ``` diff --git a/docs/README.md b/docs/README.md index 6c350b1..36fdb2b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Introduction -This [Progressive Web Apps](https://developers.google.com/web/progressive-web-apps/) boilerplate is targeted towards large, serious projects and assumes you are somewhat familiar with Webpack and `vue-loader`. Make sure to also read [`vue-loader`'s documentation](http://vuejs.github.io/vue-loader/index.html) for common workflow recipes. +This [Progressive Web Apps](https://developers.google.com/web/progressive-web-apps/) boilerplate is targeted towards large, serious projects and assumes you are somewhat familiar with Webpack and `vue-loader`. Make sure to also read [`vue-loader`'s documentation](https://vue-loader.vuejs.org/) for common workflow recipes. If you just want to try out `vue-loader` or whip out a quick prototype, use the [webpack-simple](https://github.com/vuejs-templates/webpack-simple) template instead. diff --git a/docs/backend.md b/docs/backend.md index ef07286..af426f5 100644 --- a/docs/backend.md +++ b/docs/backend.md @@ -5,18 +5,37 @@ If you are building a purely-static app (one that is deployed separately from th Let's take a look at the default `config/index.js`: ``` js +// config/index.js 'use strict' - const path = require('path') module.exports = { + dev: { + / Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', + port: 8080, + + // skipping other options as they are only convenience features + }, build: { - index: path.resolve(__dirname, 'dist/index.html'), - assetsRoot: path.resolve(__dirname, 'dist'), + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', - productionSourceMap: true + + productionSourceMap: true, + + // skipping the rest ... }, +//TODO: the rest of the content is not on webpack template. is it ok? dev: { port: 8080, proxyTable: {} diff --git a/docs/commands.md b/docs/commands.md index c811e33..175903c 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -24,11 +24,12 @@ All build commands are executed via [NPM Scripts](https://docs.npmjs.com/misc/sc ### `npm run unit` -> Run unit tests in PhantomJS with [Karma](https://karma-runner.github.io/). See [Unit Testing](unit.md) for more details. +> Run unit tests in JSDOM with [Jest](https://facebook.github.io/jest/docs/getting-started.html). See [Unit Testing](unit.md) for more details. - Supports ES2015+ in test files. +TODO: following line is not present on webpack template. is it ok? - Supports all webpack loaders. -- Easy [mock injection](http://vuejs.github.io/vue-loader/en/workflow/testing-with-mocks.html). +- Easy [mocking](https://facebook.github.io/jest/docs/mock-functions.html). ### `npm run e2e` @@ -38,3 +39,7 @@ All build commands are executed via [NPM Scripts](https://docs.npmjs.com/misc/sc - Works with one command out of the box: - Selenium and chromedriver dependencies automatically handled. - Automatically spawns the Selenium server. + +### `npm run lint` + +> Runs eslint and reports any linting errors in your code. See [Linter Configuration](linter.md) diff --git a/docs/e2e.md b/docs/e2e.md index 1525fa4..2c2f173 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -10,7 +10,7 @@ Let's take a look at the files in the `test/e2e` directory: - `nightwatch.conf.js` - Nightwatch configuration file. See [Nightwatch's docs on configuration](http://nightwatchjs.org/guide#settings-file) for more details. + Nightwatch configuration file. See [Nightwatch's docs on configuration](http://nightwatchjs.org/gettingstarted#settings-file) for more details. - `custom-assertions/` diff --git a/docs/env.md b/docs/env.md index 211ca5d..c5aa697 100644 --- a/docs/env.md +++ b/docs/env.md @@ -47,5 +47,5 @@ As we can see, `test.env` inherits the `dev.env` and the `dev.env` inherits the It is simple to use the environment variables in your code. For example: ```js -Vue.config.debug = process.env.DEBUG_MODE +Vue.config.productionTip = process.env.NODE_ENV === 'production' ``` \ No newline at end of file diff --git a/docs/linter.md b/docs/linter.md index 0bc3be1..478c1a1 100644 --- a/docs/linter.md +++ b/docs/linter.md @@ -1,15 +1,35 @@ # Linter Configuration -This boilerplate uses [ESLint](http://eslint.org/) as the linter, and uses the [Standard](https://github.com/feross/standard/blob/master/RULES.md) preset with some small customizations. +This boilerplate uses [ESLint](https://eslint.org/) as the linter, and uses the [Standard](https://github.com/feross/standard/blob/master/RULES.md) preset with some small customizations. + +## eslint-plugin-vue + +We always add [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) as well, which comes with a whole bunch of helpful rules to write consistent Vue components - it can also lint templates! + +You can find an overview of all the available rules on [github](https://github.com/vuejs/eslint-plugin-vue#gear-configs). We chose to add the `essential` configs, but we recommend to switch to the bigger `strongly-recommended` or `recommended` rulesets once you are familiar with them. + +## Customizing If you are not happy with the default linting rules, you have several options: 1. Overwrite individual rules in `.eslintrc.js`. For example, you can add the following rule to enforce semicolons instead of omitting them: ``` js + // .eslintrc.js "semi": [2, "always"] ``` 2. Pick a different ESLint preset when generating the project, for example [eslint-config-airbnb](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb). -3. Pick "none" for ESLint preset when generating the project and define your own rules. See [ESLint documentation](http://eslint.org/docs/rules/) for more details. +3. Pick "none" for ESLint preset when generating the project and define your own rules. See [ESLint documentation](https://eslint.org/docs/rules/) for more details. + +## Fixing Linting Errors + +You can run the following command to let eslint fix any errors it finds (if it can - not all errors are fixable like this): + +``` +npm run lint -- --fix +``` + +*(The `--` in the middle is necessary to ensure the `--fix` option is passdd to `eslint`, not to `npm`. It can be omitted whne using yarn)* + diff --git a/docs/pre-processors.md b/docs/pre-processors.md index 957e41c..fa58751 100644 --- a/docs/pre-processors.md +++ b/docs/pre-processors.md @@ -1,6 +1,6 @@ # Pre-Processors -This boilerplate has pre-configured CSS extraction for most popular CSS pre-processors including LESS, SASS, Stylus, and PostCSS. To use a pre-processor, all you need to do is installing the appropriate webpack loader for it. For example, to use SASS: +This boilerplate has pre-configured CSS extraction for most popular CSS pre-processors including LESS, SASS, Stylus, and PostCSS. To use a pre-processor, all you need to do is install the appropriate webpack loader for it. For example, to use SASS: ``` bash npm install sass-loader node-sass --save-dev @@ -20,19 +20,23 @@ Once installed, you can use the pre-processors inside your `*.vue` components us ### A note on SASS syntax -- `lang="scss"` corresponds to the CSS-superset syntax (with curly braces and semicolones). +- `lang="scss"` corresponds to the CSS-superset syntax (with curly braces and semicolons). - `lang="sass"` corresponds to the indentation-based syntax. ### PostCSS -Styles in `*.vue` files are piped through PostCSS by default, so you don't need to use a specific loader for it. You can simply add PostCSS plugins you want to use in `build/webpack.base.conf.js` under the `vue` block: +Styles in `*.vue` files and style files (`*.css`, `*.scss` etc) are piped through PostCSS by default, so you don't need to use a specific loader for it. + +You can simply add PostCSS plugins you want to use to the `.postcssrc.js`file in your project's root directory: ``` js -// build/webpack.base.conf.js +// https://github.com/michael-ciniawsky/postcss-load-config + module.exports = { - // ... - vue: { - postcss: [/* your plugins */] + "plugins": { + // to edit target browsers: use "browserslist" field in package.json + "postcss-import": {}, + "autoprefixer": {} } } ``` diff --git a/docs/proxy.md b/docs/proxy.md index fc822ea..60b5151 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -31,7 +31,7 @@ In addition to static urls you can also use glob patterns to match URLs, e.g. `/ ``` js proxyTable: { - '*': { + '**': { target: 'http://jsonplaceholder.typicode.com', filter: function (pathname, req) { return pathname.match('^/api') && req.method === 'GET' diff --git a/docs/static.md b/docs/static.md index f9f9a3c..9987ec8 100644 --- a/docs/static.md +++ b/docs/static.md @@ -1,4 +1,4 @@ -# Handing Static Assets +# Handling Static Assets You will notice in the project structure we have two directories for static assets: `src/assets` and `static/`. What is the difference between them? @@ -6,9 +6,9 @@ You will notice in the project structure we have two directories for static asse To answer this question, we first need to understand how Webpack deals with static assets. In `*.vue` components, all your templates and CSS are parsed by `vue-html-loader` and `css-loader` to look for asset URLs. For example, in `` and `background: url(./logo.png)`, `"./logo.png"` is a relative asset path and will be **resolved by Webpack as a module dependency**. -Because `logo.png` is not JavaScript, when treated as a module dependency, we need to use `url-loader` and `file-loader` to process it. This boilerplate has already configured these loaders for you, so you basically get features such as filename fingerprinting and conditional base64 inlining for free, while being able to use relative/module paths without worrying about deployment. +Because `logo.png` is not JavaScript, when treated as a module dependency, we need to use `url-loader` and `file-loader` to process it. This template has already configured these loaders for you, so you get features such as filename fingerprinting and conditional base64 inlining for free, while being able to use relative/module paths without worrying about deployment. -Since these assets may be inlined/copied/renamed during build, they are essentially part of your source code. This is why it is recommended to place Webpack-processed static assets inside `/src`, along side other source files. In fact, you don't even have to put them all in `/src/assets`: you can organize them based on the module/component using them. For example, you can put each component in its own directory, with its static assets right next to it. +Since these assets may be inlined/copied/renamed during build, they are essentially part of your source code. This is why it is recommended to place Webpack-processed static assets inside `/src`, alongside other source files. In fact, you don't even have to put them all in `/src/assets`: you can organize them based on the module/component using them. For example, you can put each component in its own directory, with its static assets right next to it. ### Asset Resolving Rules @@ -41,7 +41,7 @@ In comparison, files in `static/` are not processed by Webpack at all: they are As an example, with the following default values: ``` js -// config.js +// config/index.js module.exports = { // ... build: { diff --git a/docs/structure.md b/docs/structure.md index 8daf543..75c958a 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -17,20 +17,26 @@ ├── static/ # pure static assets (directly copied) ├── test/ │ └── unit/ # unit tests -│ │   ├── specs/ # test spec files -│ │   ├── index.js # test build entry file -│ │   └── karma.conf.js # test runner config file +│ │ ├── specs/ # test spec files +│ │ ├── eslintrc # config file for eslint with extra settings only for unit tests +│ │ ├── index.js # test build entry file +│ │ ├── jest.conf.js # Config file when using Jest for unit tests +│ │ └── karma.conf.js # test runner config file when using Karma for unit tests +│ │ ├── setup.js # file that runs before Jest runs your unit tests │ └── e2e/ # e2e tests │ │   ├── specs/ # test spec files │ │   ├── custom-assertions/ # custom assertions for e2e tests │ │   ├── runner.js # test runner script │ │   └── nightwatch.conf.js # test runner config file ├── .babelrc # babel config -├── .postcssrc.js # postcss config +├── .editorconfig # indentation, spaces/tabs and similar settings for your editor ├── .eslintrc.js # eslint config -├── .editorconfig # editor config +├── .eslintignore # eslint ignore rules +├── .gitignore # sensible defaults for gitignore +├── .postcssrc.js # postcss config ├── index.html # index.html template -└── package.json # build scripts and dependencies +├── package.json # build scripts and dependencies +└── README.md # Default README file ``` ### `build/` diff --git a/docs/unit.md b/docs/unit.md index faeb527..c14c46b 100644 --- a/docs/unit.md +++ b/docs/unit.md @@ -1,6 +1,26 @@ # Unit Testing -An overview of the tools used by this boilerplate for unit testing: +This project offers two options for unit testing: + +1. Jest +2. Karma and Mocha. + + +## Jest + +- [Jest](https://facebook.github.io/jest/): the test runner that launches JSDOM runs the tests and reports the results to us. + +### Files + +- `setup.js` + + Jest runs this file before it runs the unit tests. It sets the Vue production tip to false. + +### Mocking Dependencies + +The Jest boilerplate comes with the ability to mock dependencies. See the [mock functions guide](https://facebook.github.io/jest/docs/mock-functions.html) for more details. + +## Karma and Mocha - [Karma](https://karma-runner.github.io/): the test runner that launches browsers, runs the tests and reports the results to us. - [karma-webpack](https://github.com/webpack/karma-webpack): the plugin for Karma that bundles our tests using Webpack. @@ -10,7 +30,7 @@ An overview of the tools used by this boilerplate for unit testing: Chai and Sinon are integrated using [karma-sinon-chai](https://github.com/kmees/karma-sinon-chai), so all Chai interfaces (`should`, `expect`, `assert`) and `sinon` are globally available in test files. -And the files: +### Files - `index.js` @@ -24,10 +44,10 @@ And the files: This is the Karma configuration file. See [Karma docs](https://karma-runner.github.io/) for more details. -## Running Tests in More Browsers +### Running Tests in More Browsers You can run the tests in multiple real browsers by installing more [karma launchers](https://karma-runner.github.io/1.0/config/browsers.html) and adjusting the `browsers` field in `test/unit/karma.conf.js`. -## Mocking Dependencies +### Mocking Dependencies -This boilerplate comes with [inject-loader](https://github.com/plasticine/inject-loader) installed by default. For usage with `*.vue` components, see [vue-loader docs on testing with mocks](http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html). +The Karma unit test boilerplate comes with [inject-loader](https://github.com/plasticine/inject-loader) installed by default. For usage with `*.vue` components, see [vue-loader docs on testing with mocks](http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html). diff --git a/meta.js b/meta.js index 1a79a95..0dc63e6 100644 --- a/meta.js +++ b/meta.js @@ -1,97 +1,179 @@ +const path = require('path') +const fs = require('fs') +const { + sortDependencies, + installDependencies, + runLintFix, + printMessage, +} = require('./utils') +const pkg = require('./package.json') + +const templateVersion = pkg.version + module.exports = { - "helpers": { - "if_or": function (v1, v2, options) { + helpers: { + if_or(v1, v2, options) { if (v1 || v2) { - return options.fn(this); + return options.fn(this) } - return options.inverse(this); - } - }, - "prompts": { - "name": { - "type": "string", - "required": true, - "message": "Project name" + return options.inverse(this) }, - "short_name": { - "type": "string", - "required": false, - "message": "Project short name: fewer than 12 characters to not be truncated on homescreens (default: same as name)" + template_version() { + return templateVersion }, - "description": { - "type": "string", - "required": false, - "message": "Project description", - "default": "A Vue.js project" + }, + + prompts: { + name: { + type: 'string', + required: true, + message: 'Project name', + }, + description: { + type: 'string', + required: false, + message: 'Project description', + default: 'A Vue.js project', }, - "author": { - "type": "string", - "message": "Author" + author: { + type: 'string', + message: 'Author', }, - "build": { - "type": "list", - "message": "Vue build", - "choices": [ + build: { + type: 'list', + message: 'Vue build', + choices: [ { - "name": "Runtime + Compiler: recommended for most users", - "value": "standalone", - "short": "standalone" + name: 'Runtime + Compiler: recommended for most users', + value: 'standalone', + short: 'standalone', }, { - "name": "Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere", - "value": "runtime", - "short": "runtime" - } - ] + name: + 'Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere', + value: 'runtime', + short: 'runtime', + }, + ], }, - "router": { - "type": "confirm", - "message": "Install vue-router?" + router: { + type: 'confirm', + message: 'Install vue-router?', }, - "lint": { - "type": "confirm", - "message": "Use ESLint to lint your code?" + lint: { + type: 'confirm', + message: 'Use ESLint to lint your code?', }, - "lintConfig": { - "when": "lint", - "type": "list", - "message": "Pick an ESLint preset", - "choices": [ + lintConfig: { + when: 'lint', + type: 'list', + message: 'Pick an ESLint preset', + choices: [ { - "name": "Standard (https://github.com/feross/standard)", - "value": "standard", - "short": "Standard" + name: 'Standard (https://github.com/standard/standard)', + value: 'standard', + short: 'Standard', }, { - "name": "Airbnb (https://github.com/airbnb/javascript)", - "value": "airbnb", - "short": "Airbnb" + name: 'Airbnb (https://github.com/airbnb/javascript)', + value: 'airbnb', + short: 'Airbnb', }, { - "name": "none (configure it yourself)", - "value": "none", - "short": "none" - } - ] + name: 'none (configure it yourself)', + value: 'none', + short: 'none', + }, + ], }, - "unit": { - "type": "confirm", - "message": "Setup unit tests with Karma + Mocha?" + unit: { + type: 'confirm', + message: 'Set up unit tests', + }, + runner: { + when: 'unit', + type: 'list', + message: 'Pick a test runner', + choices: [ + { + name: 'Jest', + value: 'jest', + short: 'jest', + }, + { + name: 'Karma and Mocha', + value: 'karma', + short: 'karma', + }, + { + name: 'none (configure it yourself)', + value: 'noTest', + short: 'noTest', + }, + ], + }, + e2e: { + type: 'confirm', + message: 'Setup e2e tests with Nightwatch?', + }, + autoInstall: { + type: 'list', + message: + 'Should we run `npm install` for you after the project has been created? (recommended)', + choices: [ + { + name: 'Yes, use NPM', + value: 'npm', + short: 'npm', + }, + { + name: 'Yes, use Yarn', + value: 'yarn', + short: 'yarn', + }, + { + name: 'No, I will handle that myself', + value: false, + short: 'no', + }, + ], }, - "e2e": { - "type": "confirm", - "message": "Setup e2e tests with Nightwatch?" - } }, - "filters": { - ".eslintrc.js": "lint", - ".eslintignore": "lint", - "config/test.env.js": "unit || e2e", - "test/unit/**/*": "unit", - "build/webpack.test.conf.js": "unit", - "test/e2e/**/*": "e2e", - "src/router/**/*": "router" + filters: { + '.eslintrc.js': 'lint', + '.eslintignore': 'lint', + 'config/test.env.js': 'unit || e2e', + 'build/webpack.test.conf.js': "unit && runner === 'karma'", + 'test/unit/**/*': 'unit', + 'test/unit/index.js': "unit && runner === 'karma'", + 'test/unit/jest.conf.js': "unit && runner === 'jest'", + 'test/unit/karma.conf.js': "unit && runner === 'karma'", + 'test/unit/specs/index.js': "unit && runner === 'karma'", + 'test/unit/setup.js': "unit && runner === 'jest'", + 'test/e2e/**/*': 'e2e', + 'src/router/**/*': 'router', + }, + complete: function(data, { chalk }) { + const green = chalk.green + + sortDependencies(data, green) + + const cwd = path.join(process.cwd(), data.inPlace ? '' : data.destDirName) + + if (data.autoInstall) { + installDependencies(cwd, data.autoInstall, green) + .then(() => { + return runLintFix(cwd, data, green) + }) + .then(() => { + printMessage(data, green) + }) + .catch(e => { + console.log(chalk.red('Error:'), e) + }) + } else { + printMessage(data, chalk) + } }, - "completeMessage": "To get started:\n\n {{^inPlace}}cd {{destDirName}}\n {{/inPlace}}npm install\n npm run dev\n\nDocumentation can be found at https://vuejs-templates.github.io/webpack" -}; +} diff --git a/template/.babelrc b/template/.babelrc index 63826f6..6646ce7 100644 --- a/template/.babelrc +++ b/template/.babelrc @@ -8,11 +8,12 @@ }], "stage-2" ], - "plugins": ["transform-runtime"], + "plugins": ["transform-vue-jsx", "transform-runtime"]{{#if_or unit e2e}}, "env": { "test": { - "presets": ["env", "stage-2"], - "plugins": [ "istanbul" ] + "presets": ["env", "stage-2"]{{#if_eq runner "karma"}}, + "plugins": ["transform-vue-jsx", "istanbul"]{{/if_eq}}{{#if_eq runner "jest"}}, + "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]{{/if_eq}} } - } + }{{/if_or}} } diff --git a/template/.eslintrc.js b/template/.eslintrc.js index 00b5b57..4754b75 100644 --- a/template/.eslintrc.js +++ b/template/.eslintrc.js @@ -1,55 +1,77 @@ -// http://eslint.org/docs/user-guide/configuring +// https://eslint.org/docs/user-guide/configuring module.exports = { root: true, - parser: 'babel-eslint', parserOptions: { - sourceType: 'module' + parser: 'babel-eslint' }, env: { browser: true, }, {{#if_eq lintConfig "standard"}} - // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style - extends: 'standard', + extends: [ + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + 'plugin:vue/essential', + // https://github.com/standard/standard/blob/master/docs/RULES-en.md + 'standard' + ], {{/if_eq}} {{#if_eq lintConfig "airbnb"}} - extends: 'airbnb-base', + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + extends: ['plugin:vue/essential', 'airbnb-base'], + {{/if_eq}} + {{#if_eq lintConfig "none"}} + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + extends: ['plugin:vue/essential'], {{/if_eq}} // required to lint *.vue files plugins: [ - 'html' + 'vue' ], {{#if_eq lintConfig "airbnb"}} // check if imports actually resolve - 'settings': { + settings: { 'import/resolver': { - 'webpack': { - 'config': 'build/webpack.base.conf.js' + webpack: { + config: 'build/webpack.base.conf.js' } } }, {{/if_eq}} // add your custom rules here - 'rules': { + rules: { {{#if_eq lintConfig "standard"}} // allow paren-less arrow functions + // TODO: if it's not defined to standard. should it be removed? It's not present on vuejs-template/webpack 'arrow-parens': 0, // allow async-await - 'generator-star-spacing': 0, + 'generator-star-spacing': 'off', {{/if_eq}} {{#if_eq lintConfig "airbnb"}} // don't require .vue extension when importing 'import/extensions': ['error', 'always', { - 'js': 'never', - 'vue': 'never' + js: 'never', + vue: 'never' + }], + // disallow reassignment of function parameters + // disallow parameter object manipulation except for specific exclusions + 'no-param-reassign': ['error', { + props: true, + ignorePropertyModificationsFor: [ + 'state', // for vuex state + 'acc', // for reduce accumulators + 'e' // for e.returnvalue + ] }], // allow optionalDependencies 'import/no-extraneous-dependencies': ['error', { - 'optionalDependencies': ['test/unit/index.js'] + optionalDependencies: ['test/unit/index.js'] }], {{/if_eq}} // allow debugger during development - 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' } } diff --git a/template/.gitignore b/template/.gitignore index a251c24..c80b682 100644 --- a/template/.gitignore +++ b/template/.gitignore @@ -5,15 +5,16 @@ npm-debug.log* yarn-debug.log* yarn-error.log* {{#unit}} -/test/unit/coverage +/test/unit/coverage/ {{/unit}} {{#e2e}} -/test/e2e/reports +/test/e2e/reports/ selenium-debug.log {{/e2e}} # Editor directories and files .idea +.vscode *.suo *.ntvs* *.njsproj diff --git a/template/.postcssrc.js b/template/.postcssrc.js index ea9a5ab..eee3e92 100644 --- a/template/.postcssrc.js +++ b/template/.postcssrc.js @@ -2,7 +2,9 @@ module.exports = { "plugins": { - // to edit target browsers: use "browserlist" field in package.json + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json "autoprefixer": {} } } diff --git a/template/README.md b/template/README.md index a690642..4b0258a 100644 --- a/template/README.md +++ b/template/README.md @@ -33,4 +33,4 @@ npm test {{/if_or}} ``` -For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/template/build/build.js b/template/build/build.js index 91bfe0e..11eb437 100644 --- a/template/build/build.js +++ b/template/build/build.js @@ -1,5 +1,4 @@ 'use strict' - require('./check-versions')() process.env.NODE_ENV = 'production' @@ -17,17 +16,22 @@ spinner.start() rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err - webpack(webpackConfig, function (err, stats) { + webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, - children: false, + children: false, // if you are using ts-loader, setting this to true will make typescript errors show up during build chunks: false, chunkModules: false }) + '\n\n') + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' + diff --git a/template/build/check-versions.js b/template/build/check-versions.js index 2e2f531..3ef972a 100644 --- a/template/build/check-versions.js +++ b/template/build/check-versions.js @@ -1,9 +1,9 @@ 'use strict' - const chalk = require('chalk') const semver = require('semver') const packageConfig = require('../package.json') const shell = require('shelljs') + function exec (cmd) { return require('child_process').execSync(cmd).toString().trim() } @@ -13,7 +13,7 @@ const versionRequirements = [ name: 'node', currentVersion: semver.clean(process.version), versionRequirement: packageConfig.engines.node - }, + } ] if (shell.which('npm')) { @@ -26,8 +26,10 @@ if (shell.which('npm')) { module.exports = function () { const warnings = [] + for (let i = 0; i < versionRequirements.length; i++) { const mod = versionRequirements[i] + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + @@ -40,10 +42,12 @@ module.exports = function () { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log() + for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.log(' ' + warning) } + console.log() process.exit(1) } diff --git a/template/build/dev-client.js b/template/build/dev-client.js deleted file mode 100644 index ea35821..0000000 --- a/template/build/dev-client.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -/* eslint-disable */ -require('eventsource-polyfill') -var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') - -hotClient.subscribe(function (event) { - if (event.action === 'reload') { - window.location.reload() - } -}) diff --git a/template/build/dev-server.js b/template/build/dev-server.js deleted file mode 100644 index 3c94166..0000000 --- a/template/build/dev-server.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict' - -require('./check-versions')() - -const config = require('../config') -if (!process.env.NODE_ENV) { - process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) -} - -const opn = require('opn') -const path = require('path') -const express = require('express') -const webpack = require('webpack') -const proxyMiddleware = require('http-proxy-middleware') -const webpackConfig = {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' - ? require('./webpack.prod.conf') - : {{/if_or}}require('./webpack.dev.conf') - -// default port where dev server listens for incoming traffic -const port = process.env.PORT || config.dev.port -// automatically open browser, if not set will be false -const autoOpenBrowser = !!config.dev.autoOpenBrowser -// Define HTTP proxies to your custom API backend -// https://github.com/chimurai/http-proxy-middleware -const proxyTable = config.dev.proxyTable - -const app = express() -const compiler = webpack(webpackConfig) - -const devMiddleware = require('webpack-dev-middleware')(compiler, { - publicPath: webpackConfig.output.publicPath, - quiet: true -}) - -const hotMiddleware = require('webpack-hot-middleware')(compiler, { - log: false -}) -// force page reload when html-webpack-plugin template changes -compiler.plugin('compilation', function (compilation) { - compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { - hotMiddleware.publish({ action: 'reload' }) - cb() - }) -}) - -// enable hot-reload and state-preserving -// compilation error display -app.use(hotMiddleware) - -// proxy api requests -Object.keys(proxyTable).forEach(function (context) { - let options = proxyTable[context] - if (typeof options === 'string') { - options = { target: options } - } - app.use(proxyMiddleware(options.filter || context, options)) -}) - -// handle fallback for HTML5 history API -app.use(require('connect-history-api-fallback')()) - -// serve webpack bundle output -app.use(devMiddleware) - -// serve pure static assets -const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) -app.use(staticPath, express.static('./static')) - -const uri = 'http://localhost:' + port - -let _resolve -const readyPromise = new Promise(resolve => { - _resolve = resolve -}) - -console.log('> Starting dev server...') -devMiddleware.waitUntilValid(() => { - console.log('> Listening at ' + uri + '\n') - // when env is testing, don't need open it - if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { - opn(uri) - } - _resolve() -}) - -const server = app.listen(port) - -module.exports = { - ready: readyPromise, - close: () => { - server.close() - } -} diff --git a/template/build/utils.js b/template/build/utils.js index 7f8a086..117258b 100644 --- a/template/build/utils.js +++ b/template/build/utils.js @@ -1,13 +1,14 @@ 'use strict' - const path = require('path') const config = require('../config') const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') exports.assetsPath = function (_path) { const assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory + return path.posix.join(assetsSubDirectory, _path) } @@ -17,14 +18,23 @@ exports.cssLoaders = function (options) { const cssLoader = { loader: 'css-loader', options: { + //TODO: does it need to be in vuejs-template/webpack too? minimize: process.env.NODE_ENV === 'production', sourceMap: options.sourceMap } } + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { - const loaders = [cssLoader] + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + if (loader) { loaders.push({ loader: loader + '-loader', @@ -62,6 +72,7 @@ exports.cssLoaders = function (options) { exports.styleLoaders = function (options) { const output = [] const loaders = exports.cssLoaders(options) + for (const extension in loaders) { const loader = loaders[extension] output.push({ @@ -69,5 +80,24 @@ exports.styleLoaders = function (options) { use: loader }) } + return output } + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/template/build/vue-loader.conf.js b/template/build/vue-loader.conf.js index 04c79fc..33ed58b 100644 --- a/template/build/vue-loader.conf.js +++ b/template/build/vue-loader.conf.js @@ -1,18 +1,20 @@ 'use strict' - const utils = require('./utils') const config = require('../config') const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap module.exports = { loaders: utils.cssLoaders({ - sourceMap: isProduction - ? config.build.productionSourceMap - : config.dev.cssSourceMap, + sourceMap: sourceMapEnabled, extract: isProduction }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, transformToRequire: { - video: 'src', + video: ['src', 'poster'], source: 'src', img: 'src', image: 'xlink:href' diff --git a/template/build/webpack.base.conf.js b/template/build/webpack.base.conf.js index 355d43d..391160c 100644 --- a/template/build/webpack.base.conf.js +++ b/template/build/webpack.base.conf.js @@ -1,5 +1,4 @@ 'use strict' - const path = require('path') const utils = require('./utils') const config = require('../config') @@ -9,7 +8,19 @@ function resolve (dir) { return path.join(__dirname, '..', dir) } +{{#lint}}const createLintingRule = () => ({ + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter'), + emitWarning: !config.dev.showEslintErrorsInOverlay + } +}){{/lint}} + module.exports = { + context: path.resolve(__dirname, '../'), entry: { app: './src/main.js' }, @@ -26,21 +37,13 @@ module.exports = { {{#if_eq build "standalone"}} 'vue$': 'vue/dist/vue.esm.js', {{/if_eq}} - '@': resolve('src') + '@': resolve('src'), } }, module: { rules: [ {{#lint}} - { - test: /\.(js|vue)$/, - loader: 'eslint-loader', - enforce: 'pre', - include: [resolve('src'), resolve('test')], - options: { - formatter: require('eslint-friendly-formatter') - } - }, + ...(config.dev.useEslint ? [createLintingRule()] : []), {{/lint}} { test: /\.vue$/, @@ -50,7 +53,7 @@ module.exports = { { test: /\.js$/, loader: 'babel-loader', - include: [resolve('src'), resolve('test')] + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, @@ -77,5 +80,17 @@ module.exports = { } } ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' } } diff --git a/template/build/webpack.dev.conf.js b/template/build/webpack.dev.conf.js index 2035ec0..070ae22 100644 --- a/template/build/webpack.dev.conf.js +++ b/template/build/webpack.dev.conf.js @@ -1,41 +1,95 @@ 'use strict' - -const fs = require('fs') -const path = require('path') const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') +const path = require('path') const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') -// add hot-reload related code to entry chunks -Object.keys(baseWebpackConfig.entry).forEach(function (name) { - baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) -}) +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) -module.exports = merge(baseWebpackConfig, { +const devWebpackConfig = merge(baseWebpackConfig, { module: { - rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) }, // cheap-module-eval-source-map is faster for development - devtool: '#cheap-module-eval-source-map', + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, plugins: [ new webpack.DefinePlugin({ - 'process.env': config.dev.env + 'process.env': require('../config/dev.env') }), - // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', - inject: true, - serviceWorkerLoader: `` + inject: true }), - new FriendlyErrorsPlugin() + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) ] }) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/template/build/webpack.prod.conf.js b/template/build/webpack.prod.conf.js index d4e5e28..f368150 100644 --- a/template/build/webpack.prod.conf.js +++ b/template/build/webpack.prod.conf.js @@ -1,6 +1,4 @@ 'use strict' - -const fs = require('fs') const path = require('path') const utils = require('./utils') const webpack = require('webpack') @@ -11,22 +9,23 @@ const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') -const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin') const loadMinified = require('./load-minified') const env = {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' ? require('../config/test.env') - : {{/if_or}}config.build.env + : {{/if_or}}require('../config/prod.env') const webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, - extract: true + extract: true, + usePostCSS: true }) }, - devtool: config.build.productionSourceMap ? '#source-map' : false, + devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), @@ -48,14 +47,19 @@ const webpackConfig = merge(baseWebpackConfig, { }), // extract css into its own file new ExtractTextPlugin({ - filename: utils.assetsPath('css/[name].[contenthash].css') + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, }), // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ - cssProcessorOptions: { - safe: true - } + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } }), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html @@ -78,10 +82,14 @@ const webpackConfig = merge(baseWebpackConfig, { serviceWorkerLoader: `` }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', - minChunks: function (module, count) { + minChunks: function (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && @@ -96,8 +104,18 @@ const webpackConfig = merge(baseWebpackConfig, { // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', - chunks: ['vendor'] + minChunks: Infinity }), + // This instance extracts shared chunks from code splitted chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + // copy custom static assets new CopyWebpackPlugin([ { diff --git a/template/build/webpack.test.conf.js b/template/build/webpack.test.conf.js index a0f0223..0d658d9 100644 --- a/template/build/webpack.test.conf.js +++ b/template/build/webpack.test.conf.js @@ -1,13 +1,12 @@ 'use strict' - // This is the webpack config used for unit tests. const utils = require('./utils') const webpack = require('webpack') const merge = require('webpack-merge') -const baseConfig = require('./webpack.base.conf') +const baseWebpackConfig = require('./webpack.base.conf') -const webpackConfig = merge(baseConfig, { +const webpackConfig = merge(baseWebpackConfig, { // use inline sourcemap for karma-sourcemap-loader module: { rules: utils.styleLoaders() diff --git a/template/config/dev.env.js b/template/config/dev.env.js index 9423fde..1e22973 100644 --- a/template/config/dev.env.js +++ b/template/config/dev.env.js @@ -1,5 +1,4 @@ 'use strict' - const merge = require('webpack-merge') const prodEnv = require('./prod.env') diff --git a/template/config/index.js b/template/config/index.js index d67c345..d1ec13d 100644 --- a/template/config/index.js +++ b/template/config/index.js @@ -1,40 +1,77 @@ 'use strict' - +// Template version: {{ template_version }} // see http://vuejs-templates.github.io/webpack for documentation. + const path = require('path') module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + {{#lint}}// Use Eslint Loader? + // If true, your code will be linted during bundling and + // linting errors and warnings will be shown in the console. + useEslint: true, + // If true, eslint errors and warnings will also be shown in the error overlay + // in the browser. + showEslintErrorsInOverlay: false, + {{/lint}} + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true, + }, + build: { - env: require('./prod.env'), + // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), + + // Paths assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', + + /** + * Source Maps + */ + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], + // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report - }, - dev: { - env: require('./dev.env'), - port: 8080, - autoOpenBrowser: true, - assetsSubDirectory: 'static', - assetsPublicPath: '/', - proxyTable: {}, - // CSS Sourcemaps off by default because relative paths are "buggy" - // with this option, according to the CSS-Loader README - // (https://github.com/webpack/css-loader#sourcemaps) - // In our experience, they generally work as expected, - // just be aware of this issue when enabling this option. - cssSourceMap: false } } diff --git a/template/config/prod.env.js b/template/config/prod.env.js index 773d263..a6f9976 100644 --- a/template/config/prod.env.js +++ b/template/config/prod.env.js @@ -1,3 +1,4 @@ +'use strict' module.exports = { NODE_ENV: '"production"' } diff --git a/template/config/test.env.js b/template/config/test.env.js index e2ca170..c2824a3 100644 --- a/template/config/test.env.js +++ b/template/config/test.env.js @@ -1,5 +1,4 @@ 'use strict' - const merge = require('webpack-merge') const devEnv = require('./dev.env') diff --git a/template/package.json b/template/package.json index 255fc2c..a1e2169 100644 --- a/template/package.json +++ b/template/package.json @@ -5,99 +5,118 @@ "author": "{{ author }}", "private": true, "scripts": { - "dev": "node build/dev-server.js", - "start": "node build/dev-server.js", - "build": "node build/build.js"{{#unit}}, - "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run"{{/unit}}{{#e2e}}, - "e2e": "node test/e2e/runner.js"{{/e2e}}{{#if_or unit e2e}}, - "test": "{{#unit}}npm run unit{{/unit}}{{#unit}}{{#e2e}} && {{/e2e}}{{/unit}}{{#e2e}}npm run e2e{{/e2e}}"{{/if_or}}{{#lint}}, - "lint": "eslint --ext .js,.vue src{{#unit}} test/unit/specs{{/unit}}{{#e2e}} test/e2e/specs{{/e2e}}"{{/lint}} + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + {{#if_eq runner "jest"}} + "unit": "jest --config test/unit/jest.conf.js --coverage", + {{/if_eq}} + {{#if_eq runner "karma"}} + "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", + {{/if_eq}} + {{#e2e}} + "e2e": "node test/e2e/runner.js", + {{/e2e}} + {{#if_or unit e2e}} + "test": "{{#unit}}npm run unit{{/unit}}{{#unit}}{{#e2e}} && {{/e2e}}{{/unit}}{{#e2e}}npm run e2e{{/e2e}}", + {{/if_or}} + {{#lint}} + "lint": "eslint --ext .js,.vue src{{#unit}} test/unit/specs{{/unit}}{{#e2e}} test/e2e/specs{{/e2e}}", + {{/lint}} + "build": "node build/build.js" }, "dependencies": { "vue": "^2.5.2"{{#router}}, "vue-router": "^3.0.1"{{/router}} }, "devDependencies": { - "autoprefixer": "^7.1.5", - "babel-core": "^6.26.0", - "sw-precache-webpack-plugin": "^0.11.4", {{#lint}} - "babel-eslint": "^8.0.1", - {{/lint}} - "babel-loader": "^7.1.2", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.6.0", - "babel-preset-stage-2": "^6.24.1", - "babel-register": "^6.26.0", - "chalk": "^2.1.0", - "connect-history-api-fallback": "^1.4.0", - "copy-webpack-plugin": "^4.1.1", - "css-loader": "^0.28.7", - "cssnano": "^3.10.0", - {{#lint}} - "eslint": "^4.9.0", + "babel-eslint": "^8.2.1", + "eslint": "^4.15.0", "eslint-friendly-formatter": "^3.0.0", - "eslint-loader": "^1.9.0", - "eslint-plugin-html": "^3.2.2", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-node": "^5.2.0", + "eslint-loader": "^1.7.1", + "eslint-plugin-vue": "^4.0.0", {{#if_eq lintConfig "standard"}} "eslint-config-standard": "^10.2.1", - "eslint-plugin-promise": "^3.6.0", + "eslint-plugin-promise": "^3.4.0", "eslint-plugin-standard": "^3.0.1", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-node": "^5.2.0", {{/if_eq}} {{#if_eq lintConfig "airbnb"}} - "eslint-config-airbnb-base": "^11.1.3", - "eslint-import-resolver-webpack": "^0.8.1", + "eslint-config-airbnb-base": "^11.3.0", + "eslint-import-resolver-webpack": "^0.8.3", + "eslint-plugin-import": "^2.7.0", {{/if_eq}} {{/lint}} - "eventsource-polyfill": "^0.9.6", - "express": "^4.16.2", - "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^1.1.5", - "friendly-errors-webpack-plugin": "^1.6.1", - "html-webpack-plugin": "^2.30.1", - "http-proxy-middleware": "^0.17.4", - "webpack-bundle-analyzer": "^2.9.0", - {{#unit}} - "cross-env": "^5.0.5", - "karma": "^1.7.1", + {{#if_eq runner "jest"}} + "babel-jest": "^21.0.2", + "babel-plugin-dynamic-import-node": "^1.2.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", + "jest": "^21.2.0", + "jest-serializer-vue": "^0.3.0", + "vue-jest": "^1.0.2", + {{/if_eq}} + {{#if_eq runner "karma"}} + "cross-env": "^5.0.1", + "karma": "^1.4.1", "karma-coverage": "^1.1.1", "karma-mocha": "^1.3.0", - "karma-phantomjs-launcher": "^1.0.4", - "karma-phantomjs-shim": "^1.5.0", - "karma-sinon-chai": "^1.3.2", + "karma-phantomjs-launcher": "^1.0.2", + "karma-phantomjs-shim": "^1.4.0", + "karma-sinon-chai": "^1.3.1", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "0.0.31", - "karma-webpack": "^2.0.5", - "mocha": "^4.0.1", + "karma-webpack": "^2.0.2", + "mocha": "^3.2.0", "chai": "^4.1.2", - "sinon": "^4.0.1", - "sinon-chai": "^2.14.0", - "inject-loader": "^3.0.1", - "babel-plugin-istanbul": "^4.1.5", - "phantomjs-prebuilt": "^2.1.15", - {{/unit}} + "sinon": "^4.0.0", + "sinon-chai": "^2.8.0", + "inject-loader": "^3.0.0", + "babel-plugin-istanbul": "^4.1.1", + "phantomjs-prebuilt": "^2.1.14", + {{/if_eq}} {{#e2e}} - "chromedriver": "^2.33.1", - "cross-spawn": "^5.1.0", - "nightwatch": "^0.9.16", - "selenium-server": "^3.6.0", + "babel-register": "^6.22.0", + "chromedriver": "^2.27.2", + "cross-spawn": "^5.0.1", + "nightwatch": "^0.9.12", + "selenium-server": "^3.0.1", {{/e2e}} - "semver": "^5.4.1", - "shelljs": "^0.7.8", - "opn": "^5.1.0", + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.28.0", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^2.30.1", + "webpack-bundle-analyzer": "^2.9.0", + "sw-precache-webpack-plugin": "^0.11.4", + "node-notifier": "^5.1.2", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "semver": "^5.3.0", + "shelljs": "^0.7.6", "optimize-css-assets-webpack-plugin": "^3.2.0", - "ora": "^1.3.0", - "rimraf": "^2.6.2", - "uglifyjs-webpack-plugin": "^1.1.2", - "url-loader": "^0.6.2", + "ora": "^1.2.0", + "rimraf": "^2.6.0", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", "vue-loader": "^13.3.0", - "vue-style-loader": "^3.0.3", + "vue-style-loader": "^3.0.1", "vue-template-compiler": "^2.5.2", - "webpack": "^3.7.1", - "webpack-dev-middleware": "^1.12.0", - "webpack-hot-middleware": "^2.19.1", + "portfinder": "^1.0.13", + "webpack": "^3.6.0", + "webpack-dev-server": "^2.9.1", "webpack-merge": "^4.1.0", "uglify-es": "^3.1.3" }, diff --git a/template/src/App.vue b/template/src/App.vue index a7bc1bf..bf1e818 100644 --- a/template/src/App.vue +++ b/template/src/App.vue @@ -16,15 +16,15 @@