├── src
├── pages
│ ├── blog.ts
│ ├── home.ts
│ └── about.ts
├── index.html
├── main.spec.ts
└── main.ts
├── .gitignore
├── tsconfig.json
├── README.md
├── package.json
├── karma.conf.js
├── webpack.config.js
└── tslint.json
/src/pages/blog.ts:
--------------------------------------------------------------------------------
1 | export default `
2 |
This is my blog.
3 | `;
4 |
--------------------------------------------------------------------------------
/src/pages/home.ts:
--------------------------------------------------------------------------------
1 | export default `
2 | This is home.
3 | `;
4 |
--------------------------------------------------------------------------------
/src/pages/about.ts:
--------------------------------------------------------------------------------
1 | export default `
2 | This page is about myself.
3 | `;
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | src/**/*.js
4 | src/**/*.js.map
5 | typings/
6 | dist/
7 | coverage/
8 | *.log
9 | package-lock.json
10 | *.lock
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | webpack-typescript-starter
4 |
5 |
6 | Home
7 | About
8 | Blog
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main.spec.ts:
--------------------------------------------------------------------------------
1 | import Home from './pages/home';
2 |
3 | const sum = (n1: number, n2: number) => n1 + n2;
4 |
5 | describe('Application', () => {
6 | it('true should be true', () => {
7 | expect(true).toBe(true);
8 | });
9 |
10 | it('2+2 should be 4', () => {
11 | expect(sum(2, 2)).toBe(4);
12 | });
13 |
14 | it('home page should be defined and should be string', () => {
15 | expect(Home).toBeDefined();
16 | expect(typeof Home).toBe('string');
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | async function bootstrap(main: HTMLMainElement) {
2 | const module = await import(`./pages${location.pathname}.ts`);
3 | main.innerHTML = module.default;
4 | }
5 |
6 | document.addEventListener('DOMContentLoaded', async () => {
7 | const main: HTMLMainElement = document.querySelector('main');
8 | const links = Array.from(document.querySelectorAll('a'));
9 |
10 | await bootstrap(main);
11 |
12 | links.forEach((link) => {
13 | link.addEventListener('click', async (e: MouseEvent) => {
14 | e.preventDefault();
15 |
16 | const module = await import(`./pages/${link.dataset.chunk}.ts`);
17 |
18 | history.replaceState({}, `${link.dataset.title}`, `/${link.dataset.chunk}`);
19 | main.innerHTML = module.default;
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "sourceMap": true,
5 | "declaration": false,
6 | "moduleResolution": "node",
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "target": "es5",
10 | "module": "esnext",
11 | "typeRoots": [
12 | "./node_modules/@types"
13 | ],
14 | "noImplicitReturns": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "lib": [
17 | "es2017",
18 | "es2015",
19 | "dom"
20 | ],
21 | "noImplicitAny": true,
22 | "noImplicitThis": true,
23 | "noUnusedParameters": true,
24 | "forceConsistentCasingInFileNames": true,
25 | "allowJs": true,
26 | "strictNullChecks": false
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Webpack + TypeScript starter
2 | ---
3 |
4 | 
5 |
6 | Это далеко не самый сложный starter kit. Цель его состоит в том, чтобы получить простейшую возможную настройку для работы с Webpack и TypeScript. Развернув себе этот репозиторий, вы сможете дополнить теми инструментами, которые вам нужны, например, SASS-компиляцию, Gzip, Brotli-компрессию, дополнить тестами свое окружение.
7 |
8 | ## Features
9 |
10 | - [x] Webpack
11 | - [x] TypeScript compilation
12 | - [x] ts-lint
13 | - [x] Webpack Development Server
14 | - [x] Karma and Jasmine test execution
15 |
16 | ## How to use
17 |
18 | Просто склонируйте репозиторий, а дальше:
19 |
20 | ```bash
21 | # Переходим в директорию с проектом
22 | $ cd
23 |
24 | # Удаляем `.git` директорию
25 |
26 | # Установка зависимостей
27 | $ npm i
28 | $ # or
29 | $ yarn
30 |
31 | # Запуск сборки приложения и веб-сервера:
32 | $ npm serve
33 |
34 | # Сборка приложения без минификации:
35 | $ npm run build
36 |
37 | # Сборка приложения с минификацией:
38 | $ npm run build:prod
39 |
40 | # Запуск тестов
41 | $ npm run test
42 | ```
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-typescript-starter",
3 | "version": "0.0.1",
4 | "description": "A simple Webpack starter with TypeScript transpilation",
5 | "scripts": {
6 | "serve": "webpack-dev-server --progress --color",
7 | "build": "webpack -w",
8 | "build:prod": "cross-env NODE_ENV=production webpack",
9 | "test": "karma start"
10 | },
11 | "keywords": [
12 | "webpack",
13 | "typescript"
14 | ],
15 | "author": "Ivanov Max",
16 | "license": "ISC",
17 | "devDependencies": {
18 | "@types/jasmine": "2.8.6",
19 | "@types/karma": "1.7.3",
20 | "@types/node": "9.6.5",
21 | "cross-env": "5.1.4",
22 | "html-webpack-plugin": "3.2.0",
23 | "jasmine-core": "3.1.0",
24 | "karma": "2.0.2",
25 | "karma-chrome-launcher": "2.2.0",
26 | "karma-cli": "1.0.1",
27 | "karma-coverage": "1.1.2",
28 | "karma-jasmine": "1.1.1",
29 | "karma-sourcemap-loader": "0.3.7",
30 | "karma-webpack": "4.0.0-beta.0",
31 | "source-map-loader": "0.2.3",
32 | "ts-loader": "4.2.0",
33 | "tslint": "5.9.1",
34 | "tslint-loader": "3.6.0",
35 | "typescript": "2.8.1",
36 | "uglifyjs-webpack-plugin": "1.2.4",
37 | "webpack": "4.5.0",
38 | "webpack-cli": "2.0.14",
39 | "webpack-dev-server": "3.1.3",
40 | "webpack-merge": "4.1.2"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const SourceMapDevToolPlugin = require('webpack').SourceMapDevToolPlugin;
5 |
6 | const testFile = process.argv[4] || '*';
7 | const testFilesGlob = `./src/**/${testFile}.spec.ts`;
8 |
9 | module.exports = config => {
10 | config.set({
11 | basePath: __dirname,
12 |
13 | frameworks: ['jasmine'],
14 |
15 | files: [testFilesGlob],
16 |
17 | preprocessors: {
18 | [testFilesGlob]: ['webpack', 'sourcemap']
19 | },
20 |
21 | browsers: ['Chrome'],
22 |
23 | webpack: {
24 | devtool: 'inline-source-map',
25 |
26 | mode: 'production',
27 |
28 | resolve: {
29 | extensions: ['.ts'],
30 |
31 | modules: [
32 | path.join(__dirname, 'src')
33 | ]
34 | },
35 |
36 | module: {
37 | rules: [{
38 | test: /\.ts$/,
39 | loader: 'ts-loader'
40 | }]
41 | },
42 |
43 | plugins: [
44 | new SourceMapDevToolPlugin({
45 | test: /\.(ts|js)($|\?)/
46 | })
47 | ]
48 | },
49 |
50 | webpackMiddleware: {
51 | stats: {
52 | color: true,
53 | chunks: true
54 | }
55 | },
56 |
57 | client: {
58 | captureConsole: false
59 | },
60 |
61 | logLevel: config.LOG_INFO,
62 |
63 | mime: {
64 | 'text/x-typescript': ['ts']
65 | },
66 |
67 | reporters: ['progress', 'coverage'],
68 |
69 | port: 9876,
70 |
71 | colors: true,
72 |
73 | autoWatch: false,
74 |
75 | singleRun: true,
76 |
77 | concurrency: Infinity,
78 |
79 | coverageReporter: {
80 | type: 'html',
81 | dir: 'coverage/'
82 | }
83 | });
84 | };
85 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const merge = require('webpack-merge');
5 | const isProduction = process.env.NODE_ENV == 'production';
6 |
7 | const modes = {
8 | [true]: 'production',
9 | [false]: 'development'
10 | };
11 |
12 | const config = {
13 | context: __dirname,
14 |
15 | entry: './src/main.ts',
16 |
17 | output: {
18 | filename: '[name].[hash].js',
19 | chunkFilename: '[name].[chunkhash].chunk.js',
20 | pathinfo: true
21 | },
22 |
23 | target: 'web',
24 |
25 | mode: modes[isProduction],
26 |
27 | resolve: {
28 | extensions: ['.js', '.ts']
29 | },
30 |
31 | module: {
32 | rules: [{
33 | enforce: 'pre',
34 | test: /\.js$/,
35 | loader: 'source-map-loader'
36 | }, {
37 | enforce: 'pre',
38 | test: /\.ts$/,
39 | exclude: /node_modules/,
40 | loader: 'tslint-loader'
41 | }, {
42 | test: /\.ts$/,
43 | loader: 'ts-loader'
44 | }]
45 | },
46 |
47 | plugins: [
48 | new HtmlWebpackPlugin({
49 | template: './src/index.html'
50 | })
51 | ]
52 | };
53 |
54 | if (isProduction) {
55 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
56 |
57 | module.exports = merge(config, {
58 | optimization: {
59 | minimize: true,
60 |
61 | minimizer: [
62 | new UglifyJsPlugin({
63 | parallel: require('os').cpus().length,
64 |
65 | uglifyOptions: {
66 | ie8: false,
67 |
68 | output: {
69 | ecma: 8,
70 | beautify: false,
71 | comments: false
72 | }
73 | }
74 | })
75 | ]
76 | }
77 | });
78 | } else {
79 | module.exports = merge(config, {
80 | devServer: {
81 | port: 4200,
82 | open: true,
83 | watchContentBase: true,
84 | historyApiFallback: true
85 | }
86 | });
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsRules": {
3 | "class-name": true,
4 | "comment-format": [
5 | true,
6 | "check-space"
7 | ],
8 | "indent": [
9 | true,
10 | "spaces"
11 | ],
12 | "no-duplicate-variable": true,
13 | "no-eval": true,
14 | "no-trailing-whitespace": true,
15 | "no-unsafe-finally": true,
16 | "one-line": [
17 | true,
18 | "check-open-brace",
19 | "check-whitespace"
20 | ],
21 | "quotemark": [
22 | true,
23 | "single"
24 | ],
25 | "semicolon": [
26 | true,
27 | "always"
28 | ],
29 | "triple-equals": [
30 | true,
31 | "allow-null-check"
32 | ],
33 | "variable-name": [
34 | true,
35 | "ban-keywords"
36 | ],
37 | "whitespace": [
38 | true,
39 | "check-branch",
40 | "check-decl",
41 | "check-operator",
42 | "check-separator",
43 | "check-type"
44 | ]
45 | },
46 | "rules": {
47 | "class-name": true,
48 | "comment-format": [
49 | true,
50 | "check-space"
51 | ],
52 | "indent": [
53 | true,
54 | "spaces"
55 | ],
56 | "no-eval": true,
57 | "no-internal-module": true,
58 | "no-trailing-whitespace": true,
59 | "no-unsafe-finally": true,
60 | "no-var-keyword": false,
61 | "one-line": [
62 | true,
63 | "check-open-brace",
64 | "check-whitespace"
65 | ],
66 | "quotemark": [
67 | true,
68 | "single"
69 | ],
70 | "semicolon": [
71 | true,
72 | "always"
73 | ],
74 | "triple-equals": [
75 | true,
76 | "allow-null-check"
77 | ],
78 | "typedef-whitespace": [
79 | true,
80 | {
81 | "call-signature": "nospace",
82 | "index-signature": "nospace",
83 | "parameter": "nospace",
84 | "property-declaration": "nospace",
85 | "variable-declaration": "nospace"
86 | }
87 | ],
88 | "variable-name": [
89 | true,
90 | "ban-keywords"
91 | ],
92 | "whitespace": [
93 | true,
94 | "check-branch",
95 | "check-decl",
96 | "check-operator",
97 | "check-separator",
98 | "check-type"
99 | ]
100 | }
101 | }
--------------------------------------------------------------------------------