├── .gitignore ├── README.md ├── config ├── helpers.js ├── webpack.common.js ├── webpack.dev.js ├── webpack.prod.js └── webpack.test.js ├── modules.d.ts ├── package-lock.json ├── package.json ├── src ├── app │ ├── Position.ts │ ├── app.html │ ├── app.scss │ ├── app.ts │ ├── input.ts │ └── squiggles │ │ ├── Settings.ts │ │ ├── State.ts │ │ └── squiggle.ts ├── index.html ├── index.ts ├── package.ts ├── styles.scss └── vendor.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage/* 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | yarn.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVG squiggles 2 | Colorful, mouse following, squiggles. Written with Typescript, GSAP and rxjs. 3 | 4 | Demo https://codepen.io/steveg3003/full/qobgBM/ 5 | 6 | ### Getting started 7 | First you need to download the the packages 8 | 9 | ~~~~ 10 | npm install 11 | ~~~~ 12 | 13 | ### Development 14 | For development run 15 | 16 | ~~~~ 17 | npm run start 18 | ~~~~ 19 | 20 | Which will the app on http://localhost:3000 and will reload when you make changes 21 | 22 | ### Build 23 | To create a new build run 24 | 25 | ~~~~ 26 | npm run build 27 | ~~~~ 28 | 29 | Which will create a new version of the app in a 'dists' folder. 30 | -------------------------------------------------------------------------------- /config/helpers.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var _root = path.resolve(__dirname, '..'); 3 | function root(args) { 4 | args = Array.prototype.slice.call(arguments, 0); 5 | return path.join.apply(path, [_root].concat(args)); 6 | } 7 | exports.root = root; 8 | -------------------------------------------------------------------------------- /config/webpack.common.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | var helpers = require('./helpers'); 5 | 6 | module.exports = { 7 | entry: { 8 | 'vendor': './src/vendor.ts', 9 | 'app': './src/index.ts' 10 | }, 11 | 12 | resolve: { 13 | extensions: ['.js', '.ts'] 14 | }, 15 | 16 | module: { 17 | loaders: [ 18 | { 19 | test: /\.ts$/, 20 | loaders: ['awesome-typescript-loader'] 21 | }, 22 | { 23 | test: /\.html$/, 24 | loader: 'html-loader' 25 | }, 26 | { 27 | test: /\.(png|jp(e*)g|svg)$/, 28 | use: [{ 29 | loader: 'url-loader', 30 | options: { 31 | limit: 8000, // Convert images < 8kb to base64 strings 32 | name: 'images/[hash]-[name].[ext]' 33 | } 34 | }] 35 | }, 36 | { 37 | test: /\.scss$/, 38 | use: [{ 39 | loader: "style-loader" // creates style nodes from JS strings 40 | }, { 41 | loader: "css-loader" // translates CSS into CommonJS 42 | }, { 43 | loader: "sass-loader" // compiles Sass to CSS 44 | }] 45 | } 46 | ] 47 | }, 48 | 49 | plugins: [ 50 | new webpack.optimize.CommonsChunkPlugin({ 51 | name: ['app', 'vendor'] 52 | }), 53 | 54 | new HtmlWebpackPlugin({ 55 | template: './src/index.html' 56 | }), 57 | 58 | new webpack.DefinePlugin({ 59 | VERSION: JSON.stringify(process.env.npm_package_version), 60 | }) 61 | ] 62 | }; 63 | -------------------------------------------------------------------------------- /config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | var webpackMerge = require('webpack-merge'); 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 3 | var commonConfig = require('./webpack.common.js'); 4 | var helpers = require('./helpers'); 5 | 6 | module.exports = webpackMerge(commonConfig, { 7 | devtool: 'cheap-module-eval-source-map', 8 | 9 | output: { 10 | publicPath: 'http://localhost:3000/', 11 | filename: '[name].js', 12 | chunkFilename: '[id].chunk.js' 13 | }, 14 | 15 | plugins: [ 16 | new ExtractTextPlugin('[name].css') 17 | ], 18 | 19 | devServer: { 20 | historyApiFallback: true, 21 | stats: 'minimal' 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var webpackMerge = require('webpack-merge'); 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | var commonConfig = require('./webpack.common.js'); 5 | var helpers = require('./helpers'); 6 | 7 | const ENV = process.env.NODE_ENV = process.env.ENV = 'production'; 8 | 9 | module.exports = webpackMerge(commonConfig, { 10 | devtool: 'source-map', 11 | 12 | output: { 13 | path: helpers.root('dist'), 14 | publicPath: '/', 15 | filename: '[name].[hash].js', 16 | chunkFilename: '[id].[hash].chunk.js' 17 | }, 18 | 19 | plugins: [ 20 | new webpack.NoEmitOnErrorsPlugin(), 21 | new webpack.optimize.UglifyJsPlugin(), 22 | new ExtractTextPlugin('[name].[hash].css'), 23 | new webpack.DefinePlugin({ 24 | 'process.env': { 25 | 'ENV': JSON.stringify(ENV) 26 | } 27 | }) 28 | ] 29 | }); 30 | -------------------------------------------------------------------------------- /config/webpack.test.js: -------------------------------------------------------------------------------- 1 | var helpers = require('./helpers'); 2 | 3 | module.exports = { 4 | devtool: 'inline-source-map', 5 | 6 | resolve: { 7 | extensions: ['.ts', '.js'] 8 | }, 9 | 10 | module: { 11 | loaders: [ 12 | { 13 | test: /\.ts$/, 14 | loaders: ['awesome-typescript-loader'] 15 | }, 16 | { 17 | test: /\.html$/, 18 | loader: 'html' 19 | 20 | }, 21 | { 22 | test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, 23 | loader: 'null' 24 | }, 25 | { 26 | test: /\.scss$/, 27 | loaders: ["style", "css", "sass", "scss"] 28 | } 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png' 2 | declare function require(string:string): string; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svg-squiggles", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --progress --port 3000", 8 | "build": "rimraf docs && webpack --config config/webpack.prod.js --progress --profile --bail" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ste-vg/svg-squiggles.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/ste-vg/svg-squiggles/issues" 19 | }, 20 | "homepage": "https://github.com/ste-vg/svg-squiggles#readme", 21 | "dependencies": { 22 | "gsap": "^1.20.4", 23 | "rxjs": "^5.5.7" 24 | }, 25 | "devDependencies": { 26 | "@types/gsap": "^1.19.2", 27 | "awesome-typescript-loader": "^3.4.1", 28 | "css-loader": "^0.28.8", 29 | "extract-text-webpack-plugin": "^3.0.2", 30 | "file-loader": "^1.1.6", 31 | "fs": "0.0.1-security", 32 | "html-loader": "^0.5.4", 33 | "html-webpack-plugin": "^2.30.1", 34 | "node-sass": "^4.11.0", 35 | "rimraf": "^2.6.2", 36 | "sass-loader": "^6.0.6", 37 | "style-loader": "^0.19.1", 38 | "typescript": "^2.6.2", 39 | "url-loader": "^0.6.2", 40 | "webpack": "^3.10.0", 41 | "webpack-dev-server": "^2.10.0", 42 | "webpack-merge": "^4.1.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/Position.ts: -------------------------------------------------------------------------------- 1 | export interface Position 2 | { 3 | x: number, 4 | y: number 5 | } -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- 1 | path 2 | { 3 | //mix-blend-mode: color-dodge; 4 | } -------------------------------------------------------------------------------- /src/app/app.ts: -------------------------------------------------------------------------------- 1 | 2 | import './app.scss'; 3 | import { Pkg } from "../package"; 4 | import { Squiggle } from "./squiggles/squiggle"; 5 | import { SquiggleSettings } from "./squiggles/Settings"; 6 | import { SquiggleState } from './squiggles/State'; 7 | import { Position } from './Position'; 8 | import { Input } from './input'; 9 | 10 | import { Observable } from "rxjs"; 11 | 12 | const html = require('./app.html'); 13 | 14 | export class App 15 | { 16 | private container:HTMLElement; 17 | private svg:HTMLElement; 18 | private squiggles:Squiggle[] = []; 19 | 20 | private width: number = 600; 21 | private height: number = 600; 22 | 23 | private lastMousePosition:Position; 24 | private direction:Position; 25 | 26 | private grid:number = 40; 27 | 28 | constructor(container:HTMLElement) 29 | { 30 | console.log(Pkg().version); 31 | 32 | this.container = container; 33 | this.container.innerHTML = html; 34 | this.svg = document.getElementById('stage'); 35 | this.onResize(); 36 | 37 | this.tick(); 38 | 39 | let input = new Input(this.container); 40 | 41 | input.moves.subscribe((position:Position) => 42 | { 43 | for(let i = 0; i < 3; i++) this.createSqwigFromMouse(position); 44 | }) 45 | 46 | input.starts.subscribe((position:Position) => this.lastMousePosition = position) 47 | input.ends.subscribe((position:Position) => this.burst(true)) 48 | 49 | if(location.pathname.match(/fullcpgrid/i)) setInterval(() => this.burst(false), 1000) 50 | 51 | Observable.fromEvent(window, "resize").subscribe(() => this.onResize()) 52 | } 53 | 54 | burst(fromMouse:boolean = false) 55 | { 56 | for(let i = 0; i < 5; i++) this.createRandomSqwig(fromMouse); 57 | } 58 | 59 | createSqwigFromMouse(position:Position) 60 | { 61 | let sections:number = 4; 62 | if(this.lastMousePosition) 63 | { 64 | let newDirection:Position = {x: 0, y: 0}; 65 | let xAmount = Math.abs(this.lastMousePosition.x - position.x); 66 | let yAmount = Math.abs(this.lastMousePosition.y - position.y); 67 | 68 | if(xAmount > yAmount) 69 | { 70 | newDirection.x = this.lastMousePosition.x - position.x < 0 ? 1 : -1; 71 | sections += Math.round(xAmount/4) 72 | } 73 | else 74 | { 75 | newDirection.y = this.lastMousePosition.y - position.y < 0 ? 1 : -1; 76 | sections += Math.round(yAmount/4) 77 | } 78 | this.direction = newDirection; 79 | } 80 | 81 | if(this.direction) 82 | { 83 | let settings:SquiggleSettings = { 84 | x: this.lastMousePosition.x, 85 | y: this.lastMousePosition.y, 86 | directionX: this.direction.x, 87 | directionY: this.direction.y, 88 | sections: sections > 20 ? 20 : sections 89 | } 90 | let newSqwig = new Squiggle(this.svg, settings, 10 + Math.random() * (sections * 1.5)); 91 | this.squiggles.push(newSqwig); 92 | } 93 | 94 | this.lastMousePosition = position; 95 | } 96 | 97 | createRandomSqwig(fromMouse:boolean = false) 98 | { 99 | let dx = Math.random(); 100 | if(dx > 0.5) dx = dx > 0.75 ? 1 : -1; 101 | else dx = 0; 102 | let dy= 0; 103 | if(dx == 0) dx = Math.random() > 0.5 ? 1 : -1; 104 | 105 | let settings:SquiggleSettings = { 106 | x: fromMouse ? this.lastMousePosition.x : this.width / 2, // Math.round(Math.random() * (this.width / this.grid)) * this.grid, 107 | y: fromMouse ? this.lastMousePosition.y : this.height / 2, //Math.round(Math.random() * (this.height / this.grid)) * this.grid, 108 | directionX: dx, 109 | directionY: dy, 110 | sections: 5 + Math.round(Math.random() * 15) 111 | } 112 | let newSqwig = new Squiggle(this.svg, settings, this.grid/2 + Math.random() * this.grid/2); 113 | this.squiggles.push(newSqwig); 114 | } 115 | 116 | onResize() 117 | { 118 | this.width = this.container.offsetWidth; 119 | this.height = this.container.offsetHeight; 120 | 121 | this.svg.setAttribute('width', String(this.width)); 122 | this.svg.setAttribute('height', String(this.height)); 123 | } 124 | 125 | tick() 126 | { 127 | // if(this.direction && this.squiggles.length < this.lineCount) 128 | // { 129 | 130 | // } 131 | 132 | let step = this.squiggles.length - 1; 133 | 134 | while(step >= 0) 135 | { 136 | if(this.squiggles[step].state != SquiggleState.ended) 137 | { 138 | this.squiggles[step].update(); 139 | 140 | } 141 | else 142 | { 143 | this.squiggles[step] = null; 144 | this.squiggles.splice(step, 1); 145 | } 146 | 147 | --step; 148 | } 149 | 150 | requestAnimationFrame(() => this.tick()); 151 | } 152 | } -------------------------------------------------------------------------------- /src/app/input.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from "rxjs"; 2 | import { Position } from "./Position"; 3 | 4 | // code form https://codepen.io/HunorMarton/post/handling-complex-mouse-and-touch-events-with-rxjs 5 | 6 | export class Input 7 | { 8 | private mouseDowns:Observable; 9 | private mouseMoves:Observable; 10 | private mouseUps:Observable; 11 | 12 | private touchStarts:Observable; 13 | private touchMoves:Observable; 14 | private touchEnds:Observable; 15 | 16 | public starts:Observable; 17 | public moves:Observable; 18 | public ends:Observable; 19 | 20 | constructor(element:HTMLElement) 21 | { 22 | this.mouseDowns = Observable.fromEvent(element, "mousedown").map(this.mouseEventToCoordinate); 23 | this.mouseMoves = Observable.fromEvent(window, "mousemove").map(this.mouseEventToCoordinate); 24 | this.mouseUps = Observable.fromEvent(window, "mouseup").map(this.mouseEventToCoordinate); 25 | 26 | this.touchStarts = Observable.fromEvent(element, "touchstart").map(this.touchEventToCoordinate); 27 | this.touchMoves = Observable.fromEvent(element, "touchmove").map(this.touchEventToCoordinate); 28 | this.touchEnds = Observable.fromEvent(window, "touchend").map(this.touchEventToCoordinate); 29 | 30 | this.starts = this.mouseDowns.merge(this.touchStarts); 31 | this.moves = this.mouseMoves.merge(this.touchMoves); 32 | this.ends = this.mouseUps.merge(this.touchEnds); 33 | } 34 | 35 | private mouseEventToCoordinate = (mouseEvent:MouseEvent) => 36 | { 37 | mouseEvent.preventDefault(); 38 | return { 39 | x: mouseEvent.clientX, 40 | y: mouseEvent.clientY 41 | }; 42 | }; 43 | 44 | private touchEventToCoordinate = (touchEvent:TouchEvent) => 45 | { 46 | touchEvent.preventDefault(); 47 | return { 48 | x: touchEvent.changedTouches[0].clientX, 49 | y: touchEvent.changedTouches[0].clientY 50 | }; 51 | }; 52 | } -------------------------------------------------------------------------------- /src/app/squiggles/Settings.ts: -------------------------------------------------------------------------------- 1 | export interface SquiggleSettings 2 | { 3 | x: number; 4 | y: number; 5 | directionX: number; 6 | directionY: number; 7 | length?: number; 8 | sections: number; 9 | width?: number; 10 | chunkLength?: number; 11 | color?: string; 12 | progress?: number; 13 | opacity?: number; 14 | } -------------------------------------------------------------------------------- /src/app/squiggles/State.ts: -------------------------------------------------------------------------------- 1 | export enum SquiggleState 2 | { 3 | ready, 4 | animating, 5 | ended 6 | } -------------------------------------------------------------------------------- /src/app/squiggles/squiggle.ts: -------------------------------------------------------------------------------- 1 | import { SquiggleSettings } from "./Settings"; 2 | import { SquiggleState } from "./State"; 3 | import { TweenLite, Power1 } from "gsap"; 4 | 5 | interface SquiggleSet 6 | { 7 | path: SVGPathElement; 8 | settings: SquiggleSettings; 9 | } 10 | 11 | export class Squiggle 12 | { 13 | private grid:number; 14 | private stage:HTMLElement; 15 | private sqwig:SVGPathElement; 16 | private sqwigs: SquiggleSet[] = []; 17 | private settings:SquiggleSettings; 18 | public state:SquiggleState = SquiggleState.ready; 19 | 20 | constructor(stage:HTMLElement, settings:SquiggleSettings, grid:number) 21 | { 22 | this.grid = grid; 23 | this.stage = stage; 24 | 25 | settings.width = 0; 26 | settings.opacity = 1; 27 | 28 | this.state = SquiggleState.animating; 29 | let path = this.createLine(settings); 30 | let sqwigCount:number = 3; 31 | for(let i = 0; i < sqwigCount; i++) 32 | { 33 | this.createSqwig(i, sqwigCount, path, JSON.parse(JSON.stringify(settings)) as SquiggleSettings, i == sqwigCount - 1) 34 | } 35 | } 36 | 37 | createSqwig(index:number, total:number, path:string, settings:SquiggleSettings, forceWhite:boolean) 38 | { 39 | let sqwig = document.createElementNS("http://www.w3.org/2000/svg", 'path') 40 | sqwig.setAttribute('d', path) 41 | sqwig.style.fill = 'none'; 42 | sqwig.style.stroke = forceWhite ? '#303030' : this.getColor(); 43 | sqwig.style.strokeLinecap = "round" 44 | 45 | settings.length = sqwig.getTotalLength(); 46 | settings.chunkLength = settings.length / 6; //(settings.sections * 2) + (Math.random() * 40); 47 | settings.progress = settings.chunkLength; 48 | 49 | sqwig.style.strokeDasharray= `${settings.chunkLength}, ${settings.length + settings.chunkLength}` 50 | sqwig.style.strokeDashoffset = `${settings.progress}` 51 | 52 | this.stage.appendChild(sqwig); 53 | 54 | this.sqwigs.unshift({path: sqwig, settings: settings}); 55 | 56 | TweenLite.to(settings, settings.sections * 0.1, { 57 | progress: - settings.length, 58 | width: settings.sections * 0.9, 59 | ease: Power1.easeOut, 60 | delay: index * (settings.sections * 0.01), 61 | onComplete: () => 62 | { 63 | if(index = total - 1) this.state = SquiggleState.ended; 64 | sqwig.remove(); 65 | } 66 | }) 67 | } 68 | 69 | public update() 70 | { 71 | this.sqwigs.map((set: SquiggleSet) => 72 | { 73 | set.path.style.strokeDashoffset = `${set.settings.progress}`; 74 | set.path.style.strokeWidth = `${set.settings.width}px`; 75 | set.path.style.opacity = `${set.settings.opacity}`; 76 | }) 77 | 78 | } 79 | 80 | private createLine(settings:SquiggleSettings):string 81 | { 82 | let x = settings.x; 83 | let y = settings.y; 84 | let dx = settings.directionX; 85 | let dy = settings.directionY; 86 | let path:string[] = [ 87 | 'M', 88 | '' + x, 89 | '' + y, 90 | "Q" 91 | ] 92 | 93 | let steps = settings.sections; 94 | let step = 0; 95 | let getNewDirection = (direction: string, goAnywhere:boolean) => 96 | { 97 | if(!goAnywhere && settings['direction' + direction.toUpperCase()] != 0) return settings['direction' + direction.toUpperCase()]; 98 | return Math.random() < 0.5 ? -1 : 1; 99 | } 100 | 101 | while(step < steps * 2) 102 | { 103 | step++; 104 | x += (dx * (step/ 30)) * this.grid; 105 | y += (dy * (step/ 30)) * this.grid; 106 | if(step != 1) path.push(','); 107 | path.push('' + x); 108 | path.push('' + y); 109 | 110 | if(step % 2 != 0) 111 | { 112 | dx = dx == 0 ? getNewDirection('x', step > 8) : 0; 113 | dy = dy == 0 ? getNewDirection('y', step > 8) : 0; 114 | } 115 | } 116 | 117 | return path.join(' '); 118 | } 119 | 120 | private getColor():string 121 | { 122 | let offset = Math.round(Math.random() * 100) 123 | var r = Math.sin(0.3 * offset) * 100 + 155; 124 | var g = Math.sin(0.3 * offset + 2) * 100 + 155; 125 | var b = Math.sin(0.3 * offset + 4) * 100 + 155; 126 | return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b); 127 | } 128 | 129 | private componentToHex(c:number) 130 | { 131 | var hex = Math.round(c).toString(16); 132 | return hex.length == 1 ? "0" + hex : hex; 133 | } 134 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SVG Squiggles 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import './styles.scss'; 2 | import { App } from "./app/app"; 3 | 4 | let container = document.getElementById('app'); 5 | let app = new App(container); -------------------------------------------------------------------------------- /src/package.ts: -------------------------------------------------------------------------------- 1 | // declare variables defined by webpack from package.json 2 | 3 | declare var VERSION:string; 4 | 5 | interface Package 6 | { 7 | version: string 8 | } 9 | 10 | export function Pkg():Package 11 | { 12 | return { version: VERSION } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | html, body 2 | { 3 | width: 100%; 4 | height: 100%; 5 | overflow: hidden; 6 | margin: 0; 7 | padding: 0; 8 | user-select: none; 9 | color: #111; 10 | } 11 | 12 | body 13 | { 14 | background-color: #303030; 15 | } 16 | 17 | #app 18 | { 19 | width: 100%; 20 | height: 100%; 21 | } -------------------------------------------------------------------------------- /src/vendor.ts: -------------------------------------------------------------------------------- 1 | //import "gsap"; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2016", 7 | "dom" 8 | ], 9 | "moduleResolution": "node", 10 | "sourceMap": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "removeComments": false, 14 | "noImplicitAny": true, 15 | "suppressImplicitAnyIndexErrors": true 16 | } 17 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./config/webpack.dev.js'); --------------------------------------------------------------------------------