├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── README.md ├── meta.json └── template ├── .editorconfig ├── .gitignore ├── README.md ├── config ├── helpers.js ├── karma.coverage.js ├── karma.debug.js ├── karma.unit.js ├── webpack.config.base.js ├── webpack.config.coverage.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── webpack.config.test.js ├── environment ├── dev.env.js └── prod.env.js ├── package-lock.json ├── package.json ├── src ├── assets │ └── img │ │ └── logo.png ├── components │ ├── about │ │ ├── about.html │ │ ├── about.spec.ts │ │ ├── about.ts │ │ └── index.ts │ ├── home │ │ ├── home.html │ │ ├── home.scss │ │ ├── home.spec.ts │ │ ├── home.ts │ │ └── index.ts │ ├── list │ │ ├── index.ts │ │ ├── list.html │ │ ├── list.spec.ts │ │ └── list.ts │ └── navbar │ │ ├── index.ts │ │ ├── link.ts │ │ ├── navbar.html │ │ ├── navbar.spec.ts │ │ └── navbar.ts ├── favicon.ico ├── icon.png ├── index.html ├── main.ts ├── router.ts ├── sass │ └── main.scss ├── test.ts └── util │ ├── component-test.ts │ ├── hot-reload.ts │ └── log.ts ├── tsconfig.json ├── tslint.json └── typings └── vue-hot-reload-api └── index.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .vscode 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | ## [v1.3.1] (2018-02-01) 4 | 5 | **Features** 6 | 7 | * Implement Bootstrap-vue v2 8 | * Code cleanup 9 | * Uplift Typescript to v2.7.1 10 | 11 | ## [v1.3.0] (2018-01-31) 12 | 13 | **Features** 14 | 15 | * Minify CSS on build 16 | * Implement Bootstrap v4 17 | * Prompt for hot-reloading on project initialisation 18 | * Add `standard` for Javascript linting 19 | * Add `tslint-config-standard` for Typescript linting 20 | * Uplift npm packages 21 | 22 | ## [v1.2.1] (2018-01-08) 23 | 24 | **Features** 25 | 26 | * Uplift npm packages 27 | 28 | ## [v1.2.0] (2017-11-23) 29 | 30 | **Features** 31 | 32 | * Update sample components to use correct types 33 | * Uplift npm packages 34 | 35 | ## [v1.1.1] (2017-11-14) 36 | 37 | **Fixes** 38 | 39 | * Clean-up hot reloading code 40 | 41 | ## [v1.1.0] (2017-11-09) 42 | 43 | **Features** 44 | 45 | * Implement component hot-reloading without browser refresh [#29] 46 | * Add dynamic component loading 47 | * Add webpack code-splitting 48 | * Add manifest and vendor bundle creation 49 | 50 | ## [v1.0.0] (2017-11-06) 51 | 52 | **Features** 53 | 54 | * Add [uiv](https://uiv.wxsm.space/) for navbar dropdown 55 | * Move router config to `router.ts` with dynamic imports 56 | * Uplift npm packages - Vue 2.5, Webpack 3.8, Typescript 2.6 57 | 58 | ## [v0.10.0] (2017-11-06) 59 | 60 | **Features** 61 | 62 | * HTML5 history support - @davidmeirlevy [#22] 63 | 64 | ## [v0.9.0] (2017-08-24) 65 | 66 | **Features** 67 | 68 | * Add [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) for creating HTML files 69 | * Add webpack loaders for loading sass styles 70 | * Add [favicon webpack generator](https://github.com/jantimon/favicons-webpack-plugin) for creating favicons and icons! 71 | 72 | ## [v0.8.0] (2017-08-24) 73 | 74 | **Features** 75 | 76 | * Uplift npm packages - Vue 2.4, Webpack 3.5, Typescript 2.4 77 | * Remove PhantomJS 78 | 79 | ## [v0.7.1] (2017-06-30) 80 | 81 | **Fixes** 82 | 83 | * Fix misspelling of project - @dlb46 [#17] 84 | 85 | ## [v0.7.0] (2017-04-28) 86 | 87 | **Features** 88 | 89 | * Add environment vars support - @simon0191 [#15] 90 | 91 | ## [v0.6.0] (2017-04-07) 92 | 93 | **Features** 94 | 95 | * Implement chrome test debugging [#12] 96 | * Update chai and sinon assertions 97 | 98 | ## [v0.5.1] (2017-03-23) 99 | 100 | **Fixes** 101 | 102 | * Update generated readme 103 | 104 | ## [v0.5.0] (2017-03-23) 105 | 106 | **Features** 107 | 108 | * Implement mocha for unit testing 109 | * Split webpack configuration into multiple files 110 | * Rename npm tasks 111 | 112 | ## [v0.4.1] (2017-03-17) 113 | 114 | **Fixes** 115 | 116 | * Fix missing bootstrap.css 117 | 118 | 119 | ## [v0.4.0] (2017-03-17) 120 | 121 | **Features** 122 | 123 | * Uplift to Vue 2.2 124 | * Refactor tests 125 | * Use es2015 style imports 126 | 127 | **Fixes** 128 | 129 | * Vue router is not a constructor - @paulvanbladel [#6] 130 | * Typescript lint task not working 131 | 132 | 133 | ## [v0.3.1] (2017-02-20) 134 | 135 | **Features** 136 | 137 | * Add barrels 138 | 139 | 140 | ## [v0.3.0] (2017-02-20) 141 | 142 | **Features** 143 | 144 | * Fix resources path for development - @rorisme [#3] 145 | * Use webpack-dev-server for development - @rorisme [#3] 146 | 147 | 148 | ## [v0.2.0] (2017-02-17) 149 | 150 | **Features** 151 | 152 | * Migrate to webpack 2 - @ethanrubio [#2] 153 | 154 | 155 | ## [v0.1.1] (2017-01-30) 156 | 157 | **Fixes** 158 | 159 | * Add clean-css-cli dependency which was missing - @coding2012 [#1] 160 | 161 | 162 | ## v0.1.0 (2017-01-24) 163 | 164 | * Initial release 165 | 166 | [#29]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/29 167 | [#22]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/22 168 | [#17]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/17 169 | [#15]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/15 170 | [#12]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/12 171 | [#6]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/6 172 | [#3]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/3 173 | [#2]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/2 174 | [#1]: https://github.com/ducksoupdev/vue-webpack-typescript/pull/1 175 | [v1.3.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.3.0...v1.3.1 176 | [v1.3.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.2.1...v1.3.0 177 | [v1.2.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.2.0...v1.2.1 178 | [v1.2.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.1.1...v1.2.0 179 | [v1.1.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.1.0...v1.1.1 180 | [v1.1.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v1.0.0...v1.1.0 181 | [v1.0.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.10.0...v1.0.0 182 | [v0.10.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.9.0...v0.10.0 183 | [v0.9.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.8.0...v0.9.0 184 | [v0.8.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.7.1...v0.8.0 185 | [v0.7.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.7.0...v0.7.1 186 | [v0.7.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.6.0...v0.7.0 187 | [v0.6.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.5.1...v0.6.0 188 | [v0.5.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.5.0...v0.5.1 189 | [v0.5.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.4.1...v0.5.0 190 | [v0.4.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.4.0...v0.4.1 191 | [v0.4.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.3.1...v0.4.0 192 | [v0.3.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.2.0...v0.3.1 193 | [v0.3.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.2.0...v0.3.0 194 | [v0.2.0]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.1.1...v0.2.0 195 | [v0.1.1]: https://github.com/ducksoupdev/vue-webpack-typescript/compare/v0.1.0...v0.1.1 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webpack-typescript 2 | 3 | > A Vue 2.5, Webpack 3.10, Typescript 2.7, Bootstrap 4.0 setup with hot reload, dynamic imports, unit testing, 4 | code coverage, sass and bundling/minification. 5 | 6 | > See the [changelog](CHANGELOG.md) for updates. 7 | 8 | ### Usage 9 | 10 | This is a project template for [vue-cli < 3.x](https://github.com/vuejs/vue-cli). 11 | 12 | ``` bash 13 | $ npm install -g vue-cli 14 | $ vue init ducksoupdev/vue-webpack-typescript my-project 15 | $ cd my-project 16 | $ npm install 17 | $ npm run dev 18 | ``` 19 | 20 | ### What's Included 21 | 22 | - `npm run dev`: Webpack + Typescript with config for source maps & hot-reload 23 | - `npm test`: Mocha unit tests 24 | - `npm run test:debug`: Debug Mocha unit tests in Chrome 25 | - `npm run test:watch`: Fast feedback Mocha unit tests with hot-reload 26 | - `npm run coverage`: Karma coverage reporter 27 | - `npm run lint`: Lint all Typescript files 28 | - `npm run build`: build with HTML/CSS/JS minification, code splitting and icon generation 29 | - `npm run ci:teamcity`: Teamcity CI integration 30 | - `npm run ci:jenkins`: Jenkins CI integration 31 | -------------------------------------------------------------------------------- /meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "prompts": { 3 | "name": { 4 | "type": "string", 5 | "required": true, 6 | "label": "Project name" 7 | }, 8 | "description": { 9 | "type": "string", 10 | "required": true, 11 | "label": "Project description", 12 | "default": "A Vue.js project" 13 | }, 14 | "author": { 15 | "type": "string", 16 | "label": "Author" 17 | }, 18 | "hotReload": { 19 | "type": "confirm", 20 | "message": "Use hot-reloading?", 21 | "default": true 22 | } 23 | }, 24 | "completeMessage": "To get started:\n\n cd {{destDirName}}\n npm install\n npm run dev" 25 | } 26 | -------------------------------------------------------------------------------- /template/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | coverage/ 5 | src/css 6 | src/**/*.js 7 | src/**/*.map 8 | *.log 9 | -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | # {{ name }} 2 | 3 | > {{ description }} 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # lint the Typescript 15 | npm run lint 16 | 17 | # run the tests 18 | npm test 19 | 20 | # run the tests on changes 21 | npm run test:watch 22 | 23 | # run the test suite and generate a coverage report 24 | npm run coverage 25 | 26 | # run the tests on Teamcity 27 | npm run ci:teamcity 28 | 29 | # run the tests on Jenkins 30 | npm run ci:jenkins 31 | 32 | # build for production with minification 33 | npm run build 34 | 35 | # clean the production build 36 | npm run clean 37 | ``` 38 | -------------------------------------------------------------------------------- /template/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const ROOT = path.resolve(__dirname, '..') 4 | 5 | function root (args) { 6 | args = Array.prototype.slice.call(arguments, 0) 7 | return path.join.apply(path, [ROOT].concat(args)) 8 | } 9 | 10 | exports.root = root 11 | -------------------------------------------------------------------------------- /template/config/karma.coverage.js: -------------------------------------------------------------------------------- 1 | var parseArgs = require('minimist') 2 | var webpackConfig = require('./webpack.config.coverage') 3 | 4 | var args = parseArgs(process.argv.slice(2), { 5 | string: ['env'], 6 | default: { 7 | 'env': 'mocha' 8 | } 9 | }) 10 | 11 | var reporters = ['mocha', 'coverage'] 12 | 13 | if (args.env === 'tc') { 14 | reporters = ['teamcity', 'coverage'] 15 | } 16 | 17 | if (args.env === 'jk') { 18 | reporters = ['junit', 'coverage'] 19 | } 20 | 21 | module.exports = function (config) { 22 | config.set({ 23 | basePath: '..', 24 | frameworks: ['mocha', 'chai', 'sinon'], 25 | files: [ 26 | 'node_modules/es6-promise/dist/es6-promise.auto.js', 27 | 'src/test.ts' 28 | ], 29 | reporters: reporters, 30 | preprocessors: { 31 | 'src/test.ts': ['webpack'] 32 | }, 33 | webpack: webpackConfig, 34 | webpackServer: { 35 | noInfo: true 36 | }, 37 | junitReporter: { 38 | outputDir: 'reports/' 39 | }, 40 | coverageReporter: { 41 | reporters: [{ 42 | type: 'json', 43 | dir: 'coverage/json', 44 | subdir: '.' 45 | }] 46 | }, 47 | port: 9876, 48 | colors: true, 49 | logLevel: config.LOG_INFO, 50 | autoWatch: false, 51 | browsers: ['Chrome'], 52 | mime: { 53 | 'text/x-typescript': ['ts'] 54 | }, 55 | singleRun: true, 56 | concurrency: Infinity 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /template/config/karma.debug.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require('./webpack.config.test') 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | basePath: '..', 6 | frameworks: ['source-map-support', 'mocha', 'chai', 'sinon'], 7 | files: [ 8 | 'node_modules/es6-promise/dist/es6-promise.auto.js', 9 | { 10 | pattern: 'node_modules/es6-promise/dist/es6-promise.auto.map', 11 | watched: false, 12 | included: false, 13 | served: true 14 | }, 15 | 'src/test.ts' 16 | ], 17 | reporters: ['mocha'], 18 | preprocessors: { 19 | 'src/test.ts': ['webpack'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackServer: { 23 | noInfo: true 24 | }, 25 | mime: { 26 | 'text/x-typescript': ['ts'] 27 | }, 28 | port: 9876, 29 | colors: true, 30 | logLevel: config.LOG_INFO, 31 | autoWatch: true, 32 | browsers: ['Chrome_with_debugging'], 33 | customLaunchers: { 34 | Chrome_with_debugging: { 35 | base: 'Chrome', 36 | flags: ['--remote-debugging-port=9222'], 37 | debug: true 38 | } 39 | }, 40 | singleRun: false 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /template/config/karma.unit.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require('./webpack.config.test') 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | basePath: '..', 6 | frameworks: ['mocha', 'chai', 'sinon'], 7 | files: [ 8 | 'node_modules/es6-promise/dist/es6-promise.auto.js', 9 | 'src/test.ts' 10 | ], 11 | preprocessors: { 12 | 'src/test.ts': ['webpack'] 13 | }, 14 | webpack: webpackConfig, 15 | webpackServer: { noInfo: true }, 16 | reporters: ['mocha'], 17 | port: 9876, 18 | colors: true, 19 | logLevel: config.LOG_INFO, 20 | autoWatch: false, 21 | browsers: ['Chrome'], 22 | mime: { 23 | 'text/x-typescript': ['ts'] 24 | }, 25 | singleRun: true 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /template/config/webpack.config.base.js: -------------------------------------------------------------------------------- 1 | const helpers = require('./helpers') 2 | const NamedModulesPlugin = require('webpack/lib/NamedModulesPlugin') 3 | const CopyWebpackPlugin = require('copy-webpack-plugin') 4 | 5 | let config = { 6 | entry: { 7 | 'main': helpers.root('/src/main.ts') 8 | }, 9 | output: { 10 | path: helpers.root('/dist'), 11 | filename: 'js/[name].[hash].js', 12 | chunkFilename: 'js/[name].[hash].js', 13 | publicPath: '/' 14 | }, 15 | devtool: 'source-map', 16 | resolve: { 17 | extensions: ['.ts', '.js', '.html'], 18 | alias: { 19 | 'vue$': 'vue/dist/vue.esm.js' 20 | } 21 | }, 22 | module: { 23 | rules: [{ 24 | test: /\.ts$/, 25 | exclude: /node_modules/, 26 | enforce: 'pre', 27 | loader: 'tslint-loader' 28 | }, 29 | { 30 | test: /\.ts$/, 31 | exclude: /node_modules/, 32 | loader: 'awesome-typescript-loader' 33 | }, 34 | { 35 | test: /\.html$/, 36 | loader: 'raw-loader', 37 | exclude: ['./src/index.html'] 38 | } 39 | ] 40 | }, 41 | plugins: [ 42 | new NamedModulesPlugin(), 43 | new CopyWebpackPlugin([{ 44 | from: 'src/assets', 45 | to: './assets' 46 | } ]) 47 | ] 48 | } 49 | 50 | module.exports = config 51 | -------------------------------------------------------------------------------- /template/config/webpack.config.coverage.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config.test') 2 | 3 | webpackConfig.module.rules = [...webpackConfig.module.rules, 4 | { 5 | test: /\.ts$/, 6 | enforce: 'post', 7 | loader: 'istanbul-instrumenter-loader', 8 | exclude: [ 9 | 'node_modules', 10 | /\.spec\.ts$/ 11 | ], 12 | query: { 13 | esModules: true 14 | } 15 | } 16 | ] 17 | 18 | module.exports = webpackConfig 19 | -------------------------------------------------------------------------------- /template/config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const helpers = require('./helpers') 2 | const webpackConfig = require('./webpack.config.base') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const DefinePlugin = require('webpack/lib/DefinePlugin') 5 | const env = require('../environment/dev.env') 6 | 7 | webpackConfig.module.rules = [...webpackConfig.module.rules, 8 | { 9 | test: /\.scss$/, 10 | use: [{ 11 | loader: 'style-loader' 12 | }, 13 | { 14 | loader: 'css-loader' 15 | }, 16 | { 17 | loader: 'sass-loader' 18 | } 19 | ] 20 | }, 21 | { 22 | test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/, 23 | loader: 'file-loader' 24 | } 25 | ] 26 | 27 | webpackConfig.plugins = [...webpackConfig.plugins, 28 | new HtmlWebpackPlugin({ 29 | inject: true, 30 | template: helpers.root('/src/index.html'), 31 | favicon: helpers.root('/src/favicon.ico') 32 | }), 33 | new DefinePlugin({ 34 | 'process.env': env 35 | }) 36 | ] 37 | 38 | webpackConfig.devServer = { 39 | port: 8080, 40 | host: 'localhost', 41 | historyApiFallback: true, 42 | watchOptions: { 43 | aggregateTimeout: 300, 44 | poll: 1000 45 | }, 46 | contentBase: './src', 47 | open: true 48 | } 49 | 50 | module.exports = webpackConfig 51 | -------------------------------------------------------------------------------- /template/config/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin') 2 | const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const CompressionPlugin = require('compression-webpack-plugin') 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 6 | const FaviconsWebpackPlugin = require('favicons-webpack-plugin') 7 | const autoprefixer = require('autoprefixer') 8 | const webpackConfig = require('./webpack.config.base') 9 | const helpers = require('./helpers') 10 | const DefinePlugin = require('webpack/lib/DefinePlugin') 11 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') 12 | const env = require('../environment/prod.env') 13 | 14 | const extractSass = new ExtractTextPlugin({ 15 | filename: 'css/[name].[contenthash].css', 16 | disable: process.env.NODE_ENV === 'development' 17 | }) 18 | 19 | webpackConfig.module.rules = [...webpackConfig.module.rules, 20 | { 21 | test: /\.scss$/, 22 | use: extractSass.extract({ 23 | use: [{ 24 | loader: 'css-loader', 25 | options: { 26 | minimize: false, 27 | sourceMap: false, 28 | importLoaders: 2 29 | } 30 | }, 31 | { 32 | loader: 'postcss-loader', 33 | options: { 34 | plugins: () => [autoprefixer], 35 | sourceMap: false 36 | } 37 | }, 38 | { 39 | loader: 'sass-loader', 40 | options: { 41 | sourceMap: false 42 | } 43 | } 44 | ], 45 | // use style-loader in development 46 | fallback: 'style-loader' 47 | }) 48 | }, 49 | { 50 | test: /\.(jpg|png|gif)$/, 51 | loader: 'file-loader', 52 | options: { 53 | regExp: /(img\/.*)/, 54 | name: '[name].[ext]', 55 | publicPath: '../', 56 | outputPath: 'assets/img/' 57 | } 58 | }, 59 | { 60 | test: /\.(eot|svg|ttf|woff|woff2)$/, 61 | loader: 'file-loader', 62 | options: { 63 | regExp: /(fonts\/.*)/, 64 | name: '[name].[ext]', 65 | publicPath: '../', 66 | outputPath: 'fonts/' 67 | } 68 | } 69 | ] 70 | 71 | // ensure ts lint fails the build 72 | webpackConfig.module.rules[0].options = { 73 | failOnHint: true 74 | } 75 | 76 | webpackConfig.plugins = [...webpackConfig.plugins, 77 | new CommonsChunkPlugin({ 78 | name: 'vendor', 79 | minChunks: function (module) { 80 | return module.context && module.context.indexOf('node_modules') !== -1 81 | } 82 | }), 83 | new CommonsChunkPlugin({ 84 | name: 'manifest', 85 | minChunks: Infinity 86 | }), 87 | extractSass, 88 | new OptimizeCssAssetsPlugin({ 89 | cssProcessor: require('cssnano'), 90 | cssProcessorOptions: { 91 | discardUnused: false, 92 | discardComments: { removeAll: true } 93 | }, 94 | canPrint: true 95 | }), 96 | new HtmlWebpackPlugin({ 97 | inject: true, 98 | template: helpers.root('/src/index.html'), 99 | favicon: helpers.root('/src/favicon.ico'), 100 | minify: { 101 | removeComments: true, 102 | collapseWhitespace: true, 103 | removeRedundantAttributes: true, 104 | useShortDoctype: true, 105 | removeEmptyAttributes: true, 106 | removeStyleLinkTypeAttributes: true, 107 | keepClosingSlash: true, 108 | minifyJS: true, 109 | minifyCSS: true, 110 | minifyURLs: true 111 | } 112 | }), 113 | new UglifyJsPlugin({ 114 | include: /\.js$/, 115 | minimize: true 116 | }), 117 | new CompressionPlugin({ 118 | asset: '[path].gz[query]', 119 | test: /\.js$/ 120 | }), 121 | new DefinePlugin({ 122 | 'process.env': env 123 | }), 124 | new FaviconsWebpackPlugin(helpers.root('/src/icon.png')) 125 | ] 126 | 127 | module.exports = webpackConfig 128 | -------------------------------------------------------------------------------- /template/config/webpack.config.test.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config.base') 2 | const DefinePlugin = require('webpack/lib/DefinePlugin') 3 | const SourceMapDevToolPlugin = require('webpack/lib/SourceMapDevToolPlugin') 4 | const env = require('../environment/dev.env') 5 | 6 | webpackConfig.module.rules = [ 7 | { 8 | test: /\.ts$/, 9 | exclude: /node_modules/, 10 | loader: 'awesome-typescript-loader', 11 | query: { 12 | compilerOptions: { 13 | inlineSourceMap: true, 14 | sourceMap: false 15 | } 16 | } 17 | }, 18 | { 19 | test: /\.html$/, 20 | loader: 'raw-loader', 21 | exclude: ['./src/index.html'] 22 | }, 23 | { 24 | test: /\.scss$/, 25 | use: [{ 26 | loader: 'style-loader' 27 | }, 28 | { 29 | loader: 'css-loader' 30 | }, 31 | { 32 | loader: 'sass-loader' 33 | } 34 | ] 35 | }, 36 | { 37 | test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/, 38 | loader: 'url-loader?limit=8192' 39 | } 40 | ] 41 | 42 | webpackConfig.plugins = [...webpackConfig.plugins, 43 | new SourceMapDevToolPlugin({ 44 | filename: null, // if no value is provided the sourcemap is inlined 45 | test: /\.(ts|js)($|\?)/i 46 | }), 47 | new DefinePlugin({ 48 | 'process.env': env 49 | }) 50 | ] 51 | 52 | webpackConfig.devtool = 'inline-source-map' 53 | 54 | module.exports = webpackConfig 55 | -------------------------------------------------------------------------------- /template/environment/dev.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ENV: '"development"', 3 | NODE_ENV: '"development"', 4 | DEBUG_MODE: true, 5 | API_KEY: '"XXXX-XXXXX-XXXX-XXXX"' 6 | } 7 | -------------------------------------------------------------------------------- /template/environment/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ENV: '"production"', 3 | NODE_ENV: '"production"', 4 | DEBUG_MODE: false, 5 | API_KEY: '"XXXX-XXXXX-XXXX-XXXX"' 6 | } 7 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ name }}", 3 | "description": "{{ description }}", 4 | "version": "1.0.0", 5 | "author": "{{ author }}", 6 | "private": true, 7 | "engines": { 8 | "node": ">=6", 9 | "npm": ">=3" 10 | }, 11 | "scripts": { 12 | "build": "cross-env-shell NODE_ENV=production npm run clean && npm run lint && npm run test && npm run compile", 13 | "ci:teamcity": "karma --env=tc start config/karma.coverage.js && npm run coverage:remap", 14 | "ci:jenkins": "karma --env=jk start config/karma.coverage.js && npm run coverage:remap", 15 | "clean": "rimraf dist && rimraf coverage", 16 | "compile": "webpack --config config/webpack.config.prod.js", 17 | "coverage": "npm run coverage:run && npm run coverage:remap && npm run coverage:open", 18 | "coverage:open": "opn coverage/html-ts/index.html", 19 | "coverage:remap": "remap-istanbul -i coverage/json/coverage-final.json -o coverage/html-ts -t html", 20 | "coverage:run": "cross-env NODE_ENV=development karma start config/karma.coverage.js", 21 | "dev": "webpack-dev-server --config config/webpack.config.dev.js --hot --inline", 22 | "lint": "tslint src/**/*.ts", 23 | "serve": "http-server dist/ -g -o", 24 | "test": "cross-env NODE_ENV=development karma start config/karma.unit.js", 25 | "test:debug": "cross-env NODE_ENV=development karma start config/karma.debug.js", 26 | "test:watch": "cross-env NODE_ENV=development karma start config/karma.unit.js --singleRun=false --auto-watch" 27 | }, 28 | "dependencies": { 29 | "axios": "~0.17.1", 30 | "bootstrap-vue": "~2.0.0-rc.1", 31 | "vue": "~2.5.13", 32 | "vue-class-component": "~6.1.2", 33 | "vue-property-decorator": "~6.0.0", 34 | "vue-router": "~3.0.1" 35 | }, 36 | "devDependencies": { 37 | "@types/mocha": "~2.2.47", 38 | "@types/node": "~9.4.0", 39 | "@types/sinon": "~4.1.3", 40 | "@types/webpack-env": "~1.13.5", 41 | "autoprefixer": "~7.2.5", 42 | "awesome-typescript-loader": "~3.4.1", 43 | "bootstrap": "~4.0.0", 44 | "chai": "~4.1.2", 45 | "compression-webpack-plugin": "~1.1.6", 46 | "copy-webpack-plugin": "~4.3.1", 47 | "cross-env": "~5.1.3", 48 | "css-loader": "~0.28.9", 49 | "cssnano": "~3.10.0", 50 | "es6-promise": "~4.2.4", 51 | "extract-text-webpack-plugin": "~3.0.2", 52 | "favicons-webpack-plugin": "0.0.7", 53 | "file-loader": "~1.1.6", 54 | "html-webpack-plugin": "~2.30.1", 55 | "http-server": "~0.11.1", 56 | "istanbul-instrumenter-loader": "~3.0.0", 57 | "json-loader": "~0.5.7", 58 | "karma": "~2.0.0", 59 | "karma-chai": "~0.1.0", 60 | "karma-chrome-launcher": "~2.2.0", 61 | "karma-coverage": "~1.1.1", 62 | "karma-junit-reporter": "~1.2.0", 63 | "karma-mocha": "~1.3.0", 64 | "karma-mocha-reporter": "~2.2.5", 65 | "karma-sinon": "~1.0.5", 66 | "karma-source-map-support": "~1.2.0", 67 | "karma-sourcemap-loader": "~0.3.7", 68 | "karma-teamcity-reporter": "~1.1.0", 69 | "karma-webpack": "~2.0.9", 70 | "lodash.merge": "~4.6.0", 71 | "minimist": "~1.2.0", 72 | "mocha": "~5.0.0", 73 | "ncp": "~2.0.0", 74 | "node-sass": "~4.7.2", 75 | "opn-cli": "~3.1.0", 76 | "optimize-css-assets-webpack-plugin": "~3.2.0", 77 | "postcss-loader": "~2.0.10", 78 | "raw-loader": "~0.5.1", 79 | "remap-istanbul": "~0.10.1", 80 | "rimraf": "~2.6.2", 81 | "sass-loader": "~6.0.6", 82 | "sinon": "~4.2.2", 83 | "standard": "~10.0.3", 84 | "style-loader": "~0.20.1", 85 | "tslint": "~5.9.1", 86 | "tslint-config-standard": "~7.0.0", 87 | "tslint-loader": "~3.5.3", 88 | "typescript": "~2.7.1", 89 | "url-loader": "~0.6.2", 90 | "vue-hot-reload-api": "~2.2.4", 91 | "webpack": "~3.10.0", 92 | "webpack-dev-server": "~2.11.1" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /template/src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducksoupdev/vue-webpack-typescript/fbbc049f63bae46244b3b41339121bf88be2d600/template/src/assets/img/logo.png -------------------------------------------------------------------------------- /template/src/components/about/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | This is the about page 6 |

7 |

Make sure to follow the project on 8 | GitHub to stay up to date with the latest releases, or contribute to the broject by opening an issue or making 9 | a pull-request!

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /template/src/components/about/about.spec.ts: -------------------------------------------------------------------------------- 1 | import { spy, assert } from 'sinon' 2 | import { expect } from 'chai' 3 | import Component from 'vue-class-component' 4 | import { ComponentTest, MockLogger } from '../../util/component-test' 5 | import { AboutComponent } from './about' 6 | 7 | let loggerSpy = spy() 8 | 9 | @Component({ 10 | template: require('./about.html') 11 | }) 12 | class MockAboutComponent extends AboutComponent { 13 | constructor () { 14 | super() 15 | this.logger = new MockLogger(loggerSpy) 16 | } 17 | } 18 | 19 | describe('About component', () => { 20 | let directiveTest: ComponentTest 21 | 22 | beforeEach(() => { 23 | directiveTest = new ComponentTest('
', { 'about': MockAboutComponent }) 24 | }) 25 | 26 | it('should render correct contents', async () => { 27 | debugger 28 | directiveTest.createComponent() 29 | 30 | await directiveTest.execute((vm) => { 31 | expect(vm.$el.querySelector('.repo-link').getAttribute('href')).to.equal('https://github.com/ducksoupdev/vue-webpack-typescript') 32 | assert.calledWith(loggerSpy, 'about is ready!') 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /template/src/components/about/about.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | import bContainer from 'bootstrap-vue/es/components/layout/container' 3 | import bCol from 'bootstrap-vue/es/components/layout/col' 4 | import bRow from 'bootstrap-vue/es/components/layout/row' 5 | import { Logger } from '../../util/log' 6 | 7 | @Component({ 8 | template: require('./about.html'), 9 | components: { 10 | 'b-container': bContainer, 11 | 'b-col': bCol, 12 | 'b-row': bRow 13 | } 14 | }) 15 | export class AboutComponent extends Vue { 16 | 17 | repo: string = 'https://github.com/ducksoupdev/vue-webpack-typescript' 18 | protected logger: Logger 19 | 20 | mounted () { 21 | if (!this.logger) this.logger = new Logger() 22 | this.$nextTick(() => this.logger.info('about is ready!')) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /template/src/components/about/index.ts: -------------------------------------------------------------------------------- 1 | export * from './about' 2 | -------------------------------------------------------------------------------- /template/src/components/home/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | Thank you for using 6 | \{{package}} 7 | 8 |

9 |

This project is running in 10 | \{{mode}} mode 11 |

12 |

Make sure to follow the project on 13 | GitHub to stay up to date with the latest releases, or contribute to the project by opening an issue or making 14 | a pull-request!

15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /template/src/components/home/home.scss: -------------------------------------------------------------------------------- 1 | .content h1 { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /template/src/components/home/home.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { HomeComponent } from './home' 3 | import { ComponentTest } from '../../util/component-test' 4 | 5 | describe('Home component', () => { 6 | let directiveTest: ComponentTest 7 | 8 | beforeEach(() => { 9 | directiveTest = new ComponentTest('
', { 'home': HomeComponent }) 10 | }) 11 | 12 | it('should render correct contents', async () => { 13 | directiveTest.createComponent() 14 | await directiveTest.execute((vm) => { 15 | debugger 16 | const mode = process.env.ENV 17 | expect(vm.$el.querySelector('.mode').textContent).to.equal(`${mode} mode`) 18 | expect(vm.$el.querySelector('.package').textContent).to.equal('vue-webpack-typescript') 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /template/src/components/home/home.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | import bContainer from 'bootstrap-vue/es/components/layout/container' 3 | import bCol from 'bootstrap-vue/es/components/layout/col' 4 | import bRow from 'bootstrap-vue/es/components/layout/row' 5 | 6 | import './home.scss' 7 | 8 | @Component({ 9 | template: require('./home.html'), 10 | components: { 11 | 'b-container': bContainer, 12 | 'b-col': bCol, 13 | 'b-row': bRow 14 | } 15 | }) 16 | export class HomeComponent extends Vue { 17 | 18 | package: string = 'vue-webpack-typescript' 19 | repo: string = 'https://github.com/ducksoupdev/vue-webpack-typescript' 20 | mode: string = process.env.ENV 21 | 22 | } 23 | -------------------------------------------------------------------------------- /template/src/components/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home' 2 | -------------------------------------------------------------------------------- /template/src/components/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list' 2 | -------------------------------------------------------------------------------- /template/src/components/list/list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

List

5 |
    6 |
  • \{{item.name}}
  • 7 |
8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /template/src/components/list/list.spec.ts: -------------------------------------------------------------------------------- 1 | import Component from 'vue-class-component' 2 | import { expect } from 'chai' 3 | import { ComponentTest } from '../../util/component-test' 4 | import { ListComponent } from './list' 5 | 6 | @Component({ 7 | template: require('./list.html') 8 | }) 9 | class MockListComponent extends ListComponent { 10 | constructor () { 11 | super() 12 | this.axios = { 13 | get: () => { 14 | return Promise.resolve({ data: [{ name: 'test 1' }, { name: 'test 2' }, { name: 'test 3' }] }) 15 | } 16 | } 17 | } 18 | } 19 | 20 | describe('List component', () => { 21 | let directiveTest: ComponentTest 22 | 23 | beforeEach(() => { 24 | directiveTest = new ComponentTest('
', { 'list': MockListComponent }) 25 | }) 26 | 27 | it('should render correct contents', async () => { 28 | directiveTest.createComponent() 29 | 30 | await directiveTest.execute((vm) => { // ensure Vue has bootstrapped/run change detection 31 | debugger 32 | console.log(vm.$el.querySelectorAll('.content ul li')) 33 | expect(vm.$el.querySelectorAll('.content ul li').length).to.equal(3) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /template/src/components/list/list.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | import axios, { AxiosResponse } from 'axios' 3 | import bContainer from 'bootstrap-vue/es/components/layout/container' 4 | import bCol from 'bootstrap-vue/es/components/layout/col' 5 | import bRow from 'bootstrap-vue/es/components/layout/row' 6 | 7 | interface UserResponse { 8 | id: string 9 | name: string 10 | } 11 | 12 | @Component({ 13 | template: require('./list.html'), 14 | components: { 15 | 'b-container': bContainer, 16 | 'b-col': bCol, 17 | 'b-row': bRow 18 | } 19 | }) 20 | export class ListComponent extends Vue { 21 | 22 | items: UserResponse[] = [] 23 | protected axios 24 | private url = 'https://jsonplaceholder.typicode.com/users' 25 | 26 | constructor () { 27 | super() 28 | this.axios = axios 29 | } 30 | 31 | mounted () { 32 | this.$nextTick(() => { 33 | this.loadItems() 34 | }) 35 | } 36 | 37 | private loadItems () { 38 | if (!this.items.length) { 39 | this.axios.get(this.url).then((response: AxiosResponse) => { 40 | this.items = response.data 41 | }, (error) => { 42 | console.error(error) 43 | }) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /template/src/components/navbar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './navbar' 2 | -------------------------------------------------------------------------------- /template/src/components/navbar/link.ts: -------------------------------------------------------------------------------- 1 | export class Link { 2 | name: string 3 | path: string 4 | 5 | constructor (name: string, path: string) { 6 | this.name = name 7 | this.path = path 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /template/src/components/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ name }} 4 | 5 | 6 | \{{link.name}} 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /template/src/components/navbar/navbar.spec.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Component from 'vue-class-component' 4 | import { spy, assert } from 'sinon' 5 | import { expect } from 'chai' 6 | import { ComponentTest, MockLogger } from '../../util/component-test' 7 | import { NavbarComponent } from './navbar' 8 | 9 | let loggerSpy = spy() 10 | 11 | @Component({ 12 | template: require('./navbar.html') 13 | }) 14 | class MockNavbarComponent extends NavbarComponent { 15 | constructor () { 16 | super() 17 | this.logger = new MockLogger(loggerSpy) 18 | } 19 | } 20 | 21 | describe('Navbar component', () => { 22 | let directiveTest: ComponentTest 23 | let router: VueRouter 24 | 25 | before(() => { 26 | Vue.use(VueRouter) 27 | directiveTest = new ComponentTest('
loading...
', { 'navbar': MockNavbarComponent }) 28 | 29 | let homeComponent = { template: '
Home
' } 30 | let aboutComponent = { template: '
About
' } 31 | let listComponent = { template: '
List
' } 32 | 33 | router = new VueRouter({ 34 | routes: [ 35 | { path: '/', component: homeComponent }, 36 | { path: '/about', component: aboutComponent }, 37 | { path: '/list', component: listComponent } 38 | ] 39 | }) 40 | }) 41 | 42 | it('should render correct contents', async () => { 43 | directiveTest.createComponent({ router: router }) 44 | 45 | await directiveTest.execute((vm) => { // ensure Vue has bootstrapped/run change detection 46 | debugger 47 | assert.calledWith(loggerSpy, 'Default object property!') 48 | expect(vm.$el.querySelectorAll('.navbar-nav a').length).to.equal(3) 49 | }) 50 | }) 51 | 52 | describe('When clicking the about link', () => { 53 | beforeEach(async () => { 54 | directiveTest.createComponent({ router: router }) 55 | 56 | await directiveTest.execute((vm) => { 57 | let anchor = vm.$el.querySelector('.navbar-nav a[href="#/about"]') as HTMLAnchorElement 58 | anchor.click() 59 | }) 60 | }) 61 | 62 | it('should render correct about contents', async () => { 63 | await directiveTest.execute((vm) => { 64 | expect(vm.$el.querySelector('div.about').textContent).to.equal('About') 65 | }) 66 | }) 67 | }) 68 | 69 | describe('When clicking the list link', () => { 70 | beforeEach(async () => { 71 | directiveTest.createComponent({ router: router }) 72 | 73 | await directiveTest.execute((vm) => { 74 | let anchor = vm.$el.querySelector('.navbar-nav a[href="#/list"]') as HTMLAnchorElement 75 | anchor.click() 76 | }) 77 | }) 78 | 79 | it('should render correct about contents', async () => { 80 | await directiveTest.execute((vm) => { 81 | expect(vm.$el.querySelector('div.list').textContent).to.equal('List') 82 | }) 83 | }) 84 | }) 85 | 86 | }) 87 | -------------------------------------------------------------------------------- /template/src/components/navbar/navbar.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue, Watch } from 'vue-property-decorator' 2 | import bCollapse from 'bootstrap-vue/es/components/collapse/collapse' 3 | import bNavItem from 'bootstrap-vue/es/components/nav/nav-item' 4 | import bNavbar from 'bootstrap-vue/es/components/navbar/navbar' 5 | import bNavbarToggle from 'bootstrap-vue/es/components/navbar/navbar-toggle' 6 | import bNavbarBrand from 'bootstrap-vue/es/components/navbar/navbar-brand' 7 | import bNavbarNav from 'bootstrap-vue/es/components/navbar/navbar-nav' 8 | import { Link } from './link' 9 | import { Logger } from '../../util/log' 10 | 11 | @Component({ 12 | template: require('./navbar.html'), 13 | components: { 14 | 'b-collapse': bCollapse, 15 | 'b-nav-item': bNavItem, 16 | 'b-navbar': bNavbar, 17 | 'b-navbar-toggle': bNavbarToggle, 18 | 'b-navbar-brand': bNavbarBrand, 19 | 'b-navbar-nav': bNavbarNav 20 | } 21 | }) 22 | export class NavbarComponent extends Vue { 23 | 24 | object: { default: string } = { default: 'Default object property!' } 25 | links: Link[] = [ 26 | new Link('Home', '/'), 27 | new Link('About', '/about'), 28 | new Link('List', '/list') 29 | ] 30 | 31 | protected logger: Logger 32 | 33 | @Watch('$route.path') 34 | pathChanged () { 35 | this.logger.info('Changed current path to: ' + this.$route.path) 36 | } 37 | 38 | mounted () { 39 | if (!this.logger) this.logger = new Logger() 40 | this.$nextTick(() => this.logger.info(this.object.default)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /template/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducksoupdev/vue-webpack-typescript/fbbc049f63bae46244b3b41339121bf88be2d600/template/src/favicon.ico -------------------------------------------------------------------------------- /template/src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ducksoupdev/vue-webpack-typescript/fbbc049f63bae46244b3b41339121bf88be2d600/template/src/icon.png -------------------------------------------------------------------------------- /template/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ name }} 9 | 10 | 11 | 12 | 13 |
14 | 15 | loading... 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /template/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'{{#hotReload}} 2 | import { makeHot, reload } from './util/hot-reload'{{/hotReload}} 3 | import { createRouter } from './router' 4 | 5 | const navbarComponent = () => import('./components/navbar').then(({ NavbarComponent }) => NavbarComponent) 6 | // const navbarComponent = () => import(/* webpackChunkName: 'navbar' */'./components/navbar').then(({ NavbarComponent }) => NavbarComponent) 7 | 8 | import './sass/main.scss'{{#hotReload}} 9 | 10 | if (process.env.ENV === 'development' && module.hot) { 11 | const navbarModuleId = './components/navbar' 12 | 13 | // first arguments for `module.hot.accept` and `require` methods have to be static strings 14 | // see https://github.com/webpack/webpack/issues/5668 15 | makeHot(navbarModuleId, navbarComponent, 16 | module.hot.accept('./components/navbar', () => reload(navbarModuleId, (require('./components/navbar') as any).NavbarComponent))) 17 | }{{/hotReload}} 18 | 19 | // tslint:disable-next-line:no-unused-expression 20 | new Vue({ 21 | el: '#app-main', 22 | router: createRouter(), 23 | components: { 24 | 'navbar': navbarComponent 25 | } 26 | }) 27 | -------------------------------------------------------------------------------- /template/src/router.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter, { Location, Route, RouteConfig } from 'vue-router'{{#hotReload}} 3 | import { makeHot, reload } from './util/hot-reload'{{/hotReload}} 4 | 5 | const homeComponent = () => import('./components/home').then(({ HomeComponent }) => HomeComponent) 6 | const aboutComponent = () => import('./components/about').then(({ AboutComponent }) => AboutComponent) 7 | const listComponent = () => import('./components/list').then(({ ListComponent }) => ListComponent) 8 | // const homeComponent = () => import(/* webpackChunkName: 'home' */'./components/home').then(({ HomeComponent }) => HomeComponent) 9 | // const aboutComponent = () => import(/* webpackChunkName: 'about' */'./components/about').then(({ AboutComponent }) => AboutComponent) 10 | // const listComponent = () => import(/* webpackChunkName: 'list' */'./components/list').then(({ ListComponent }) => ListComponent) 11 | {{#hotReload}} 12 | if (process.env.ENV === 'development' && module.hot) { 13 | const homeModuleId = './components/home' 14 | const aboutModuleId = './components/about' 15 | const listModuleId = './components/list' 16 | 17 | // first arguments for `module.hot.accept` and `require` methods have to be static strings 18 | // see https://github.com/webpack/webpack/issues/5668 19 | makeHot(homeModuleId, homeComponent, 20 | module.hot.accept('./components/home', () => reload(homeModuleId, (require('./components/home') as any).HomeComponent))) 21 | 22 | makeHot(aboutModuleId, aboutComponent, 23 | module.hot.accept('./components/about', () => reload(aboutModuleId, (require('./components/about') as any).AboutComponent))) 24 | 25 | makeHot(listModuleId, listComponent, 26 | module.hot.accept('./components/list', () => reload(listModuleId, (require('./components/list') as any).ListComponent))) 27 | } 28 | {{/hotReload}} 29 | 30 | Vue.use(VueRouter) 31 | 32 | export const createRoutes: () => RouteConfig[] = () => [ 33 | { 34 | path: '/', 35 | component: homeComponent 36 | }, 37 | { 38 | path: '/about', 39 | component: aboutComponent 40 | }, 41 | { 42 | path: '/list', 43 | component: listComponent 44 | } 45 | ] 46 | 47 | export const createRouter = () => new VueRouter({ mode: 'history', routes: createRoutes() }) 48 | -------------------------------------------------------------------------------- /template/src/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/bootstrap.scss"; 2 | 3 | html, body, #app-main { 4 | height: 100%; 5 | } 6 | 7 | .content { 8 | margin-top: 50px; 9 | } 10 | -------------------------------------------------------------------------------- /template/src/test.ts: -------------------------------------------------------------------------------- 1 | requireAll((require as any).context('./', true, /spec.ts$/)) 2 | function requireAll (r: any): any { 3 | r.keys().forEach(r) 4 | } 5 | -------------------------------------------------------------------------------- /template/src/util/component-test.ts: -------------------------------------------------------------------------------- 1 | import Vue, { Component } from 'vue' 2 | import { SinonSpy } from 'sinon' 3 | import merge from 'lodash.merge' 4 | import { ILogger } from './log' 5 | 6 | export interface IComponents { 7 | [key: string]: Component 8 | } 9 | 10 | export class ComponentTest { 11 | 12 | public vm: Vue 13 | 14 | constructor (private template: string, private components: IComponents) { 15 | } 16 | 17 | public createComponent (createOptions?: any): void { 18 | let options = { 19 | template: this.template, 20 | components: this.components 21 | } 22 | if (createOptions) merge(options, createOptions) 23 | this.vm = new Vue(options).$mount() 24 | } 25 | 26 | public async execute (callback: (vm: Vue) => Promise | void): Promise { 27 | await Vue.nextTick() 28 | await callback(this.vm) 29 | } 30 | 31 | } 32 | 33 | export class MockLogger implements ILogger { 34 | 35 | constructor (private loggerSpy: SinonSpy) { 36 | } 37 | 38 | info (msg: any) { 39 | this.loggerSpy(msg) 40 | } 41 | 42 | warn (msg: any) { 43 | this.loggerSpy(msg) 44 | } 45 | 46 | error (msg: any) { 47 | this.loggerSpy(msg) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /template/src/util/hot-reload.ts: -------------------------------------------------------------------------------- 1 | import Vue, { Component } from 'vue' 2 | import * as api from 'vue-hot-reload-api' 3 | 4 | export async function makeHot (id: string, componentLoader: () => Promise, acceptFunc: void) { 5 | if (module.hot) { 6 | api.install(Vue) 7 | if (!api.compatible) { 8 | throw new Error('vue-hot-reload-api is not compatible with the version of Vue you are using.') 9 | } 10 | 11 | const loadedComponent = await componentLoader() 12 | api.createRecord(id, loadedComponent) 13 | } 14 | } 15 | 16 | export function reload (id: string, component: Component) { 17 | api.reload(id, component) 18 | } 19 | -------------------------------------------------------------------------------- /template/src/util/log.ts: -------------------------------------------------------------------------------- 1 | export interface ILogger { 2 | info (msg: any) 3 | warn (msg: any) 4 | error (msg: any) 5 | } 6 | 7 | export class Logger implements ILogger { 8 | 9 | info (msg: any) { 10 | console.info(msg) 11 | } 12 | 13 | warn (msg: any) { 14 | console.warn(msg) 15 | } 16 | 17 | error (msg: any) { 18 | console.error(msg) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "es5", 6 | "es2015", 7 | "es2015.promise" 8 | ], 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "target": "es5", 12 | "sourceMap": true, 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "allowSyntheticDefaultImports": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /template/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-config-standard" 3 | } 4 | -------------------------------------------------------------------------------- /template/typings/vue-hot-reload-api/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-hot-reload-api' { 2 | import Vue, { Component } from 'vue'; 3 | export function install(Vue): void; 4 | export function compatible(): boolean; 5 | export function createRecord(id: string, component: Component): void; 6 | export function reload(id: string, component: Component): void; 7 | } 8 | --------------------------------------------------------------------------------