├── 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 | --------------------------------------------------------------------------------