diff --git a/README.md b/README.md index a4b24503a5..476c8224a2 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ $ npm install $ npm run dev ``` +This will scaffold the project using the `master` branch. If you wish to use the latest version of the webpack template, do the following instead: + +``` bash +$ vue init webpack#develop my-project +``` + +:warning: **The develop branch is not considered stable and can contain bugs or not build at all, so use at your own risk.** + The development server will run on port 8080 by default. If that port is already in use on your machine, the next free port will be used. ## What's Included @@ -33,7 +41,7 @@ The development server will run on port 8080 by default. If that port is already - Source maps - `npm run build`: Production ready build. - - JavaScript minified with [UglifyJS](https://github.com/mishoo/UglifyJS2). + - 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). - 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. diff --git a/docs/backend.md b/docs/backend.md index fcc6611349..e7acadf7c5 100644 --- a/docs/backend.md +++ b/docs/backend.md @@ -10,17 +10,31 @@ Let's take a look at the default `config/index.js`: 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 ... }, - dev: { - port: 8080, - proxyTable: {} - } } ``` diff --git a/docs/commands.md b/docs/commands.md index 29aa12c76e..7a8d48963b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -16,7 +16,7 @@ All build commands are executed via [NPM Scripts](https://docs.npmjs.com/misc/sc > Build assets for production. See [Integrating with Backend Framework](backend.md) for more details. -- JavaScript minified with [UglifyJS](https://github.com/mishoo/UglifyJS2). +- 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. @@ -36,3 +36,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/linter.md b/docs/linter.md index bff51c0119..80d15c2b3c 100644 --- a/docs/linter.md +++ b/docs/linter.md @@ -14,3 +14,14 @@ If you are not happy with the default linting rules, you have several options: 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](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`)* + diff --git a/docs/pre-processors.md b/docs/pre-processors.md index 8611ef542f..fa58751e32 100644 --- a/docs/pre-processors.md +++ b/docs/pre-processors.md @@ -25,14 +25,18 @@ Once installed, you can use the pre-processors inside your `*.vue` components us ### 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/structure.md b/docs/structure.md index 05d3a06a1a..33bec21d84 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -18,20 +18,25 @@ ├── test/ │ └── unit/ # unit tests │ │ ├── specs/ # test spec files -│ │ ├── setup.js # file that runs before Jest tests +│ │ ├── eslintrc # config file for eslint with extra settings only for unit tests │ │ ├── index.js # test build entry file -│ │ └── karma.conf.js # test runner config 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.js # 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/meta.js b/meta.js index 19869a693a..ed7a8b6a2d 100644 --- a/meta.js +++ b/meta.js @@ -1,3 +1,15 @@ +const path = require('path'); +const fs = require('fs'); + +function sortObject(object) { + // Based on https://github.com/yarnpkg/yarn/blob/v1.3.2/src/config.js#L79-L85 + const sortedObject = {}; + Object.keys(object).sort().forEach(item => { + sortedObject[item] = object[item]; + }); + return sortedObject; +} + module.exports = { "helpers": { "if_or": function (v1, v2, options) { @@ -72,7 +84,7 @@ module.exports = { }, "unit": { "type": "confirm", - "message": "Setup unit tests" + "message": "Set up unit tests" }, "runner": { "when": "unit", @@ -115,5 +127,20 @@ module.exports = { "test/e2e/**/*": "e2e", "src/router/**/*": "router" }, - "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" + "complete": function (data) { + const packageJsonFile = path.join( + data.inPlace ? "" : data.destDirName, + "package.json" + ); + const packageJson = JSON.parse(fs.readFileSync(packageJsonFile)); + packageJson.devDependencies = sortObject(packageJson.devDependencies); + packageJson.dependencies = sortObject(packageJson.dependencies); + fs.writeFileSync( + packageJsonFile, + JSON.stringify(packageJson, null, 2) + "\n" + ); + + const message = `To get started:\n\n ${data.inPlace ? '' : `cd ${data.destDirName}\n `}npm install\n npm run dev\n\nDocumentation can be found at https://vuejs-templates.github.io/webpack`; + console.log("\n" + message.split(/\r?\n/g).map(line => " " + line).join("\n")); + } }; diff --git a/package.json b/package.json index 92538abd00..09951adace 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-cli-template-webpack", - "version": "1.2.4", + "version": "1.2.5", "license": "MIT", "description": "A full-featured Webpack setup with hot-reload, lint-on-save, unit testing & css extraction.", "scripts": { diff --git a/template/.eslintrc.js b/template/.eslintrc.js index 7844912865..8e8be20e8d 100644 --- a/template/.eslintrc.js +++ b/template/.eslintrc.js @@ -22,34 +22,32 @@ module.exports = { ], {{#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 - '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' }], // 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/build/build.js b/template/build/build.js index 30f036a180..955e9defca 100644 --- a/template/build/build.js +++ b/template/build/build.js @@ -16,7 +16,7 @@ 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({ diff --git a/template/build/check-versions.js b/template/build/check-versions.js index ca407bb16f..3ef972a08d 100644 --- a/template/build/check-versions.js +++ b/template/build/check-versions.js @@ -3,6 +3,7 @@ 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() } @@ -25,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 ' + @@ -39,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/utils.js b/template/build/utils.js index 55c301fe8d..e534fb0fd6 100644 --- a/template/build/utils.js +++ b/template/build/utils.js @@ -2,12 +2,13 @@ const path = require('path') const config = require('../config') const ExtractTextPlugin = require('extract-text-webpack-plugin') -const pkg = require('../package.json') +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) } @@ -21,7 +22,7 @@ exports.cssLoaders = function (options) { } } - var postcssLoader = { + const postcssLoader = { loader: 'postcss-loader', options: { sourceMap: options.sourceMap @@ -31,6 +32,7 @@ exports.cssLoaders = function (options) { // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + if (loader) { loaders.push({ loader: loader + '-loader', @@ -68,6 +70,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({ @@ -75,21 +78,21 @@ exports.styleLoaders = function (options) { use: loader }) } + return output } -exports.createNotifierCallback = function () { +exports.createNotifierCallback = () => { const notifier = require('node-notifier') return (severity, errors) => { - if (severity !== 'error') { - return - } - const error = errors[0] + if (severity !== 'error') return + const error = errors[0] const filename = error.file && error.file.split('!').pop() + notifier.notify({ - title: pkg.name, + 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 847f7cf2b9..33ed58bc0a 100644 --- a/template/build/vue-loader.conf.js +++ b/template/build/vue-loader.conf.js @@ -6,16 +6,15 @@ const sourceMapEnabled = isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap - module.exports = { loaders: utils.cssLoaders({ sourceMap: sourceMapEnabled, extract: isProduction }), cssSourceMap: sourceMapEnabled, - cacheBusting: config.dev.cacheBusting, + 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 e2f09ecc61..1f6057d611 100644 --- a/template/build/webpack.base.conf.js +++ b/template/build/webpack.base.conf.js @@ -8,6 +8,17 @@ function resolve (dir) { return path.join(__dirname, '..', dir) } +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 + } +}) + module.exports = { context: path.resolve(__dirname, '../'), entry: { @@ -32,16 +43,7 @@ module.exports = { module: { rules: [ {{#lint}} - ...(config.dev.useEslint? [{ - test: /\.(js|vue)$/, - loader: 'eslint-loader', - enforce: 'pre', - include: [resolve('src'), resolve('test')], - options: { - formatter: require('eslint-friendly-formatter'), - emitWarning: !config.dev.showEslintErrorsInOverlay - } - }] : []), + ...(config.dev.useEslint ? [createLintingRule()] : []), {{/lint}} { test: /\.vue$/, @@ -78,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 old mode 100644 new mode 100755 index 813a7b0ccd..03e94282ba --- a/template/build/webpack.dev.conf.js +++ b/template/build/webpack.dev.conf.js @@ -8,6 +8,9 @@ const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const portfinder = require('portfinder') +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + const devWebpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) @@ -21,13 +24,12 @@ const devWebpackConfig = merge(baseWebpackConfig, { historyApiFallback: true, hot: true, compress: true, - host: process.env.HOST || config.dev.host, - port: process.env.PORT || config.dev.port, + host: HOST || config.dev.host, + port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, - overlay: config.dev.errorOverlay ? { - warnings: false, - errors: true, - } : false, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxyTable, quiet: true, // necessary for FriendlyErrorsPlugin @@ -65,7 +67,7 @@ module.exports = new Promise((resolve, reject) => { // Add FriendlyErrorsPlugin devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ compilationSuccessInfo: { - messages: [`Your application is running here: http://${config.dev.host}:${port}`], + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], }, onErrors: config.dev.notifyOnErrors ? utils.createNotifierCallback() diff --git a/template/build/webpack.prod.conf.js b/template/build/webpack.prod.conf.js index b8df87e647..d121faed35 100644 --- a/template/build/webpack.prod.conf.js +++ b/template/build/webpack.prod.conf.js @@ -9,6 +9,7 @@ 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 UglifyJsPlugin = require('uglifyjs-webpack-plugin') const env = {{#if_or unit e2e}}process.env.NODE_ENV === 'testing' ? require('../config/test.env') @@ -33,10 +34,11 @@ const webpackConfig = merge(baseWebpackConfig, { new webpack.DefinePlugin({ 'process.env': env }), - // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } }, sourceMap: config.build.productionSourceMap, parallel: true @@ -53,8 +55,8 @@ const webpackConfig = merge(baseWebpackConfig, { // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap - ? { safe: true, map: { inline: false } } - : { safe: true } + ? { 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 @@ -82,7 +84,7 @@ const webpackConfig = merge(baseWebpackConfig, { // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', - minChunks: function (module) { + minChunks (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && diff --git a/template/config/index.js b/template/config/index.js index 08d4d4d166..0a7cfb43e1 100644 --- a/template/config/index.js +++ b/template/config/index.js @@ -1,5 +1,5 @@ 'use strict' -// Template version: 1.2.4 +// Template version: 1.2.5 // see http://vuejs-templates.github.io/webpack for documentation. const path = require('path') diff --git a/template/package.json b/template/package.json index 03d1efc5a8..23ffeb6ccc 100644 --- a/template/package.json +++ b/template/package.json @@ -14,7 +14,8 @@ "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}} + "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}} @@ -28,20 +29,8 @@ "vue-router": "^3.0.1"{{/router}} }, "devDependencies": { - "autoprefixer": "^7.1.2", - "babel-core": "^6.22.1", {{#lint}} "babel-eslint": "^7.1.1", - {{/lint}} - "babel-loader": "^7.1.1", - "babel-plugin-transform-runtime": "^6.22.0", - "babel-preset-env": "^1.3.2", - "babel-preset-stage-2": "^6.22.0", - "babel-register": "^6.22.0", - "chalk": "^2.0.1", - "copy-webpack-plugin": "^4.0.1", - "css-loader": "^0.28.0", - {{#lint}} "eslint": "^3.19.0", "eslint-friendly-formatter": "^3.0.0", "eslint-loader": "^1.7.1", @@ -59,12 +48,6 @@ "eslint-plugin-import": "^2.7.0", {{/if_eq}} {{/lint}} - "eventsource-polyfill": "^0.9.6", - "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", {{#if_eq runner "jest"}} "babel-jest": "^21.0.2", "babel-plugin-dynamic-import-node": "^1.2.0", @@ -92,13 +75,29 @@ "babel-plugin-istanbul": "^4.1.1", "phantomjs-prebuilt": "^2.1.14", {{/if_eq}} - "node-notifier": "^5.1.2", {{#e2e}} + "babel-register": "^6.22.0", "chromedriver": "^2.27.2", "cross-spawn": "^5.0.1", "nightwatch": "^0.9.12", "selenium-server": "^3.0.1", {{/e2e}} + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-loader": "^7.1.1", + "babel-plugin-transform-runtime": "^6.22.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", + "eventsource-polyfill": "^0.9.6", + "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", + "node-notifier": "^5.1.2", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.8", "semver": "^5.3.0", @@ -106,6 +105,7 @@ "optimize-css-assets-webpack-plugin": "^3.2.0", "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.1",