├── src
└── index.js
├── .gitignore
├── test
└── index.js
├── index.hbs
├── .travis.yml
├── .babelrc
├── .editorconfig
├── README.md
├── webpack.config.rules.js
├── webpack.config.test.js
├── karma.conf.js
├── package.json
├── webpack.config.js
├── .eslintrc
└── helper.js
/src/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 | .DS_Store
4 | *.lock
5 | !yarn.lock
6 | *.log
7 | out/
8 | dist/
9 | node_modules/
10 | coverage/
11 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 |
3 | describe('Test', () => {
4 | it('should work', () => {
5 | assert.isTrue(true);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{htmlWebpackPlugin.options.title}}
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | script: "npm test -- --single-run --browsers Firefox"
5 | before_script:
6 | - export DISPLAY=:99.0
7 | - sh -e /etc/init.d/xvfb start
8 | cache: yarn
9 | addons:
10 | firefox: latest
11 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "modules": false,
7 | "targets": {
8 | "ie": "11"
9 | }
10 | }
11 | ]
12 | ],
13 | "plugins": [
14 | "@babel/transform-runtime"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.js,*.json]
4 | indent_style = space
5 | indent_size = 4
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 | # editorconfig-tools is unable to ignore longs strings or urls
11 | max_line_length = 120
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## LoftSchool example project
2 |
3 | ### Доступные команды
4 |
5 | * `npm install` - установить зависимости
6 | * `npm run prepare` - запустить тесты и проверить стиль кода
7 | * `npm run test` - запустить тесты
8 | * `npm run codestyle` - проверить стиль кода
9 | * `npm run start` - запустить встроенный сервер и следить за изменениями файлов
10 | * `npm run build` - собрать проект в папку `build`
11 |
--------------------------------------------------------------------------------
/webpack.config.rules.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | test: /\.js$/,
4 | exclude: /node_modules/,
5 | loader: 'babel-loader',
6 | options: { cacheDirectory: true }
7 | },
8 | {
9 | test: /\.hbs/,
10 | loader: 'handlebars-loader'
11 | },
12 | {
13 | test: /\.(jpe?g|png|gif|svg|eot|ttf|woff|woff2)$/i,
14 | loader: 'file-loader',
15 | options: {
16 | name: '[hash:8].[ext]',
17 | outputPath: 'reosurces'
18 | }
19 | }
20 | ];
21 |
--------------------------------------------------------------------------------
/webpack.config.test.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const rules = require('./webpack.config.rules');
3 |
4 | module.exports = {
5 | mode: 'development',
6 | devtool: 'inline-source-map',
7 | module: {
8 | rules: [
9 | ...rules,
10 | {
11 | test: /\.js$/,
12 | enforce: 'post',
13 | include: [path.resolve('src/')],
14 | loader: 'istanbul-instrumenter-loader',
15 | options: { esModules: true }
16 | },
17 | {
18 | test: /\.css$/,
19 | include: path.resolve('src/'),
20 | use: ['style-loader', 'css-loader']
21 | }
22 | ]
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | process.env.CHROME_BIN = require('puppeteer').executablePath()
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | basePath: '',
6 | frameworks: ['mocha', 'chai'],
7 | files: [
8 | 'test/**/*.js'
9 | ],
10 | preprocessors: {
11 | 'test/**/*.js': ['webpack', 'sourcemap'],
12 | },
13 | webpack: require('./webpack.config.test'),
14 | webpackMiddleware: {
15 | stats: 'errors-only'
16 | },
17 | reporters: ['mocha', 'coverage-istanbul'],
18 | coverageIstanbulReporter: {
19 | reports: ['html', 'lcovonly', 'text-summary'],
20 | fixWebpackSourcePaths: true
21 | },
22 | port: 9876,
23 | browsers: ['ChromeHeadless'], // или Chrome или Firefox
24 | captureTimeout: 20000,
25 | singleRun: true,
26 | plugins: [
27 | require('karma-mocha'),
28 | require('karma-chai'),
29 | require('karma-webpack'),
30 | require('karma-mocha-reporter'),
31 | require('karma-firefox-launcher'),
32 | require('karma-chrome-launcher'),
33 | require('karma-coverage-istanbul-reporter'),
34 | require('karma-sourcemap-loader')
35 | ]
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "loftschool-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "codestyle": "eslint src",
8 | "travis": "npm run codestyle && npm test",
9 | "build": "webpack --progress --colors",
10 | "watch": "webpack-dev-server --progress --colors --open",
11 | "start": "npm run watch",
12 | "test": "karma start",
13 | "cover": "istanbul cover _mocha -- test/*.js",
14 | "prepare": "npm run travis"
15 | },
16 | "author": "",
17 | "license": "MIT",
18 | "dependencies": {
19 | "@babel/core": "^7.1.2",
20 | "@babel/plugin-transform-runtime": "^7.1.0",
21 | "@babel/preset-env": "^7.1.0",
22 | "@babel/runtime": "^7.1.2",
23 | "babel-loader": "^8.0.4",
24 | "clean-webpack-plugin": "^0.1.19",
25 | "css-loader": "^1.0.1",
26 | "mini-css-extract-plugin": "^0.4.4",
27 | "file-loader": "^2.0.0",
28 | "handlebars": "^4.0.12",
29 | "handlebars-loader": "^1.7.0",
30 | "html-webpack-plugin": "^3.2.0",
31 | "style-loader": "^0.23.1",
32 | "webpack": "^4.25.1",
33 | "webpack-dev-server": "^3.1.10"
34 | },
35 | "devDependencies": {
36 | "chai": "^4.2.0",
37 | "eslint": "^5.8.0",
38 | "istanbul": "^0.4.5",
39 | "istanbul-instrumenter-loader": "^3.0.1",
40 | "karma": "^3.1.1",
41 | "karma-chai": "^0.1.0",
42 | "karma-chrome-launcher": "^2.2.0",
43 | "karma-coverage-istanbul-reporter": "^2.0.4",
44 | "karma-firefox-launcher": "^1.1.0",
45 | "karma-mocha": "^1.3.0",
46 | "karma-mocha-reporter": "^2.2.5",
47 | "karma-sourcemap-loader": "^0.3.7",
48 | "karma-webpack": "^4.0.0-rc.2",
49 | "mocha": "^5.2.0",
50 | "puppeteer": "^1.10.0",
51 | "webpack-cli": "^3.1.2"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlPlugin = require('html-webpack-plugin');
2 | const CleanWebpackPlugin = require('clean-webpack-plugin');
3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4 | const rules = require('./webpack.config.rules');
5 | const fs = require('fs');
6 | const path = require('path');
7 |
8 | const root = path.resolve('src');
9 | const files = fs.readdirSync(root)
10 | .reduce((all, current) => {
11 | const ext = path.extname(current);
12 | const name = path.basename(current, ext);
13 | const absPath = path.join(root, current);
14 |
15 | if (!all.hasOwnProperty(ext)) {
16 | all[ext] = [];
17 | }
18 |
19 | all[ext].push({ name, absPath });
20 |
21 | return all;
22 | }, { '.js': [], '.hbs': [] });
23 | const entries = files['.js'].reduce((all, { name, absPath }) => {
24 | all[name] = absPath;
25 |
26 | return all;
27 | }, {});
28 | const html = files['.hbs']
29 | .filter(file => entries.hasOwnProperty(file.name))
30 | .map((file) => {
31 | return new HtmlPlugin({
32 | title: file.name,
33 | template: file.absPath,
34 | filename: `${file.name}.html`,
35 | chunks: [file.name]
36 | });
37 | });
38 |
39 | if (!html.length || !files['.hbs'].find(file => file.name === 'index')) {
40 | html.push(new HtmlPlugin({
41 | title: 'index',
42 | template: 'index.hbs',
43 | chunks: ['index']
44 | }));
45 | }
46 |
47 | module.exports = {
48 | entry: entries,
49 | output: {
50 | filename: '[name].[hash].js',
51 | path: path.resolve('dist')
52 | },
53 | mode: 'development',
54 | devtool: 'source-map',
55 | module: {
56 | rules: [
57 | ...rules,
58 | {
59 | test: /\.css$/,
60 | use: [
61 | MiniCssExtractPlugin.loader,
62 | 'css-loader'
63 | ]
64 | }
65 | ]
66 | },
67 | plugins: [
68 | new MiniCssExtractPlugin({
69 | filename: '[name].css',
70 | }),
71 | ...html,
72 | new CleanWebpackPlugin(['dist'])
73 | ]
74 | };
75 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "sourceType": "module",
4 | "ecmaVersion": 7
5 | },
6 | "env": {
7 | "node": true,
8 | "browser": true,
9 | "mocha": true,
10 | "es6": true
11 | },
12 | "extends": [
13 | "eslint:recommended"
14 | ],
15 | "rules": {
16 | "no-cond-assign": "off",
17 | "space-before-blocks": "error",
18 | "spaced-comment": "error",
19 | "curly": "error",
20 | "guard-for-in": "error",
21 | "no-caller": "error",
22 | "no-else-return": "error",
23 | "no-empty-function": "error",
24 | "no-new-wrappers": "error",
25 | "no-with": "error",
26 | "block-spacing": "error",
27 | "brace-style": [
28 | "error",
29 | "1tbs"
30 | ],
31 | "indent": [
32 | "error",
33 | 4,
34 | {
35 | "SwitchCase": 1
36 | }
37 | ],
38 | "max-len": [
39 | "error",
40 | 120,
41 | { "ignoreComments": true }
42 | ],
43 | "newline-after-var": "error",
44 | "newline-before-return": "error",
45 | "no-multiple-empty-lines": [
46 | "error",
47 | {
48 | "max": 1,
49 | "maxEOF": 1
50 | }
51 | ],
52 | "no-nested-ternary": "error",
53 | "no-tabs": "error",
54 | "one-var-declaration-per-line": "error",
55 | "quotes": [
56 | "error",
57 | "single"
58 | ],
59 | "max-statements-per-line": [
60 | "error",
61 | {
62 | "max": 1
63 | }
64 | ],
65 | "keyword-spacing": [
66 | "error",
67 | {
68 | "after": true
69 | }
70 | ],
71 | "key-spacing": [
72 | "error",
73 | {
74 | "afterColon": true
75 | }
76 | ],
77 | "object-curly-spacing": ["error", "always"],
78 | "dot-notation": "error",
79 | "no-eval": "error",
80 | "no-multi-spaces": "error",
81 | "yoda": "error",
82 | "camelcase": [
83 | "error",
84 | {
85 | "properties": "never"
86 | }
87 | ],
88 | "comma-spacing": [
89 | "error",
90 | {
91 | "after": true
92 | }
93 | ],
94 | "consistent-this": [
95 | "error",
96 | "that"
97 | ],
98 | "lines-around-directive": [
99 | "error",
100 | "always"
101 | ],
102 | "max-nested-callbacks": [
103 | "error",
104 | 4
105 | ]
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/helper.js:
--------------------------------------------------------------------------------
1 | export function randomNumber(min = 0, max = 100) {
2 | return Math.round((max - min) * Math.random()) + min;
3 | }
4 |
5 | export function randomValue(types, maxDepth = 2) {
6 | let depth = 0;
7 | let allTypes = ['string', 'number', 'boolean', 'null', 'undefined', 'array', 'object'];
8 | let type;
9 |
10 | if (types) {
11 | types = Array.isArray(types) ? types : [types]
12 | } else {
13 | types = allTypes;
14 | }
15 |
16 | type = randomNumber(0, types.length - 1);
17 |
18 | switch (types[type]) {
19 | case 'string': {
20 | let length = randomNumber(3, 10);
21 | let string = [];
22 |
23 | for (let i = 0; i < length; i++) {
24 | string.push(String.fromCharCode(randomNumber(33, 126)));
25 | }
26 |
27 | return string.join('');
28 | }
29 | case 'number':
30 | return randomNumber();
31 | case 'boolean':
32 | return !!randomNumber(0, 1);
33 | case 'null':
34 | return null;
35 | case 'array': {
36 | if (depth < maxDepth) {
37 | let length = randomNumber(3, 10);
38 | let array = [];
39 |
40 | depth++;
41 |
42 | for (let i = 0; i < length; i++) {
43 | array.push(randomValue(depth == maxDepth ? allTypes.slice(0, -2) : allTypes, maxDepth - depth));
44 | }
45 |
46 | return array;
47 | } else {
48 | return randomValue(allTypes.slice(0, -2));
49 | }
50 | }
51 | case 'object': {
52 | if (depth < maxDepth) {
53 | let length = randomNumber(3, 10);
54 | let object = {};
55 |
56 | depth++;
57 |
58 | for (let i = 0; i < length; i++) {
59 | let key = randomValue('string');
60 |
61 | object[key] = randomValue(depth == maxDepth ? allTypes.slice(0, -2) : allTypes, maxDepth - depth);
62 | }
63 |
64 | return object;
65 | } else {
66 | return randomValue(allTypes.slice(0, -2));
67 | }
68 | }
69 | }
70 | }
71 |
72 | export function randomNumberArray(mode, minLength = 3, maxLength = 10, min, max) {
73 | let length = randomNumber(minLength, maxLength);
74 | let array = [];
75 |
76 | for (let i = 0; i < length; i++) {
77 | let number = randomNumber(min, max);
78 |
79 | if ((mode == 'even' && number % 2) || (mode == 'odd' && !(number % 2))) {
80 | number++;
81 | }
82 |
83 | array.push(number);
84 | }
85 |
86 | return array;
87 | }
88 |
89 | export function randomStringArray(minLength = 3, maxLength = 10) {
90 | let length = randomNumber(minLength, maxLength);
91 | let array = [];
92 |
93 | for (let i = 0; i < length; i++) {
94 | array.push(randomValue('string'));
95 | }
96 |
97 | return array;
98 | }
99 |
--------------------------------------------------------------------------------