diff --git a/.babelrc b/.babelrc index 3ec2bee..3f1e098 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,21 @@ { - "presets": ["es2015", "stage-2"], - "plugins": ["transform-runtime"], - "comments": false + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": [ + "transform-vue-jsx", + "transform-runtime" + ], + "env": { + "test": { + "presets": ["env", "stage-2"], + "plugins": ["transform-vue-jsx", "istanbul"] + } + } } - diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e2192c5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +/build/ +/config/ +/dist/ +/*.js +/test/unit/coverage/ diff --git a/.eslintrc.js b/.eslintrc.js index 85a8376..22fdce8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,16 +1,29 @@ +// https://eslint.org/docs/user-guide/configuring + module.exports = { root: true, - // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style - extends: 'standard', + parserOptions: { + parser: 'babel-eslint' + }, + env: { + browser: true, + }, + 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' + ], // required to lint *.vue files plugins: [ - 'html' + 'vue' ], // add your custom rules here - 'rules': { - // allow paren-less arrow functions - 'arrow-parens': 0, + rules: { + // allow async-await + 'generator-star-spacing': 'off', // 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/.gitignore b/.gitignore index dcf82cc..895e844 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,15 @@ .DS_Store -node_modules -npm-debug.log +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/test/unit/coverage/ + +# Editor directories and files .idea -example-dist +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..eee3e92 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/README.md b/README.md index 24c9614..601a12a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,54 @@ # Vuetify Credit Card -### Vue.js credit card component +## Vue.js credit card component -jessepollak's [Card](http://github.com/jessepollak/card) make credit card forms look awesome. -The vue-credit-card is a Vue.js component that aims to do the same for Vue.js(vue2.0) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](#) + +The vuetify-credit-card is a Vue.js component that aims to do the same for Vue.js(vue2.0). + +### Dependencies: +- [Vue.js](https://vuejs.org/) +- [Pug.js](https://pugjs.org/) +- [Stylus](http://stylus-lang.com/) +- [Payment](https://github.com/jessepollak/payment) +- [Puzzle-Pattern](https://github.com/guastallaigor/puzzle-pattern/) + +
+ + iliojunior + +
+ +## Props + +| Name | Required | Type/Value | Default | Description | +| --- | --- | --- | --- | --- | +| value | No | `Object` | {} | Used for set datas in component | +| invertCard | No | `Boolean` | false | Flag to flip card component | +| formatValue | No | `Boolean` | false | Flag to format values on value prop | + +## Contribute + +You must follow [Puzzle-Pattern](https://github.com/guastallaigor/puzzle-pattern/) like the Code of Conduct. + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8082 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +Jessepollak's [Card](http://github.com/jessepollak/card) make credit card forms look awesome. + +## Support: +- [Ilio Adriano de Oliveira Junior](https://github.com/iliojunior) - ilioadriano@live.com diff --git a/build/build.js b/build/build.js index 374f19e..8f2ad8a 100644 --- a/build/build.js +++ b/build/build.js @@ -1,34 +1,41 @@ -// https://github.com/shelljs/shelljs -require('shelljs/global') -env.NODE_ENV = 'production' +'use strict' +require('./check-versions')() -var path = require('path') -var config = require('../config') -var ora = require('ora') -var webpack = require('webpack') -var webpackConfig = require('./webpack.prod.conf') +process.env.NODE_ENV = 'production' -console.log( - ' Tip:\n' + - ' Built files are meant to be served over an HTTP server.\n' + - ' Opening index.html over file:// won\'t work.\n' -) +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') -var spinner = ora('building for production...') +const spinner = ora('building for production...') spinner.start() -var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) -rm('-rf', assetsPath) -mkdir('-p', assetsPath) - -webpack(webpackConfig, function (err, stats) { - spinner.stop() +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err - process.stdout.write(stats.toString({ - colors: true, - modules: false, - children: false, - chunks: false, - chunkModules: false - }) + '\n') + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: 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' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) }) diff --git a/build/check-versions.js b/build/check-versions.js new file mode 100644 index 0000000..3ef972a --- /dev/null +++ b/build/check-versions.js @@ -0,0 +1,54 @@ +'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() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.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 ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + 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/build/logo.png b/build/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/build/logo.png differ diff --git a/build/utils.js b/build/utils.js index d294e35..e534fb0 100644 --- a/build/utils.js +++ b/build/utils.js @@ -1,56 +1,101 @@ -var path = require('path') -var config = require('../config') -var ExtractTextPlugin = require('extract-text-webpack-plugin') +'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) { - return path.posix.join(config.build.assetsSubDirectory, _path) + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) } exports.cssLoaders = function (options) { options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + // generate loader string to be used with extract text plugin - function generateLoaders (loaders) { - var sourceLoader = loaders.map(function (loader) { - var extraParamChar - if (/\?/.test(loader)) { - loader = loader.replace(/\?/, '-loader?') - extraParamChar = '&' - } else { - loader = loader + '-loader' - extraParamChar = '?' - } - return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') - }).join('!') + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + // Extract CSS when that option is specified + // (which is the case during production build) if (options.extract) { - return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) } else { - return ['vue-style-loader', sourceLoader].join('!') + return ['vue-style-loader'].concat(loaders) } } - // http://vuejs.github.io/vue-loader/configurations/extract-css.html + // https://vue-loader.vuejs.org/en/configurations/extract-css.html return { - css: generateLoaders(['css']), - postcss: generateLoaders(['css']), - less: generateLoaders(['css', 'less']), - sass: generateLoaders(['css', 'sass?indentedSyntax']), - scss: generateLoaders(['css', 'sass']), - stylus: generateLoaders(['css', 'stylus']), - styl: generateLoaders(['css', 'stylus']) + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') } } // Generate loaders for standalone style files (outside of .vue) exports.styleLoaders = function (options) { - var output = [] - var loaders = exports.cssLoaders(options) - for (var extension in loaders) { - var loader = loaders[extension] + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] output.push({ test: new RegExp('\\.' + extension + '$'), - loader: loader + 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/build/vue-loader.conf.js b/build/vue-loader.conf.js new file mode 100644 index 0000000..33ed58b --- /dev/null +++ b/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'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: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index d2004f6..b042af9 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -1,95 +1,92 @@ -var path = require('path') -var config = require('../config') -var utils = require('./utils') -var projectRoot = path.resolve(__dirname, '../') +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +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: { app: './example/main.js' }, output: { path: config.build.assetsRoot, - publicPath: config.build.assetsPublicPath, - filename: '[name].js' + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath }, resolve: { - extensions: ['', '.js', '.vue'], - fallback: [path.join(__dirname, '../node_modules')], + extensions: ['.js', '.vue', '.json'], alias: { - 'src': path.resolve(__dirname, '../src'), - 'assets': path.resolve(__dirname, '../src/assets'), - 'components': path.resolve(__dirname, '../src/components') + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), } }, - resolveLoader: { - fallback: [path.join(__dirname, '../node_modules')] - }, module: { - preLoaders: [ - { - test: /\.vue$/, - loader: 'eslint', - include: projectRoot, - exclude: /node_modules/ - }, - { - test: /\.js$/, - loader: 'eslint', - include: projectRoot, - exclude: /node_modules/ - } - ], - loaders: [ + rules: [ + ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.vue$/, - loader: 'vue' + loader: 'vue-loader', + options: vueLoaderConfig }, { test: /\.js$/, - loader: 'babel', - include: projectRoot, - exclude: /node_modules/ - }, - { - test: /\.json$/, - loader: 'json' - }, - { - test: /\.html$/, - loader: 'vue-html' + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url', - query: { + loader: 'url-loader', + options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url', - query: { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + name: utils.assetsPath('media/[name].[hash:7].[ext]') } }, { - // sass - test: /\.scss$/, - loaders: 'style!css!sass' + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } } - ], - noParse: [ - /chart\.js/, - /handsontable\.(full\.)?js/, - /plotly\.js/ - ], - }, - eslint: { - formatter: require('eslint-friendly-formatter') + ] }, - vue: { - loaders: utils.cssLoaders() + 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/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 9c4b1b9..070ae22 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -1,35 +1,95 @@ -var config = require('../config') -var webpack = require('webpack') -var merge = require('webpack-merge') -var utils = require('./utils') -var baseWebpackConfig = require('./webpack.base.conf') -var HtmlWebpackPlugin = require('html-webpack-plugin') +'use strict' +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: { - loaders: utils.styleLoaders() + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + 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, + } }, - // eval-source-map is faster for development - devtool: '#eval-source-map', 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.optimize.OccurenceOrderPlugin(), - new webpack.optimize.DedupePlugin(), new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin(), + 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: 'example/index.html', - inject: true, - }) + template: 'index.html', + inject: true + }), + // 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/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 39c5068..2f17259 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -1,53 +1,72 @@ -var path = require('path') -var config = require('../config') -var utils = require('./utils') -var webpack = require('webpack') -var merge = require('webpack-merge') -var baseWebpackConfig = require('./webpack.base.conf') -var ExtractTextPlugin = require('extract-text-webpack-plugin') -var HtmlWebpackPlugin = require('html-webpack-plugin') -var env = process.env.NODE_ENV === 'testing' +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +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 = process.env.NODE_ENV === 'testing' ? require('../config/test.env') - : config.build.env + : require('../config/prod.env') -module.exports = merge(baseWebpackConfig, { +const webpackConfig = merge(baseWebpackConfig, { module: { - loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + 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'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, - vue: { - loaders: utils.cssLoaders({ - sourceMap: config.build.productionSourceMap, - extract: true - }) - }, plugins: [ - // http://vuejs.github.io/vue-loader/workflow/production.html + // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false - } + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true }), - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.optimize.DedupePlugin(), // extract css into its own file - new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), + new ExtractTextPlugin({ + 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: 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 // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: process.env.NODE_ENV === 'testing' - ? 'example/index.html' + ? 'index.html' : config.build.index, - template: 'example/index.html', + template: 'index.html', inject: true, minify: { removeComments: true, @@ -59,13 +78,18 @@ module.exports = merge(baseWebpackConfig, { // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), + // 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 (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && + /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 @@ -76,7 +100,50 @@ module.exports = 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([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) ] }) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/build/webpack.test.conf.js b/build/webpack.test.conf.js new file mode 100644 index 0000000..0d658d9 --- /dev/null +++ b/build/webpack.test.conf.js @@ -0,0 +1,32 @@ +'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 baseWebpackConfig = require('./webpack.base.conf') + +const webpackConfig = merge(baseWebpackConfig, { + // use inline sourcemap for karma-sourcemap-loader + module: { + rules: utils.styleLoaders() + }, + devtool: '#inline-source-map', + resolveLoader: { + alias: { + // necessary to to make lang="scss" work in test when using vue-loader's ?inject option + // see discussion at https://github.com/vuejs/vue-loader/issues/724 + 'scss-loader': 'sass-loader' + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/test.env') + }) + ] +}) + +// no need for app entry during tests +delete webpackConfig.entry + +module.exports = webpackConfig diff --git a/config/dev.env.js b/config/dev.env.js index efead7c..1e22973 100644 --- a/config/dev.env.js +++ b/config/dev.env.js @@ -1,5 +1,6 @@ -var merge = require('webpack-merge') -var prodEnv = require('./prod.env') +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"' diff --git a/config/index.js b/config/index.js index 68576dc..bfdb3d7 100644 --- a/config/index.js +++ b/config/index.js @@ -1,17 +1,76 @@ +'use strict' +// Template version: 1.3.1 // see http://vuejs-templates.github.io/webpack for documentation. -var path = require('path') + +const path = require('path') module.exports = { - build: { - env: require('./prod.env'), - index: path.resolve(__dirname, '../example-dist/index.html'), - assetsRoot: path.resolve(__dirname, '../example-dist'), + dev: { + + // Paths assetsSubDirectory: 'static', - assetsPublicPath: '', - productionSourceMap: true + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: '0.0.0.0', // 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- + + // 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, + + /** + * 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 }, - dev: { - env: require('./dev.env'), - port: 3004 + + build: { + // 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 } } diff --git a/config/prod.env.js b/config/prod.env.js index 773d263..a6f9976 100644 --- a/config/prod.env.js +++ b/config/prod.env.js @@ -1,3 +1,4 @@ +'use strict' module.exports = { NODE_ENV: '"production"' } diff --git a/config/test.env.js b/config/test.env.js index 89f90de..c2824a3 100644 --- a/config/test.env.js +++ b/config/test.env.js @@ -1,5 +1,6 @@ -var merge = require('webpack-merge') -var devEnv = require('./dev.env') +'use strict' +const merge = require('webpack-merge') +const devEnv = require('./dev.env') module.exports = merge(devEnv, { NODE_ENV: '"testing"' diff --git a/example/App.vue b/example/App.vue index 5ebebf9..5e4c380 100644 --- a/example/App.vue +++ b/example/App.vue @@ -1,58 +1,96 @@ -