danger-wcc 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a19b6ce24d61f5ff2a951f9902122eda8942c8c7
4
- data.tar.gz: e958cfa4740a01893df04a960f902ea5eb541dfc
3
+ metadata.gz: 356af9ca6ebc46af86c402709d820bb30fd94c1e
4
+ data.tar.gz: 6842e1bc5330f0088b2978c58e9c9e85afab2030
5
5
  SHA512:
6
- metadata.gz: 2779c8031e0b5dbcc595c07b9bec606c4daab6e94214fa1808909f80819b2f4c95358448adf93b4ec8462c5e1179c981489cfd4eb5be7f48291a4635189e62d3
7
- data.tar.gz: 353a0fb641903e0ce67fc874114976fcd90070a5c28c9c6d81f59682926e874d1ef67236ebe0de64e75f97572353b86fa4b0398ed91155cb0254a7e8787691f4
6
+ metadata.gz: 5c31ffdd4c4cdc7f4a7c8f1631816d6f1452ba31bc0a03d8a5e1c392265ef8b29decd9e644d2f5b437b4ec2d3a282d7973a2f2d81b8fe1c9d02c84e8c3315523
7
+ data.tar.gz: 9d8fecdeaab1eda89620ac77454c28b9975987fb5a875ce472bae1e8ba2fdac511b54dbfe0647c46ef6c7f3fccdfc9b501a5ca24fbc85c016328e26108445842
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
+ spec.add_runtime_dependency 'activesupport', '> 5'
22
23
  spec.add_runtime_dependency 'brakeman'
23
24
  spec.add_runtime_dependency 'danger-plugin-api', '~> 1.0'
24
25
  spec.add_runtime_dependency 'flay'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DangerWCC
4
- VERSION = '0.0.6'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require_relative 'utils'
5
+
6
+ class Danger::DangerWCC < Danger::Plugin
7
+ class Dependencies
8
+ include Utils
9
+
10
+ def yarn_info
11
+ @yarn_info ||= YarnInfo.new(self) if File.exist?('yarn.lock')
12
+ end
13
+
14
+ def initialize(plugin, options = {})
15
+ @plugin = plugin
16
+ @options = options
17
+ end
18
+
19
+ def perform
20
+ return unless File.exist?('yarn.lock')
21
+
22
+ find_yarn_violations
23
+ end
24
+
25
+ private
26
+
27
+ def issue_yarn_violation(package, versions)
28
+ line_index = yarn_info.find_index_in_lockfile(package, versions[1])
29
+
30
+ plugin.fail "Dangerous change! #{package} was updated "\
31
+ "from #{versions[0]} to #{versions[1]}"\
32
+ ' without a corresponding change to package.json!',
33
+ file: 'yarn.lock', line: line_index
34
+ end
35
+
36
+ def find_yarn_violations # rubocop:disable Metrics/AbcSize
37
+ # if there's a corresponding change in the package.json, ignore
38
+ mods =
39
+ yarn_info.modified_yarn_dependencies
40
+ .except(*yarn_info.package_json_changes)
41
+ .select { |_, versions| dangerous_change?(versions[0], versions[1]) }
42
+
43
+ has_dangerous_top_level_changes = false
44
+ # issue warnings for top level dependencies
45
+ mods.slice(*yarn_info.package_json_dependencies)
46
+ .each do |package, versions|
47
+ has_dangerous_top_level_changes = true
48
+ issue_yarn_violation(package, versions)
49
+ end
50
+ # issue warnings if a sub-dependency changed without a dangerous change in
51
+ # a top level dependency
52
+ return if has_dangerous_top_level_changes
53
+
54
+ mods.except(*yarn_info.package_json_dependencies)
55
+ .each do |package, versions|
56
+ issue_yarn_violation(package, versions)
57
+ end
58
+ end
59
+
60
+ def dangerous_change?(old_version, new_version)
61
+ # the package was deleted
62
+ return true unless new_version
63
+
64
+ old_segments = old_version.segments
65
+ new_segments = new_version.segments
66
+
67
+ # the major or minor version changed.
68
+ new_segments[0] > old_segments[0] ||
69
+ new_segments[1] > old_segments[1]
70
+ end
71
+ end
72
+ end
73
+
74
+ require_relative 'dependencies/yarn_info'
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Danger::DangerWCC::Dependencies
4
+ class YarnInfo
5
+ def yarn_lock
6
+ @yarn_lock ||= File.readlines('yarn.lock')
7
+ end
8
+
9
+ def package_json_dependencies
10
+ @package_json_dependencies ||=
11
+ JSON.parse(File.read('package.json'))['dependencies']&.keys || []
12
+ end
13
+
14
+ def package_json_changes
15
+ @package_json_changes ||= find_package_json_changes
16
+ end
17
+
18
+ def modified_yarn_dependencies
19
+ @modified_yarn_dependencies ||= find_modified_yarn_packages
20
+ end
21
+
22
+ attr_reader :plugin
23
+
24
+ def initialize(plugin)
25
+ @plugin = plugin
26
+ end
27
+
28
+ def find_index_in_lockfile(package, version)
29
+ return 0 unless version
30
+
31
+ re = Regexp.new("^#{Regexp.escape(package)}@", Regexp::IGNORECASE)
32
+ indexes =
33
+ yarn_lock.each_with_index
34
+ .select { |l, _i| re.match(l) }
35
+ .map { |pair| pair[1] }
36
+ idx =
37
+ indexes.find do |i|
38
+ yarn_lock[i + 1].include?("version \"#{version}\"")
39
+ end
40
+ (idx || -1) + 1
41
+ end
42
+
43
+ def parse_yarn_semver(line)
44
+ match = /(?<package>\S+)\@(?<version>\S+)/.match(line)
45
+ [match['package'], Gem::Version.new(match['version'])] if match
46
+ end
47
+
48
+ private
49
+
50
+ def find_package_json_changes
51
+ return [] unless file = plugin.find_file_in_diff('package.json')
52
+
53
+ adds = file.hunks.flat_map { |h| h.lines.select(&:addition?) }
54
+ adds.map { |l| /\"(?<package>\S+)\"\: \"\S+\"/.match(l.content) }
55
+ .compact
56
+ .map { |match| match['package'] }
57
+ end
58
+
59
+ def find_modified_yarn_packages # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
60
+ diff = plugin.run_and_diff(
61
+ 'NODE_ENV=production yarn list --depth 0 2>/dev/null'
62
+ )
63
+ diff = GitDiff.from_string(diff)
64
+
65
+ {}.tap do |modified_packages|
66
+ plugin.each_file_in_diff(diff) do |file, _diff|
67
+ file.hunks.each do |hunk|
68
+ deleted, added =
69
+ %i[deletion? addition?].map do |type|
70
+ Hash[hunk.lines.select { |l| l.public_send(type) }
71
+ .map { |l| parse_yarn_semver(l.content) }
72
+ .compact]
73
+ end
74
+ deleted.each do |(package, version)|
75
+ modified_packages[package] =
76
+ [version, added[package]]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -7,6 +7,7 @@ require_relative 'rubocop_exceptions'
7
7
  require_relative 'commit_lint'
8
8
  require_relative 'reek'
9
9
  require_relative 'jshint'
10
+ require_relative 'dependencies'
10
11
 
11
12
  class Danger::DangerWCC < Danger::Plugin
12
13
  include Utils
@@ -19,7 +20,8 @@ class Danger::DangerWCC < Danger::Plugin
19
20
  brakeman: true,
20
21
  commit_lint: false,
21
22
  reek: false,
22
- jshint: false
23
+ jshint: false,
24
+ dependencies: true
23
25
  }.freeze
24
26
 
25
27
  # Runs all the included checks in the plugin
@@ -95,6 +97,11 @@ class Danger::DangerWCC < Danger::Plugin
95
97
  Jshint.new(self, options).perform
96
98
  end
97
99
 
100
+ def dependencies(options = {})
101
+ logger.info "dependencies: #{options}"
102
+ Dependencies.new(self, options).perform
103
+ end
104
+
98
105
  private
99
106
 
100
107
  def parse_flay_results
@@ -22,7 +22,7 @@ module Utils
22
22
  # All the diffs in the PR parsed into GitDiff objects
23
23
  def parsed_diffs
24
24
  @parsed_diffs ||=
25
- plugin.git.diff.map do |d|
25
+ plugin.git.diff&.map do |d|
26
26
  begin
27
27
  GitDiff.from_string(d.patch)
28
28
  rescue StandardError
@@ -32,6 +32,13 @@ module Utils
32
32
  end
33
33
  end
34
34
 
35
+ def find_file_in_diff(filename)
36
+ each_file_in_diff do |file, _diff|
37
+ return file if file.b_path == filename
38
+ end
39
+ nil
40
+ end
41
+
35
42
  # Finds lines in the overall diff matching the given regex, and
36
43
  # executes a block for each matched line.
37
44
  # The results of the yield block are returned as an array.
@@ -52,17 +59,22 @@ module Utils
52
59
 
53
60
  def each_file_in_diff(passed_diff = nil)
54
61
  diffs = passed_diff ? [passed_diff] : parsed_diffs
55
- diffs.flat_map do |diff|
62
+ diffs&.flat_map do |diff|
56
63
  diff.files.flat_map do |file|
57
64
  yield(file, diff)
58
65
  end
59
66
  end
60
67
  end
61
68
 
62
- def each_addition_in_diff(passed_diff = nil)
69
+ def each_addition_in_diff(passed_diff = nil, &block)
70
+ each_line_in_diff(passed_diff, type: :addition, &block)
71
+ end
72
+
73
+ def each_line_in_diff(passed_diff = nil, type: nil)
63
74
  each_file_in_diff(passed_diff) do |file, diff|
64
75
  file.hunks.flat_map do |hunk|
65
- lines = hunk.lines.select(&:addition?)
76
+ lines = hunk.lines
77
+ lines = lines.select { |l| l.public_send("#{type}?") } if type
66
78
  lines = lines.map { |l| yield(l, hunk, file, diff) } if block_given?
67
79
  lines
68
80
  end
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "paper-signs",
3
+ "private": true,
4
+ "workspaces": [
5
+ ".",
6
+ "packages/*"
7
+ ],
8
+ "scripts": {
9
+ "fix-css": "stylelint app/**/*.scss --syntax scss --fix",
10
+ "fix-js": "tslint --project tsconfig.json app/assets/javascripts/**/*.ts?\\(x\\) --fix",
11
+ "fix": "yarn fix-js && yarn fix-css",
12
+ "check-types": "tsc --noemit",
13
+ "lint": "yarn lint-js && yarn lint-css",
14
+ "lint-css": "stylelint app/**/*.scss --syntax scss",
15
+ "lint-js": "tslint --project tsconfig.json app/assets/javascripts/**/*.ts?\\(x\\)",
16
+ "analyze": "RAILS_ENV=production NODE_ENV=production bin/webpack --profile --json > tmp/stats.json && webpack-bundle-analyzer tmp/stats.json public/packs --exclude server_rendering -m static",
17
+ "test": "jest && NODE_ENV=test karma start --",
18
+ "test-css": "mocha app/assets/stylesheets/true.js",
19
+ "test-watch": "NODE_ENV=test karma start --auto-watch --no-single-run",
20
+ "prepare": "contentful-ts-generator --download=false && for d in packages/*/; do (cd $d; yarn prepare) || exit -1; done"
21
+ },
22
+ "engines": {},
23
+ "dependencies": {
24
+ "@babel/core": "^7.8.3",
25
+ "@babel/plugin-proposal-class-properties": "^7.8.3",
26
+ "@babel/preset-env": "^7.8.3",
27
+ "@babel/preset-react": "^7.8.3",
28
+ "@babel/preset-typescript": "^7.8.3",
29
+ "@rails/webpacker": "^4.0.2",
30
+ "@types/algoliasearch": "^3.30.1",
31
+ "@types/algoliasearch-helper": "^2.26.1",
32
+ "@types/bootstrap": "^4.3.1",
33
+ "@types/bugsnag-js": "^3.1.0",
34
+ "@types/chai": "^4.1.7",
35
+ "@types/chai-jquery": "^1.1.38",
36
+ "@types/enzyme": "^3.1.15",
37
+ "@types/enzyme-adapter-react-16": "^1.0.3",
38
+ "@types/fontfaceobserver": "^0.0.6",
39
+ "@types/i18n-js": "^3.0.1",
40
+ "@types/lodash": "^4.14.134",
41
+ "@types/mocha": "^5.2.5",
42
+ "@types/qs": "^6.5.3",
43
+ "@types/react": "^16.9.34",
44
+ "@types/react-dom": "^16.0.9",
45
+ "@types/react-infinite-scroller": "^1.2.1",
46
+ "@types/react-instantsearch": "^5.2.1",
47
+ "@types/sinon": "^7.0.3",
48
+ "@types/webpack-env": "^1.13.9",
49
+ "@watermarkchurch/contentful-migration": "^1.0.9",
50
+ "@watermarkchurch/react-instantsearch-components": "*",
51
+ "async-toolbox": "^0.6.6",
52
+ "babel-preset-react": "^6.24.1",
53
+ "contentful-export": "^7.4.0",
54
+ "contentful-shell": "^0.2.7",
55
+ "contentful-ts-generator": "^0.2.4",
56
+ "core-js": "3",
57
+ "date-fns": "^1.30.1",
58
+ "enzyme": "^3.7.0",
59
+ "enzyme-adapter-react-16": "^1.7.0",
60
+ "fontfaceobserver": "^2.1.0",
61
+ "html-react-parser": "^0.9.1",
62
+ "i18n-js": "^3.5.1",
63
+ "identity-obj-proxy": "^3.0.0",
64
+ "inflection": "^1.12.0",
65
+ "postcss-cssnext": "^3.1.0",
66
+ "prop-types": "^15.6.2",
67
+ "qs": "^6.7.0",
68
+ "react": "^16.12.0",
69
+ "react-calendar": "^2.19.0",
70
+ "react-dom": "^16.12.0",
71
+ "react-infinite-scroller": "^1.2.2",
72
+ "react-instantsearch": "^5.3.2",
73
+ "react-svg-loader": "^3.0.3",
74
+ "react_ujs": "^2.4.4",
75
+ "sinon": "^7.2.2",
76
+ "typescript": "^3.3.3"
77
+ },
78
+ "devDependencies": {
79
+ "@percy/agent": "^0.28.0",
80
+ "@types/jest": "^25.2.1",
81
+ "@watermarkchurch/contentful-check": "*",
82
+ "@watermarkchurch/load-tester": "*",
83
+ "babel-jest": "^25.5.0",
84
+ "chai": "^4.2.0",
85
+ "chai-jquery": "^2.0.0",
86
+ "graphql-schema-diff": "^0.6.3",
87
+ "hard-source-webpack-plugin": "^0.13.1",
88
+ "jest": "^25.5.0",
89
+ "karma": "^3.1.1",
90
+ "karma-chai": "^0.1.0",
91
+ "karma-chai-jquery": "^1.0.0",
92
+ "karma-chrome-launcher": "^2.2.0",
93
+ "karma-jquery": "^0.2.3",
94
+ "karma-junit-reporter": "^1.2.0",
95
+ "karma-mocha": "^1.3.0",
96
+ "karma-mocha-reporter": "^2.2.5",
97
+ "karma-sourcemap-loader": "^0.3.7",
98
+ "karma-webpack": "^3.0.5",
99
+ "lerna": "^3.13.1",
100
+ "mocha": "^5.2.0",
101
+ "react-test-renderer": "^16.12.0",
102
+ "sass-true": "^4.0.0",
103
+ "stylelint": "^9.6.0",
104
+ "stylelint-config-sass-guidelines": "^5.2.0",
105
+ "stylelint-scss": "^3.3.2",
106
+ "ts-node": "^7.0.1",
107
+ "tslint": "^5.11.0",
108
+ "tslint-eslint-rules": "^5.4.0",
109
+ "webpack-bundle-analyzer": "^3.0.3",
110
+ "webpack-dev-server": "^3.3.1"
111
+ }
112
+ }
@@ -0,0 +1,28 @@
1
+ diff --git a/package.json b/package.json
2
+ index 74d7ad8a..ea999d34 100644
3
+ --- a/package.json
4
+ +++ b/package.json
5
+ @@ -41,7 +41,6 @@
6
+ "@types/mocha": "^5.2.5",
7
+ "@types/qs": "^6.5.3",
8
+ "@types/react": "^16.9.34",
9
+ - "@types/react-autosuggest": "^9.3.6",
10
+ "@types/react-dom": "^16.0.9",
11
+ "@types/react-infinite-scroller": "^1.2.1",
12
+ "@types/react-instantsearch": "^5.2.1",
13
+ @@ -67,7 +66,6 @@
14
+ "prop-types": "^15.6.2",
15
+ "qs": "^6.7.0",
16
+ "react": "^16.12.0",
17
+ - "react-autosuggest": "^9.4.3",
18
+ "react-calendar": "^2.19.0",
19
+ "react-dom": "^16.12.0",
20
+ "react-infinite-scroller": "^1.2.2",
21
+ @@ -78,6 +76,7 @@
22
+ "typescript": "^3.3.3"
23
+ },
24
+ "devDependencies": {
25
+ + "@percy/agent": "^0.28.0",
26
+ "@types/jest": "^25.2.1",
27
+ "@watermarkchurch/contentful-check": "*",
28
+ "@watermarkchurch/load-tester": "*",
@@ -0,0 +1,13 @@
1
+ diff --git a/package.json b/package.json
2
+ index ea999d3..efa8440 100644
3
+ --- a/package.json
4
+ +++ b/package.json
5
+ @@ -69,7 +69,7 @@
6
+ "react-calendar": "^2.19.0",
7
+ "react-dom": "^16.12.0",
8
+ "react-infinite-scroller": "^1.2.2",
9
+ - "react-instantsearch": "^5.3.2",
10
+ + "react-instantsearch": "^5.3.3",
11
+ "react-svg-loader": "^3.0.3",
12
+ "react_ujs": "^2.4.4",
13
+ "sinon": "^7.2.2",
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "paper-signs",
3
+ "private": true,
4
+ "workspaces": [
5
+ ".",
6
+ "packages/*"
7
+ ],
8
+ "scripts": {
9
+ "fix-css": "stylelint app/**/*.scss --syntax scss --fix",
10
+ "fix-js": "tslint --project tsconfig.json app/assets/javascripts/**/*.ts?\\(x\\) --fix",
11
+ "fix": "yarn fix-js && yarn fix-css",
12
+ "check-types": "tsc --noemit",
13
+ "lint": "yarn lint-js && yarn lint-css",
14
+ "lint-css": "stylelint app/**/*.scss --syntax scss",
15
+ "lint-js": "tslint --project tsconfig.json app/assets/javascripts/**/*.ts?\\(x\\)",
16
+ "analyze": "RAILS_ENV=production NODE_ENV=production bin/webpack --profile --json > tmp/stats.json && webpack-bundle-analyzer tmp/stats.json public/packs --exclude server_rendering -m static",
17
+ "test": "jest && NODE_ENV=test karma start --",
18
+ "test-css": "mocha app/assets/stylesheets/true.js",
19
+ "test-watch": "NODE_ENV=test karma start --auto-watch --no-single-run",
20
+ "prepare": "contentful-ts-generator --download=false && for d in packages/*/; do (cd $d; yarn prepare) || exit -1; done"
21
+ },
22
+ "engines": {},
23
+ "dependencies": {
24
+ "@babel/core": "^7.8.3",
25
+ "@babel/plugin-proposal-class-properties": "^7.8.3",
26
+ "@babel/preset-env": "^7.8.3",
27
+ "@babel/preset-react": "^7.8.3",
28
+ "@babel/preset-typescript": "^7.8.3",
29
+ "@rails/webpacker": "^4.0.2",
30
+ "@types/algoliasearch": "^3.30.1",
31
+ "@types/algoliasearch-helper": "^2.26.1",
32
+ "@types/bootstrap": "^4.3.1",
33
+ "@types/bugsnag-js": "^3.1.0",
34
+ "@types/chai": "^4.1.7",
35
+ "@types/chai-jquery": "^1.1.38",
36
+ "@types/enzyme": "^3.1.15",
37
+ "@types/enzyme-adapter-react-16": "^1.0.3",
38
+ "@types/fontfaceobserver": "^0.0.6",
39
+ "@types/i18n-js": "^3.0.1",
40
+ "@types/lodash": "^4.14.134",
41
+ "@types/mocha": "^5.2.5",
42
+ "@types/qs": "^6.5.3",
43
+ "@types/react": "^16.9.34",
44
+ "@types/react-dom": "^16.0.9",
45
+ "@types/react-infinite-scroller": "^1.2.1",
46
+ "@types/react-instantsearch": "^5.2.1",
47
+ "@types/sinon": "^7.0.3",
48
+ "@types/webpack-env": "^1.13.9",
49
+ "@watermarkchurch/contentful-migration": "^1.0.9",
50
+ "@watermarkchurch/react-instantsearch-components": "*",
51
+ "async-toolbox": "^0.6.6",
52
+ "babel-preset-react": "^6.24.1",
53
+ "contentful-export": "^7.4.0",
54
+ "contentful-shell": "^0.2.7",
55
+ "contentful-ts-generator": "^0.2.4",
56
+ "core-js": "3",
57
+ "date-fns": "^1.30.1",
58
+ "enzyme": "^3.7.0",
59
+ "enzyme-adapter-react-16": "^1.7.0",
60
+ "fontfaceobserver": "^2.1.0",
61
+ "html-react-parser": "^0.9.1",
62
+ "i18n-js": "^3.5.1",
63
+ "identity-obj-proxy": "^3.0.0",
64
+ "inflection": "^1.12.0",
65
+ "postcss-cssnext": "^3.1.0",
66
+ "prop-types": "^15.6.2",
67
+ "qs": "^6.7.0",
68
+ "react": "^16.12.0",
69
+ "react-calendar": "^2.19.0",
70
+ "react-dom": "^16.12.0",
71
+ "react-infinite-scroller": "^1.2.2",
72
+ "react-instantsearch": "^5.3.3",
73
+ "react-svg-loader": "^3.0.3",
74
+ "react_ujs": "^2.4.4",
75
+ "sinon": "^7.2.2",
76
+ "typescript": "^3.3.3"
77
+ },
78
+ "devDependencies": {
79
+ "@percy/agent": "^0.28.0",
80
+ "@types/jest": "^25.2.1",
81
+ "@watermarkchurch/contentful-check": "*",
82
+ "@watermarkchurch/load-tester": "*",
83
+ "babel-jest": "^25.5.0",
84
+ "chai": "^4.2.0",
85
+ "chai-jquery": "^2.0.0",
86
+ "graphql-schema-diff": "^0.6.3",
87
+ "hard-source-webpack-plugin": "^0.13.1",
88
+ "jest": "^25.5.0",
89
+ "karma": "^3.1.1",
90
+ "karma-chai": "^0.1.0",
91
+ "karma-chai-jquery": "^1.0.0",
92
+ "karma-chrome-launcher": "^2.2.0",
93
+ "karma-jquery": "^0.2.3",
94
+ "karma-junit-reporter": "^1.2.0",
95
+ "karma-mocha": "^1.3.0",
96
+ "karma-mocha-reporter": "^2.2.5",
97
+ "karma-sourcemap-loader": "^0.3.7",
98
+ "karma-webpack": "^3.0.5",
99
+ "lerna": "^3.13.1",
100
+ "mocha": "^5.2.0",
101
+ "react-test-renderer": "^16.12.0",
102
+ "sass-true": "^4.0.0",
103
+ "stylelint": "^9.6.0",
104
+ "stylelint-config-sass-guidelines": "^5.2.0",
105
+ "stylelint-scss": "^3.3.2",
106
+ "ts-node": "^7.0.1",
107
+ "tslint": "^5.11.0",
108
+ "tslint-eslint-rules": "^5.4.0",
109
+ "webpack-bundle-analyzer": "^3.0.3",
110
+ "webpack-dev-server": "^3.3.1"
111
+ }
112
+ }