Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +35 -0
  3. data/.github/workflows/ci.yml +60 -0
  4. data/.gitignore +1 -2
  5. data/.rubocop.yml +3 -0
  6. data/.stylelintrc.json +8 -0
  7. data/CHANGELOG.md +5 -23
  8. data/Gemfile +4 -2
  9. data/Rakefile +4 -5
  10. data/app/controllers/workarea/admin/import_reviews_controller.rb +0 -35
  11. data/app/controllers/workarea/admin/reports_controller.decorator +10 -0
  12. data/app/helpers/workarea/admin/reviews_helper.rb +6 -5
  13. data/app/helpers/workarea/storefront/reviews_helper.rb +1 -3
  14. data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb +34 -0
  15. data/app/mailers/workarea/admin/status_report_mailer.decorator +2 -2
  16. data/app/models/workarea/insights/most_active_reviewers.rb +34 -0
  17. data/app/models/workarea/insights/most_reviewed_products.rb +33 -0
  18. data/app/models/workarea/insights/top_rated_products.rb +33 -0
  19. data/app/models/workarea/review.rb +5 -16
  20. data/app/queries/workarea/reports/reviews_by_product.rb +86 -0
  21. data/app/queries/workarea/reports/reviews_by_user.rb +71 -0
  22. data/app/seeds/{review_seeds.rb → workarea/review_seeds.rb} +0 -0
  23. data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator +11 -0
  24. data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb +27 -0
  25. data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
  26. data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
  27. data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
  28. data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
  29. data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
  30. data/app/views/workarea/storefront/products/_rating.html.haml +1 -2
  31. data/app/views/workarea/storefront/products/_reviews.html.haml +5 -5
  32. data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +2 -2
  33. data/app/views/workarea/storefront/review_mailer/review_request.html.haml +2 -0
  34. data/app/views/workarea/storefront/review_requests/show.html.haml +2 -1
  35. data/config/initializers/append_points.rb +5 -0
  36. data/config/initializers/configuration.rb +3 -1
  37. data/config/locales/en.yml +46 -0
  38. data/config/routes.rb +4 -0
  39. data/lib/workarea/reviews/engine.rb +4 -0
  40. data/lib/workarea/reviews/version.rb +1 -1
  41. data/package.json +9 -0
  42. data/test/dummy/config/initializers/session_store.rb +3 -1
  43. data/test/helpers/workarea/admin/reviews_helper_test.rb +0 -17
  44. data/test/helpers/workarea/storefront/reviews_helper_test.rb +1 -0
  45. data/test/integration/workarea/admin/reviews_integration_test.rb +5 -1
  46. data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
  47. data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
  48. data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
  49. data/test/models/workarea/review_test.rb +29 -18
  50. data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
  51. data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
  52. data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
  53. data/workarea-reviews.gemspec +1 -1
  54. data/yarn.lock +3265 -0
  55. metadata +31 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: cbc95c26a6972a59da6d0daa765491622783a60cfc8dd0961109b82839b0af77
4
- data.tar.gz: 9f62f3d6484cdf8002172106970954fa053784844a6fa6084b2aee3baadf8e7b
3
+ metadata.gz: 7135d1659f6d6564addc0f472fbf72a6225098a29a9de3e9a3c16af4724b364e
4
+ data.tar.gz: fdfe10e7e4e59e7bfb4b4c08f05dc2a8190acc36c2bd5602277c2298a2f4072e
5
5
SHA512:
6
- metadata.gz: e6913aca804eeb31ab4bb877b74b2826b50596e7b3a087eb162c49d46dc6df2a162ab8ca683e8251a1c056c798d12597294125acea2f102a91150baef4b31a4c
7
- data.tar.gz: ab487315635024cb2fcbb3b6cea388188b01ad31e0cfbcaf6e9e9513f35f1ad53c035eeb67927bda4796265bf6459eed6b14028bec6faf22066c3257907df5a1
6
+ metadata.gz: 294e2feacac1639da7c4b927fd9580f8c5e000bbd93885893a1c647cd980327ff8dd6d01d83db044cfc56da5dc71f2c62825deb07f71f85e042eeb59a54babb9
7
+ data.tar.gz: 014a4986c50d89dad1f932d8506d3171c776c91aedf83e959d6bab7e9a0d365255d31ce99aa960229189a959c1e744bb2c2ba889f46863677dd3abe4acfb2bb0
data/.eslintrc.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "extends": "eslint:recommended",
3
+ "rules": {
4
+ "semi": ["error", "always"],
5
+ "eqeqeq": ["error", "always"]
6
+ },
7
+ "globals": {
8
+ "window": true,
9
+ "document": true,
10
+ "WORKAREA": true,
11
+ "quot;: true,
12
+ "jQuery": true,
13
+ "_": true,
14
+ "feature": true,
15
+ "JST": true,
16
+ "Turbolinks": true,
17
+ "I18n": true,
18
+ "Chart": true,
19
+ "Dropzone": true,
20
+ "strftime": true,
21
+ "Waypoint": true,
22
+ "wysihtml": true,
23
+ "LocalTime": true,
24
+ "describe": true,
25
+ "after": true,
26
+ "afterEach": true,
27
+ "before": true,
28
+ "beforeEach": true,
29
+ "it": true,
30
+ "expect": true,
31
+ "sinon": true,
32
+ "fixture": true,
33
+ "chai": true
34
+ }
35
+ }
data/.github/workflows/ci.yml ADDED
@@ -0,0 +1,60 @@
1
+ name: CI
2
+ on: [push]
3
+
4
+ jobs:
5
+ static_analysis:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v1
9
+ - uses: workarea-commerce/ci/bundler-audit@v1
10
+ - uses: workarea-commerce/ci/rubocop@v1
11
+ - uses: workarea-commerce/ci/eslint@v1
12
+ with:
13
+ args: '**/*.js'
14
+ - uses: workarea-commerce/ci/stylelint@v1
15
+ with:
16
+ args: '**/*.scss'
17
+
18
+ admin_tests:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v1
22
+ - uses: actions/setup-ruby@v1
23
+ with:
24
+ ruby-version: 2.6.x
25
+ - uses: workarea-commerce/ci/test@v1
26
+ with:
27
+ command: bin/rails app:workarea:test:admin
28
+
29
+ core_tests:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v1
33
+ - uses: actions/setup-ruby@v1
34
+ with:
35
+ ruby-version: 2.6.x
36
+ - uses: workarea-commerce/ci/test@v1
37
+ with:
38
+ command: bin/rails app:workarea:test:core
39
+
40
+ storefront_tests:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v1
44
+ - uses: actions/setup-ruby@v1
45
+ with:
46
+ ruby-version: 2.6.x
47
+ - uses: workarea-commerce/ci/test@v1
48
+ with:
49
+ command: bin/rails app:workarea:test:storefront
50
+
51
+ plugins_tests:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v1
55
+ - uses: actions/setup-ruby@v1
56
+ with:
57
+ ruby-version: 2.6.x
58
+ - uses: workarea-commerce/ci/test@v1
59
+ with:
60
+ command: bin/rails app:workarea:test:plugins
data/.gitignore CHANGED
@@ -12,5 +12,4 @@ test/dummy/tmp/
12
12
test/dummy/public/
13
13
node_modules
14
14
test/reports
15
- package.json
15
+ .rubocop-http*
16
- yarn.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/workarea-commerce/workarea/master/.rubocop.yml
3
+
data/.stylelintrc.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "stylelint-config-recommended-scss",
3
+ "rules": {
4
+ "block-no-empty": null,
5
+ "no-descending-specificity": null,
6
+ "property-no-unknown": [true, { "ignoreProperties": ["mso-hide"] }]
7
+ }
8
+ }
data/CHANGELOG.md CHANGED
@@ -1,31 +1,13 @@
1
- Workarea Reviews 3.0.9 (2019-08-26)
1
+ Workarea Reviews 3.1.0 (2019-11-26)
2
2
--------------------------------------------------------------------------------
3
3
4
- * Only try to add schedule job if service connections are not skipped
4
+ * Updates for v3.5 compatibility
5
5
6
+ Ben Crouse
6
7
8
+ * Initial commit on master
7
9
8
- Workarea Reviews 3.0.8 (2019-08-21)
10
+ Curt Howard
9
- --------------------------------------------------------------------------------
10
-
11
- * Open Source!
12
-
13
-
14
-
15
- Workarea Reviews 3.0.7 (2019-06-11)
16
- --------------------------------------------------------------------------------
17
-
18
- * Add Rake Task for Reconciling Verified Purchasers
19
-
20
- Reviews can have a `:verified` badge associated with the content, but
21
- for those upgrading to a newer version some data needs to be changed in
22
- order to make this happen retroactively for older reviews. Add a Rake
23
- task for adding the `:verified` field to reviews where the user actually
24
- bought the product. For any reviews that are made after the upgrade,
25
- this will be automatically assigned as the review is being created.
26
-
27
- REVIEWS-146
28
- Tom Scott
29
11
30
12
31
13
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
1
source 'https://rubygems.org'
2
- git_source(:github) { |repo| "git@github.com:#{repo}.git" }
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
4
4
gemspec
5
5
6
- gem 'workarea'
6
+ gem 'byebug'
7
+
8
+ gem 'workarea', github: 'workarea-commerce/workarea'
data/Rakefile CHANGED
@@ -28,16 +28,15 @@ desc "Release version #{Workarea::Reviews::VERSION} of the gem"
28
28
task :release do
29
29
host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
30
30
31
- #Rake::Task['workarea:changelog'].execute
32
- #system 'git add CHANGELOG.md'
33
- #system 'git commit -m "Update CHANGELOG"'
34
- #system 'git push origin HEAD'
31
+ Rake::Task['workarea:changelog'].execute
32
+ system 'git add CHANGELOG.md'
33
+ system 'git commit -m "Update CHANGELOG"'
34
+ system 'git push origin HEAD'
35
35
36
36
system "git tag -a v#{Workarea::Reviews::VERSION} -m 'Tagging #{Workarea::Reviews::VERSION}'"
37
37
system 'git push --tags'
38
38
39
39
system 'gem build workarea-reviews.gemspec'
40
- system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
41
40
system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem --host #{host}"
42
41
system "rm workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
43
42
end
data/app/controllers/workarea/admin/import_reviews_controller.rb DELETED
@@ -1,35 +0,0 @@
1
- module Workarea
2
- module Admin
3
- class ImportReviewsController < Admin::ApplicationController
4
- required_permissions :marketing
5
-
6
- def new
7
- @import = Import::Review.new
8
- end
9
-
10
- def create
11
- @import = Import::Review.new(import_params)
12
-
13
- if @import.save
14
- ProcessImport.perform_async(@import.to_global_id)
15
-
16
- flash[:success] = t('workarea.admin.import_reviews.flash_messages.processing')
17
- redirect_to reviews_path
18
- else
19
- render :new, status: :unprocessable_entity
20
- end
21
- end
22
-
23
- def sample
24
- send_file Reviews::Engine.root.join('public/workarea/import_samples/reviews.csv')
25
- end
26
-
27
- private
28
-
29
- def import_params
30
- params.fetch(:import_review, {})
31
- .merge(created_by_id: current_user.id)
32
- end
33
- end
34
- end
35
- end
data/app/controllers/workarea/admin/reports_controller.decorator ADDED
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ decorate Admin::ReportsController, with: :reviews do
3
+ def reviews_by_product
4
+ @report = Admin::Reports::ReviewsByProductViewModel.wrap(
5
+ Workarea::Reports::ReviewsByProduct.new(params),
6
+ view_model_options
7
+ )
8
+ end
9
+ end
10
+ end
data/app/helpers/workarea/admin/reviews_helper.rb CHANGED
@@ -1,11 +1,12 @@
1
1
module Workarea
2
2
module Admin
3
3
module ReviewsHelper
4
- def reviewer_info(model)
5
- return model.user_info unless model.user_id.present?
6
-
7
- link_to model.user_info,
8
- edit_user_path(model.user_id)
4
+ def reviews_report_filter_options
5
+ [
6
+ [t('workarea.admin.reports.reviews_by_product.filters.all'), nil],
7
+ [t('workarea.admin.reports.reviews_by_product.filters.approved'), 'approved'],
8
+ [t('workarea.admin.reports.reviews_by_product.filters.unapproved'), 'unapproved']
9
+ ]
9
10
end
10
11
end
11
12
end
data/app/helpers/workarea/storefront/reviews_helper.rb CHANGED
@@ -6,10 +6,8 @@ module Workarea
6
6
empty_star_count = 5 - rating.ceil
7
7
half_star_size = (rating % 1).round(2) * 100
8
8
half_star_width = 20 + (half_star_size - 0) * (80.0 - 20) / (100.0 - 0)
9
- itemprop = options[:aggregate] ? 'aggregateRating' : 'reviewRating'
10
- itemtype = options[:aggregate] ? 'http://schema.org/AggregateRating' : 'http://schema.org/Rating'
11
9
12
- render 'workarea/storefront/products/rating', rating: rating, full_star_count: full_star_count, empty_star_count: empty_star_count, half_star_width: half_star_width, half_star_size: half_star_size, itemprop: itemprop, itemtype: itemtype
10
+ render 'workarea/storefront/products/rating', rating: rating, full_star_count: full_star_count, empty_star_count: empty_star_count, half_star_width: half_star_width, half_star_size: half_star_size
13
11
end
14
12
15
13
def display_purchase_requirement_message
data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb ADDED
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module Storefront
3
+ module ReviewsSchemaOrgHelper
4
+ def product_schema(product, related_products: nil)
5
+ schema = super.merge(
6
+ 'review': product.reviews.map do |review|
7
+ {
8
+ '@type': 'Review',
9
+ 'author': review.user_info,
10
+ 'datePublished': review.created_at.strftime('%Y-%m-%d'),
11
+ 'description': review.body,
12
+ 'name': review.title,
13
+ 'reviewRating': {
14
+ '@type': 'Rating',
15
+ 'bestRating': '5',
16
+ 'worstRating': '1',
17
+ 'ratingValue': review.rating.round(2).to_s
18
+ }
19
+ }
20
+ end
21
+ )
22
+
23
+ if product.total_reviews > 0 && product.average_rating.present?
24
+ schema['aggregateRating'] = {
25
+ 'reviewCount': product.total_reviews.to_s,
26
+ 'ratingValue': product.average_rating.round(2).to_s
27
+ }
28
+ end
29
+
30
+ schema
31
+ end
32
+ end
33
+ end
34
+ end
data/app/mailers/workarea/admin/status_report_mailer.decorator CHANGED
@@ -1,7 +1,7 @@
1
1
module Workarea
2
2
decorate Admin::StatusReportMailer, with: :reviews do
3
- def report(email, date)
4
- @reviews_summary = ReviewSummary.new
3
+ def report(*)
4
+ @reviews_summary = ReviewSummary.new
5
5
super
6
6
end
7
7
end
data/app/models/workarea/insights/most_active_reviewers.rb ADDED
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module Insights
3
+ class MostActiveReviewers < Base
4
+ class << self
5
+ def dashboards
6
+ %w(people marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_users_list_max_results)
19
+ .map { |result| result.merge(email: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByUser.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'activity_score',
27
+ sort_direction: 'desc',
28
+ results_filter: 'approved'
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
data/app/models/workarea/insights/most_reviewed_products.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ module Insights
3
+ class MostReviewedProducts < Base
4
+ class << self
5
+ def dashboards
6
+ %w(marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_products_list_max_results)
19
+ .map { |result| result.merge(product_id: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByProduct.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'reviews',
27
+ sort_direction: 'desc'
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
data/app/models/workarea/insights/top_rated_products.rb ADDED
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ module Insights
3
+ class TopRatedProducts < Base
4
+ class << self
5
+ def dashboards
6
+ %w(marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_products_list_max_results)
19
+ .map { |result| result.merge(product_id: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByProduct.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'weighted_average_rating',
27
+ sort_direction: 'desc'
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
data/app/models/workarea/review.rb CHANGED
@@ -95,23 +95,12 @@ module Workarea
95
95
# @return [Float]
96
96
#
97
97
def self.find_sorting_score(product_id)
98
- reviews = find_for_product(product_id)
99
-
100
- votes = [ reviews.select { |r| r.rating == 1 }.length,
101
- reviews.select { |r| r.rating == 2 }.length,
98
+ reviews = find_for_product(product_id).to_a
99
+ count = (1..5).each_with_object({}) do |i, memo|
100
+ memo[i] = reviews.select { |r| r.rating == i }.length + 2
101
+ end
102
- reviews.select { |r| r.rating == 3 }.length,
103
- reviews.select { |r| r.rating == 4 }.length,
104
- reviews.select { |r| r.rating == 5 }.length ]
105
-
106
- prior = [2, 2, 2, 2, 2]
107
-
108
- posterior = votes.zip(prior).map { |a, b| a + b }
109
- sum = posterior.inject { |a, b| a + b }
110
102
111
- posterior.
103
+ count.sum { |rating, count| rating * count }.to_f / count.values.sum
112
- map.with_index { |v, i| (i + 1) * v }.
113
- inject { |a, b| a + b }.
114
- to_f / sum
115
104
end
116
105
117
106
def anonymous?
data/app/queries/workarea/reports/reviews_by_product.rb ADDED
@@ -0,0 +1,86 @@
1
+ module Workarea
2
+ module Reports
3
+ class ReviewsByProduct
4
+ include Report
5
+
6
+ self.reporting_class = Review
7
+ self.sort_fields = %w(reviews verified average_rating weighted_average_rating)
8
+
9
+ def aggregation
10
+ [filter_results, project_used_fields, group_by_product, project_averages]
11
+ end
12
+
13
+ def filter_results
14
+ {
15
+ '$match' => {
16
+ 'created_at' => { '$gte' => starts_at, '$lte' => ends_at },
17
+ **approval_query
18
+ }
19
+ }
20
+ end
21
+
22
+ def project_used_fields
23
+ {
24
+ '$project' => {
25
+ 'product_id' => 1,
26
+ 'rating' => 1,
27
+ 'verified' => 1
28
+ }
29
+ }
30
+ end
31
+
32
+ def group_by_product
33
+ {
34
+ '$group' => {
35
+ '_id' => '$product_id',
36
+ 'reviews' => { '$sum' => 1 },
37
+ 'verified' => { '$sum' => { '$cond' => { 'if' => '$verified', 'then' => 1, 'else' => 0 } } },
38
+ 'rating_tally' => { '$sum' => '$rating' },
39
+ 'rated_5' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 5] }, 'then' => 1, 'else' => 0 } } },
40
+ 'rated_4' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 4] }, 'then' => 1, 'else' => 0 } } },
41
+ 'rated_3' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 3] }, 'then' => 1, 'else' => 0 } } },
42
+ 'rated_2' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 2] }, 'then' => 1, 'else' => 0 } } },
43
+ 'rated_1' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 1] }, 'then' => 1, 'else' => 0 } } }
44
+ }
45
+ }
46
+ end
47
+
48
+ def project_averages
49
+ {
50
+ '$project' => {
51
+ 'product_id' => 1,
52
+ 'reviews' => 1,
53
+ 'verified' => 1,
54
+ 'average_rating' => { '$divide' => ['$rating_tally', '$reviews'] },
55
+ 'weighted_average_rating' => {
56
+ '$divide' => [
57
+ {
58
+ '$sum' => [
59
+ { '$multiply' => [{ '$sum' => [2, '$rated_5'] }, 5] },
60
+ { '$multiply' => [{ '$sum' => [2, '$rated_4'] }, 4] },
61
+ { '$multiply' => [{ '$sum' => [2, '$rated_3'] }, 3] },
62
+ { '$multiply' => [{ '$sum' => [2, '$rated_2'] }, 2] },
63
+ { '$multiply' => [{ '$sum' => [2, '$rated_1'] }, 1] }
64
+ ]
65
+ },
66
+ { '$sum' => [10, '$rated_5', '$rated_4', '$rated_3', '$rated_2', '$rated_1'] }
67
+ ]
68
+ }
69
+ }
70
+ }
71
+ end
72
+
73
+ private
74
+
75
+ def approval_query
76
+ if params[:results_filter] == 'approved'
77
+ { approved: true }
78
+ elsif params[:results_filter] == 'unapproved'
79
+ { approved: false }
80
+ else
81
+ {}
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
data/app/queries/workarea/reports/reviews_by_user.rb ADDED
@@ -0,0 +1,71 @@
1
+ module Workarea
2
+ module Reports
3
+ class ReviewsByUser
4
+ include Report
5
+
6
+ self.reporting_class = Review
7
+ self.sort_fields = %w(reviews verified average_rating activity_score)
8
+
9
+ def aggregation
10
+ [filter_results, project_used_fields, group_by_email, project_fields]
11
+ end
12
+
13
+ def filter_results
14
+ {
15
+ '$match' => {
16
+ 'created_at' => { '$gte' => starts_at, '$lte' => ends_at },
17
+ **approval_query
18
+ }
19
+ }
20
+ end
21
+
22
+ def project_used_fields
23
+ {
24
+ '$project' => {
25
+ 'email' => 1,
26
+ 'user_id' => 1,
27
+ 'rating' => 1,
28
+ 'verified' => 1
29
+ }
30
+ }
31
+ end
32
+
33
+ def group_by_email
34
+ {
35
+ '$group' => {
36
+ '_id' => '$email',
37
+ 'user_id' => { '$max' => '$user_id' },
38
+ 'reviews' => { '$sum' => 1 },
39
+ 'verified' => { '$sum' => { '$cond' => { 'if' => '$verified', 'then' => 1, 'else' => 0 } } },
40
+ 'rating_tally' => { '$sum' => '$rating' }
41
+ }
42
+ }
43
+ end
44
+
45
+ def project_fields
46
+ {
47
+ '$project' => {
48
+ 'email' => 1,
49
+ 'user_id' => 1,
50
+ 'reviews' => 1,
51
+ 'verified' => 1,
52
+ 'average_rating' => { '$divide' => ['$rating_tally', '$reviews'] },
53
+ 'activity_score' => { '$sum' => ['$reviews', { '$multiply' => ['$verified', 0.75] }] }
54
+ }
55
+ }
56
+ end
57
+
58
+ private
59
+
60
+ def approval_query
61
+ if params[:results_filter] == 'approved'
62
+ { approved: true }
63
+ elsif params[:results_filter] == 'unapproved'
64
+ { approved: false }
65
+ else
66
+ {}
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
data/app/seeds/{review_seeds.rb → workarea/review_seeds.rb} RENAMED
File without changes
data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator ADDED
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ decorate Admin::Dashboards::ReportsViewModel, with: :reviews do
3
+ def reviews_by_product
4
+ @reviews_by_product ||=
5
+ Admin::Reports::ReviewsByProductViewModel.wrap(
6
+ Workarea::Reports::ReviewsByProduct.new(options),
7
+ options
8
+ )
9
+ end
10
+ end
11
+ end
data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Workarea
2
+ module Admin
3
+ module Reports
4
+ class ReviewsByProductViewModel < ApplicationViewModel
5
+ def results
6
+ @results ||= model.results.map do |result|
7
+ product = products.detect { |p| p.id == result['_id'] }
8
+ OpenStruct.new(
9
+ { product: product }
10
+ .merge(result)
11
+ .merge(
12
+ average_rating: result['average_rating'].round(2),
13
+ weighted_average_rating: result['weighted_average_rating'].round(2)
14
+ )
15
+ )
16
+ end
17
+ end
18
+
19
+ def products
20
+ @products ||= Catalog::Product.any_in(
21
+ id: model.results.map { |r| r['_id'] }
22
+ ).to_a
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml ADDED
@@ -0,0 +1,17 @@
1
+ .grid__cell
2
+ .card{ class: card_classes(:reviews_by_product_report, local_assigns[:active]) }
3
+ = link_to reviews_by_product_report_path, class: 'card__header' do
4
+ %span.card__header-text= t('workarea.admin.reports.reviews_by_product.title')
5
+ = inline_svg 'workarea/admin/icons/insights.svg', class: 'card__icon'
6
+
7
+ .card__body
8
+ .card__centered-content
9
+ %table
10
+ %tbody
11
+ - @dashboard.reviews_by_product.results.take(4).each do |result|
12
+ %tr
13
+ %td= result.product.present? ? result.product.name : result._id
14
+ %td.align-right= number_with_delimiter result.reviews
15
+
16
+ = link_to reviews_by_product_report_path, class: 'card__button' do
17
+ %span.button.button--small= t('workarea.admin.dashboards.reports.view_full_report')
data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml ADDED
@@ -0,0 +1,22 @@
1
+ .insight
2
+ .insight__date
3
+ %span.insight__period= insight.reporting_on.strftime('%B %Y')
4
+ .insight__heading= t('workarea.admin.insights.most_active_reviewers.title')
5
+ .insight__body
6
+ %p.insight__note= t('workarea.admin.insights.most_active_reviewers.info')
7
+ %table
8
+ %thead
9
+ %tr
10
+ %th.align-center= t('workarea.admin.insights.most_active_reviewers.user')
11
+ %th.align-center= t('workarea.admin.insights.most_active_reviewers.reviews')
12
+ %th.align-center= t('workarea.admin.insights.most_active_reviewers.verified')
13
+ %tbody
14
+ - insight.results.each do |result|
15
+ %tr
16
+ %td.align-center
17
+ - if result.user.present?
18
+ = link_to result.user.name, user_path(result.user)
19
+ - else
20
+ = result.email
21
+ %td.align-center= number_with_delimiter result.reviews
22
+ %td.align-center= number_with_delimiter result.verified
data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml ADDED
@@ -0,0 +1,22 @@
1
+ .insight
2
+ .insight__date
3
+ %span.insight__period= insight.reporting_on.strftime('%B %Y')
4
+ .insight__heading= t('workarea.admin.insights.most_reviewed_products.title')
5
+ .insight__body
6
+ %p.insight__note= t('workarea.admin.insights.most_reviewed_products.info')
7
+ .grid.grid--large.grid--center
8
+ - insight.results.each do |result|
9
+ .grid__cell.grid__cell--50.grid__cell--25-at-wide.align-center
10
+ .insight__product
11
+ - if result.product.blank?
12
+ = image_tag(product_image_url(Workarea::Catalog::ProductPlaceholderImage.cached, :small), alt: result.product_id, class: 'insight__product-image')
13
+ - else
14
+ = link_to catalog_product_path(result.product) do
15
+ = image_tag(product_image_url(result.product.primary_image, :small), alt: result.product.name, class: 'insight__product-image')
16
+ .insight__product-name
17
+ - if result.product.blank?
18
+ = result.product_id
19
+ - else
20
+ = link_to result.product.name, catalog_product_path(result.product)
21
+ .insight__product-info
22
+ %strong= t('workarea.admin.insights.most_reviewed_products.reviews', count: result.reviews)
data/app/views/workarea/admin/insights/_top_rated_products.html.haml ADDED
@@ -0,0 +1,22 @@
1
+ .insight
2
+ .insight__date
3
+ %span.insight__period= insight.reporting_on.strftime('%B %Y')
4
+ .insight__heading= t('workarea.admin.insights.top_rated_products.title')
5
+ .insight__body
6
+ %p.insight__note= t('workarea.admin.insights.top_rated_products.info')
7
+ .grid.grid--large.grid--center
8
+ - insight.results.each do |result|
9
+ .grid__cell.grid__cell--50.grid__cell--25-at-wide.align-center
10
+ .insight__product
11
+ - if result.product.blank?
12
+ = image_tag(product_image_url(Workarea::Catalog::ProductPlaceholderImage.cached, :small), alt: result.product_id, class: 'insight__product-image')
13
+ - else
14
+ = link_to catalog_product_path(result.product) do
15
+ = image_tag(product_image_url(result.product.primary_image, :small), alt: result.product.name, class: 'insight__product-image')
16
+ .insight__product-name
17
+ - if result.product.blank?
18
+ = result.product_id
19
+ - else
20
+ = link_to result.product.name, catalog_product_path(result.product)
21
+ .insight__product-info
22
+ %strong= t('workarea.admin.insights.top_rated_products.rating', rating: number_with_delimiter(result.weighted_average_rating.round(2)))
data/app/views/workarea/admin/reports/reviews_by_product.html.haml ADDED
@@ -0,0 +1,44 @@
1
+ - @page_title = t('workarea.admin.reports.reviews_by_product.title')
2
+
3
+ .view
4
+ .view__header
5
+ .view__heading
6
+ = link_to "↑ #{t('workarea.admin.reports.all_reports')}", reports_dashboards_path
7
+ %h1.heading.heading--no-margin= t('workarea.admin.reports.reviews_by_product.title')
8
+ %p= t('workarea.admin.reports.reference_link_html', path: reference_report_path)
9
+
10
+ .view__container.view__container--narrow
11
+ .browsing-controls.browsing-controls--with-divider.browsing-controls--center.browsing-controls--filters-displayed
12
+ = form_tag reviews_by_product_report_path, method: 'get', class: 'browsing-controls__form' do
13
+ = render 'workarea/admin/shared/date_selector', starts_at: @report.starts_at, ends_at: @report.ends_at
14
+
15
+ .browsing-controls__filter
16
+ .property.property--inline
17
+ = label_tag 'results_filter', t('workarea.admin.reports.reviews_by_product.results_filter'), class: 'property__name'
18
+ = select_tag 'results_filter', options_for_select(reviews_report_filter_options, params[:results_filter]), data: { form_submitting_control: '' }
19
+
20
+
21
+ .browsing-controls__count
22
+ = render_reports_results_message(@report)
23
+ = render 'workarea/admin/reports/export', report: @report
24
+
25
+ %table
26
+ %thead
27
+ %tr
28
+ %th= t('workarea.admin.fields.product')
29
+ %th.align-center= link_to_reports_sorting t('workarea.admin.fields.reviews'), report: @report, sort_by: 'reviews'
30
+ %th.align-center= link_to_reports_sorting t('workarea.admin.fields.verified_reviews'), report: @report, sort_by: 'verified'
31
+ %th.align-center= link_to_reports_sorting t('workarea.admin.fields.average_rating'), report: @report, sort_by: 'average_rating'
32
+ %th.align-center= link_to_reports_sorting t('workarea.admin.fields.weighted_average_rating'), report: @report, sort_by: 'weighted_average_rating'
33
+ %tbody
34
+ - @report.results.each do |result|
35
+ %tr
36
+ %td
37
+ - if result.product.present?
38
+ = link_to result.product.name, catalog_product_path(result.product)
39
+ - else
40
+ = result._id
41
+ %td.align-center= number_with_delimiter result.reviews
42
+ %td.align-center= number_with_delimiter result.verified
43
+ %td.align-center= number_with_delimiter result.average_rating
44
+ %td.align-center= number_with_delimiter result.weighted_average_rating
data/app/views/workarea/storefront/products/_rating.html.haml CHANGED
@@ -1,4 +1,4 @@
1
- %p.rating{ title: "#{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}", itemprop: itemprop, itemscope: true, itemtype: itemtype }
1
+ %p.rating{ title: "#{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}" }
2
2
- full_star_count.times do
3
3
= inline_svg 'workarea/storefront/icons/star.svg', class: 'rating__star'
4
4
- if half_star_size > 0
@@ -6,5 +6,4 @@
6
6
- empty_star_count.times do
7
7
= inline_svg 'workarea/storefront/icons/empty_star.svg', class: 'rating__star'
8
8
%span.rating__text.visually-hidden
9
- %meta{ itemprop: 'ratingValue', content: rating.round(2) }
10
9
#{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}
data/app/views/workarea/storefront/products/_reviews.html.haml CHANGED
@@ -10,14 +10,14 @@
10
10
11
11
%ol.reviews__review-group
12
12
- product.reviews.each do |review|
13
- %li.reviews__review{ itemprop: 'review', itemscope: true, itemtype: 'http://schema.org/Review', data: { product_review_section_entry: { rating: review.rating.to_f, createdAt: review.created_at.to_i }.to_json } }
13
+ %li.reviews__review{ data: { product_review_section_entry: { rating: review.rating.to_f, createdAt: review.created_at.to_i }.to_json } }
14
14
= rating_stars(review.rating)
15
15
16
- %h3.reviews__review-title{ itemprop: 'name' }= review.title
17
- %p.reviews__review-body{ itemprop: 'reviewBody' }= review.body
16
+ %h3.reviews__review-title= review.title
17
+ %p.reviews__review-body= review.body
18
18
.reviews__review-meta
19
- %p.reviews__review-author{ itemprop: 'author', itemscope: true, itemtype: 'http://schema.org/Person' }
20
- %span{ itemprop: 'name' }= review.user_info
19
+ %p.reviews__review-author
20
+ %span= review.user_info
21
21
- if review.verified?
22
22
%p.reviews__review-verified= t('workarea.storefront.reviews.verified_purchaser')
23
23
%p.reviews__review-date= local_time(review.created_at, format: :long, itemprop: 'datePublished')
data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml CHANGED
@@ -1,5 +1,5 @@
1
1
- if product.has_reviews?
2
- .reviews-aggregate{ itemprop: 'aggregateRating', itemscope: true, itemtype: 'http://schema.org/AggregateRating' }
2
+ .reviews-aggregate
3
3
= link_to "#{product_path(product, product.browse_link_options)}#reviews", data: { scroll_to_button: '' }, class: 'reviews-aggregate__rating-link' do
4
4
= rating_stars(product.average_rating, aggregate: true)
5
5
@@ -7,7 +7,7 @@
7
7
%span.reviews-aggregate__count
8
8
%span.reviews-aggregate__read= t('workarea.storefront.reviews.read_reviews')
9
9
= surround '(', ')' do
10
- %span{ itemprop: 'reviewCount' }= product.total_reviews
10
+ %span= product.total_reviews
11
11
12
12
= link_to t('workarea.storefront.reviews.write_a_review'), new_product_review_path(product), class: 'reviews-aggregate__write-action', data: { dialog_button: '' }
13
13
- if display_purchase_requirement_message
data/app/views/workarea/storefront/review_mailer/review_request.html.haml CHANGED
@@ -1,3 +1,5 @@
1
+ = render_schema_org(email_action_schema(review_request_url(@request.token), t('workarea.storefront.email.review_request.email_action.name'), t('workarea.storefront.email.review_request.email_action.description')))
2
+
1
3
- content_for :preheader_text do
2
4
= @content
3
5
data/app/views/workarea/storefront/review_requests/show.html.haml CHANGED
@@ -5,7 +5,8 @@
5
5
6
6
.grid
7
7
.grid__cell.grid__cell--25
8
- .product-summary{ itemscope: true, itemtype: 'http://schema.org/Product' }
8
+ .product-summary
9
+ = render_schema_org(product_schema(@product))
9
10
= render 'workarea/storefront/products/summary', product: @product
10
11
11
12
.grid__cell.grid__cell--75
data/config/initializers/append_points.rb CHANGED
@@ -67,4 +67,9 @@ module Workarea
67
67
'admin.marketing_dashboard_navigation',
68
68
'workarea/admin/reviews/dashboard_navigation'
69
69
)
70
+
71
+ Workarea.append_partials(
72
+ 'admin.reports_dashboard',
73
+ 'workarea/admin/dashboards/reviews_by_product_card'
74
+ )
70
75
end
data/config/initializers/configuration.rb CHANGED
@@ -1,5 +1,5 @@
1
1
Workarea.configure do |config|
2
- config.seeds << 'Workarea::ReviewSeeds'
2
+ config.seeds.insert('Workarea::InsightsSeeds', 'Workarea::ReviewSeeds')
3
3
4
4
config.jump_to_navigation.merge!('Product Reviews' => :reviews_path)
5
5
@@ -11,6 +11,8 @@ Workarea.configure do |config|
11
11
12
12
config.data_file_ignored_fields << %w(total_reviews average_rating)
13
13
14
+ config.insights_model_classes << 'Workarea::User'
15
+
14
16
# The amount of time before a Review::Request will auto expire
15
17
# and be removed from the collection
16
18
config.review_request_ttl = 6.months
data/config/locales/en.yml CHANGED
@@ -15,12 +15,55 @@ en:
15
15
marketing:
16
16
reviews: Product reviews
17
17
fields:
18
+ average_rating: Average Rating
18
19
body: Body
19
20
title: Title
20
21
rating: Rating
22
+ reviews: Reviews
21
23
approved: Approved
22
24
auto_approve: Approve All Reviews
23
25
verified: Verified Purchaser
26
+ verified_reviews: Verified
27
+ weighted_average_rating: Weighted Average Rating
28
+ insights:
29
+ most_active_reviewers:
30
+ info: Your customers that are submitting the most reviews.
31
+ reviews: Reviews
32
+ title: Most Active Reviewers
33
+ user: User
34
+ verified: Verified Reviews
35
+ most_reviewed_products:
36
+ title: Most Reviewed Products
37
+ info: Your products that have received the most reviews.
38
+ reviews:
39
+ one: '%{count} Review'
40
+ other: '%{count} Reviews'
41
+ top_rated_products:
42
+ title: Top Rated Products
43
+ info: Your products with the highest weighted average rating.
44
+ rating: '%{rating} Rating'
45
+ reports:
46
+ reference:
47
+ terms:
48
+ average_rating:
49
+ name: Average Rating
50
+ description: The sum of all review ratings divided by the total number of reviews.
51
+ reviews:
52
+ name: Reviews
53
+ description: The total number of reviews.
54
+ verified:
55
+ name: Verified
56
+ description: The total number of reviews made by customers who have purchased the product.
57
+ weighted_average_rating:
58
+ name: Weighted Average Rating
59
+ description: A base rating of 3.0, adjusted up or down by each review rating.
60
+ reviews_by_product:
61
+ filters:
62
+ all: All Reviews
63
+ approved: Approved Reviews
64
+ unapproved: Unapproved Reviews
65
+ results_filter: Results Filter
66
+ title: Reviews By Product
24
67
reviews:
25
68
approved: Approved
26
69
catalog_products_cards:
@@ -66,6 +109,9 @@ en:
66
109
review_request:
67
110
subject: Review your recent purchase of %{product}
68
111
link: Write a review
112
+ email_action:
113
+ name: Write Review
114
+ description: Please review your recent purchase
69
115
fields:
70
116
username: Username
71
117
review_requests:
data/config/routes.rb CHANGED
@@ -6,6 +6,10 @@ Workarea::Admin::Engine.routes.draw do
6
6
get :reviews
7
7
end
8
8
end
9
+
10
+ resource :report, only: [] do
11
+ get :reviews_by_product
12
+ end
9
13
end
10
14
end
11
15
data/lib/workarea/reviews/engine.rb CHANGED
@@ -5,7 +5,11 @@ module Workarea
5
5
isolate_namespace Workarea::Reviews
6
6
7
7
config.to_prepare do
8
+ Admin::ApplicationController.helper(Admin::ReviewsHelper)
8
9
Storefront::ApplicationController.helper(Storefront::ReviewsHelper)
10
+ Workarea::Storefront::ApplicationController.helper(
11
+ Workarea::Storefront::ReviewsSchemaOrgHelper
12
+ )
9
13
end
10
14
end
11
15
end
data/lib/workarea/reviews/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
module Workarea
2
2
module Reviews
3
- VERSION = '3.0.10'.freeze
3
+ VERSION = '3.1.0'.freeze
4
4
end
5
5
end
data/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "devDependencies": {
3
+ "eslint": "~5.16.0",
4
+ "eslint-config-recommended": "~4.0.0",
5
+ "stylelint": "~10.1.0",
6
+ "stylelint-config-recommended-scss": "~3.3.0",
7
+ "stylelint-scss": "~3.9.2"
8
+ }
9
+ }
data/test/dummy/config/initializers/session_store.rb CHANGED
@@ -1,3 +1,5 @@
1
1
# Be sure to restart your server when you modify this file.
2
2
3
- Rails.application.config.session_store :cookie_store, key: '_dummy_session'
3
+ Rails.application.config.session_store :cookie_store,
4
+ key: '_dummy_session',
5
+ expire_after: 30.minutes
data/test/helpers/workarea/admin/reviews_helper_test.rb DELETED
@@ -1,17 +0,0 @@
1
- require 'test_helper'
2
-
3
- module Workarea
4
- module Admin
5
- class ReviewsHelperTest < ViewTest
6
- def test_reviewer_info_only_shows_link_when_user_is_present
7
- review = create_review(
8
- user_id: nil,
9
- product_id: create_product.id,
10
- user_info: 'Userless User'
11
- )
12
-
13
- assert_equal('Userless User', reviewer_info(review))
14
- end
15
- end
16
- end
17
- end
data/test/helpers/workarea/storefront/reviews_helper_test.rb CHANGED
@@ -4,6 +4,7 @@ module Workarea
4
4
module Storefront
5
5
class ReviewsHelperTest < ViewTest
6
6
def test_rating_stars_displays_correct_rating
7
+ assert_match(/1 out of 5 stars/, rating_stars(1))
7
8
assert_match(/1(?:\.0)? out of 5 stars/, rating_stars(1))
8
9
assert_match(/2\.5 out of 5 stars/, rating_stars(2.5))
9
10
assert_match(/4\.25 out of 5 stars/, rating_stars(4.251))
data/test/integration/workarea/admin/reviews_integration_test.rb CHANGED
@@ -6,7 +6,11 @@ module Workarea
6
6
include Admin::IntegrationTest
7
7
8
8
def test_updating_a_review
9
- review = create_review(product_id: product.id)
9
+ review = create_review(
10
+ product_id: product.id,
11
+ rating: 4,
12
+ approved: false
13
+ )
10
14
11
15
put admin.review_path(review), params: {
12
16
review: { body: 'foo bar', approved: true }
data/test/models/workarea/insights/most_active_reviewers_test.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Insights
5
+ class MostActiveReviewersTest < TestCase
6
+ setup :add_data, :time_travel
7
+
8
+ def add_data
9
+ travel_to Time.zone.local(2018, 10, 27)
10
+
11
+ 5.times { create_review(email: 'foo@workarea.com', approved: true, verified: true) }
12
+ 7.times { create_review(email: 'bar@workarea.com', approved: true) }
13
+ end
14
+
15
+ def time_travel
16
+ travel_to Time.zone.local(2018, 11, 1)
17
+ end
18
+
19
+ def test_generate_monthly!
20
+ MostActiveReviewers.generate_monthly!
21
+ assert_equal(1, MostActiveReviewers.count)
22
+
23
+ reviewers = MostActiveReviewers.first
24
+ assert_equal(2, reviewers.results.size)
25
+ assert_equal('foo@workarea.com', reviewers.results.first['email'])
26
+ assert_in_delta(5, reviewers.results.first['reviews'])
27
+ assert_equal('bar@workarea.com', reviewers.results.second['email'])
28
+ assert_in_delta(7, reviewers.results.second['reviews'])
29
+ end
30
+ end
31
+ end
32
+ end
data/test/models/workarea/insights/most_reviewed_products_test.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Insights
5
+ class MostReviewedProductsTest < TestCase
6
+ setup :add_data, :time_travel
7
+
8
+ def add_data
9
+ travel_to Time.zone.local(2018, 10, 27)
10
+
11
+ 6.times { create_review(product_id: 'foo', approved: true) }
12
+ 12.times { create_review(product_id: 'bar', approved: true) }
13
+ end
14
+
15
+ def time_travel
16
+ travel_to Time.zone.local(2018, 11, 1)
17
+ end
18
+
19
+ def test_generate_monthly!
20
+ MostReviewedProducts.generate_monthly!
21
+ assert_equal(1, MostReviewedProducts.count)
22
+
23
+ reviewed_products = MostReviewedProducts.first
24
+ assert_equal(2, reviewed_products.results.size)
25
+ assert_equal('bar', reviewed_products.results.first['product_id'])
26
+ assert_in_delta(12, reviewed_products.results.first['reviews'])
27
+ assert_equal('foo', reviewed_products.results.second['product_id'])
28
+ assert_in_delta(6, reviewed_products.results.second['reviews'])
29
+ end
30
+ end
31
+ end
32
+ end
data/test/models/workarea/insights/top_rated_products_test.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Insights
5
+ class TopRatedProductsTest < TestCase
6
+ setup :add_data, :time_travel
7
+
8
+ def add_data
9
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 27))
10
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 28))
11
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 28))
12
+ create_review(product_id: 'foo', rating: 5, created_at: Time.zone.local(2018, 10, 29))
13
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 29))
14
+ create_review(product_id: 'foo', rating: 2, created_at: Time.zone.local(2018, 10, 29))
15
+
16
+ create_review(product_id: 'bar', rating: 3, created_at: Time.zone.local(2018, 10, 27))
17
+ create_review(product_id: 'bar', rating: 4, created_at: Time.zone.local(2018, 10, 27))
18
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 27))
19
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 28))
20
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 28))
21
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 28))
22
+ create_review(product_id: 'bar', rating: 4, created_at: Time.zone.local(2018, 10, 28))
23
+ create_review(product_id: 'bar', rating: 4, created_at: Time.zone.local(2018, 10, 29))
24
+ create_review(product_id: 'bar', rating: 4, created_at: Time.zone.local(2018, 10, 29))
25
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 29))
26
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 29))
27
+ create_review(product_id: 'bar', rating: 3, created_at: Time.zone.local(2018, 10, 29))
28
+ end
29
+
30
+ def time_travel
31
+ travel_to Time.zone.local(2018, 11, 1)
32
+ end
33
+
34
+ def test_generate_monthly!
35
+ TopRatedProducts.generate_monthly!
36
+ assert_equal(1, TopRatedProducts.count)
37
+
38
+ rated_products = TopRatedProducts.first
39
+ assert_equal(2, rated_products.results.size)
40
+ assert_equal('bar', rated_products.results.first['product_id'])
41
+ assert_equal(4.33, rated_products.results.first['average_rating'].round(2))
42
+ assert_equal(3.73, rated_products.results.first['weighted_average_rating'].round(2))
43
+ assert_equal('foo', rated_products.results.second['product_id'])
44
+ assert_equal(3.17, rated_products.results.second['average_rating'].round(2))
45
+ assert_equal(3.06, rated_products.results.second['weighted_average_rating'].round(2))
46
+ end
47
+ end
48
+ end
49
+ end
data/test/models/workarea/review_test.rb CHANGED
@@ -2,6 +2,26 @@ require 'test_helper'
2
2
3
3
module Workarea
4
4
class ReviewTest < Workarea::TestCase
5
+ def product_id
6
+ 'PROD1'
7
+ end
8
+
9
+ def approved
10
+ @approved ||= create_review(
11
+ product_id: product_id,
12
+ rating: 4,
13
+ approved: true
14
+ )
15
+ end
16
+
17
+ def unapproved
18
+ @unapproved ||= create_review(
19
+ product_id: product_id,
20
+ rating: 4,
21
+ approved: false
22
+ )
23
+ end
24
+
5
25
def test_find_for_product_returns_approved_reviews
6
26
assert([approved], Review.find_for_product(product_id))
7
27
end
@@ -69,26 +89,17 @@ module Workarea
69
89
assert_equal(review.body.truncate(50), review.title)
70
90
end
71
91
72
- private
73
-
74
- def product_id
75
- 'PROD1'
76
- end
92
+ def test_find_sorting_score
93
+ create_review(product_id: 'foo', rating: 4, approved: true)
94
+ create_review(product_id: 'foo', rating: 5, approved: true)
95
+ create_review(product_id: 'foo', rating: 5, approved: true)
96
+ assert_equal(3.38, Review.find_sorting_score('foo').round(2))
77
97
78
- def approved
79
- @approved ||= create_review(
98
+ create_review(product_id: 'foo', rating: 1, approved: false)
99
+ assert_equal(3.38, Review.find_sorting_score('foo').round(2))
80
- product_id: product_id,
81
- rating: 4,
82
- approved: true
83
- )
84
- end
85
100
86
- def unapproved
87
- @unapproved ||= create_review(
101
+ create_review(product_id: 'foo', rating: 1, approved: true)
102
+ assert_equal(3.21, Review.find_sorting_score('foo').round(2))
88
- product_id: product_id,
89
- rating: 4,
90
- approved: false
91
- )
92
103
end
93
104
end
94
105
end
data/test/queries/workarea/reports/reviews_by_product_test.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Reports
5
+ class ReviewsByProductTest < TestCase
6
+ setup :add_data, :time_travel
7
+
8
+ def add_data
9
+ create_review(product_id: 'foo', rating: 4, created_at: Time.zone.local(2018, 10, 27), approved: true, verified: true)
10
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 28), approved: true, verified: true)
11
+ create_review(product_id: 'foo', rating: 4, created_at: Time.zone.local(2018, 10, 28), approved: true)
12
+ create_review(product_id: 'foo', rating: 4, created_at: Time.zone.local(2018, 10, 29), approved: true)
13
+ create_review(product_id: 'foo', rating: 3, created_at: Time.zone.local(2018, 10, 29), approved: false)
14
+ create_review(product_id: 'foo', rating: 2, created_at: Time.zone.local(2018, 10, 29), approved: false)
15
+
16
+ create_review(product_id: 'bar', rating: 3, created_at: Time.zone.local(2018, 10, 27), approved: true, verified: true)
17
+ create_review(product_id: 'bar', rating: 4, created_at: Time.zone.local(2018, 10, 28), approved: true)
18
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 28), approved: false)
19
+ create_review(product_id: 'bar', rating: 5, created_at: Time.zone.local(2018, 10, 29), approved: false)
20
+ create_review(product_id: 'bar', rating: 3, created_at: Time.zone.local(2018, 10, 29), approved: false)
21
+ end
22
+
23
+ def time_travel
24
+ travel_to Time.zone.local(2018, 10, 30)
25
+ end
26
+
27
+ def test_grouping_and_summing
28
+ report = ReviewsByProduct.new
29
+ assert_equal(2, report.results.length)
30
+
31
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
32
+ assert_equal(6, foo['reviews'])
33
+ assert_equal(2, foo['verified'])
34
+ assert_equal(3.33, foo['average_rating'].round(2))
35
+ assert_equal(3.13, foo['weighted_average_rating'].round(2))
36
+
37
+ bar = report.results.detect { |r| r['_id'] == 'bar' }
38
+ assert_equal(5, bar['reviews'])
39
+ assert_equal(1, bar['verified'])
40
+ assert_equal(4, bar['average_rating'])
41
+ assert_equal(3.33, bar['weighted_average_rating'].round(2))
42
+ end
43
+
44
+ def test_date_ranges
45
+ report = ReviewsByProduct.new
46
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
47
+ assert_equal(6, foo['reviews'])
48
+
49
+ report = ReviewsByProduct.new(starts_at: '2018-10-28', ends_at: '2018-10-28')
50
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
51
+ assert_equal(2, foo['reviews'])
52
+
53
+ report = ReviewsByProduct.new(starts_at: '2018-10-28', ends_at: '2018-10-29')
54
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
55
+ assert_equal(5, foo['reviews'])
56
+
57
+ report = ReviewsByProduct.new(starts_at: '2018-10-28')
58
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
59
+ assert_equal(5, foo['reviews'])
60
+
61
+ report = ReviewsByProduct.new(ends_at: '2018-10-28')
62
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
63
+ assert_equal(3, foo['reviews'])
64
+ end
65
+
66
+ def test_sorting
67
+ report = ReviewsByProduct.new(sort_by: 'average_rating', sort_direction: 'asc')
68
+ assert_equal('foo', report.results.first['_id'])
69
+
70
+ report = ReviewsByProduct.new(sort_by: 'average_rating', sort_direction: 'desc')
71
+ assert_equal('bar', report.results.first['_id'])
72
+
73
+ report = ReviewsByProduct.new(sort_by: 'reviews', sort_direction: 'desc')
74
+ assert_equal('foo', report.results.first['_id'])
75
+ end
76
+
77
+ def test_filtering
78
+ report = ReviewsByProduct.new(results_filter: 'unapproved')
79
+
80
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
81
+ assert_equal(2, foo['reviews'])
82
+ assert_equal(2.5, foo['average_rating'].round(2))
83
+ assert_equal(2.92, foo['weighted_average_rating'].round(2))
84
+
85
+ bar = report.results.detect { |r| r['_id'] == 'bar' }
86
+ assert_equal(3, bar['reviews'])
87
+ assert_equal(4.33, bar['average_rating'].round(2))
88
+ assert_equal(3.31, bar['weighted_average_rating'].round(2))
89
+
90
+ report = ReviewsByProduct.new(results_filter: 'approved')
91
+
92
+ foo = report.results.detect { |r| r['_id'] == 'foo' }
93
+ assert_equal(4, foo['reviews'])
94
+ assert_equal(3.75, foo['average_rating'].round(2))
95
+ assert_equal(3.21, foo['weighted_average_rating'].round(2))
96
+
97
+ bar = report.results.detect { |r| r['_id'] == 'bar' }
98
+ assert_equal(2, bar['reviews'])
99
+ assert_equal(3.5, bar['average_rating'])
100
+ assert_equal(3.08, bar['weighted_average_rating'].round(2))
101
+ end
102
+ end
103
+ end
104
+ end
data/test/queries/workarea/reports/reviews_by_user_test.rb ADDED
@@ -0,0 +1,102 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Reports
5
+ class ReviewsByUserTest < TestCase
6
+ setup :add_data, :time_travel
7
+
8
+ def add_data
9
+ create_review(email: 'foo@workarea.com', user_id: nil, rating: 4, created_at: Time.zone.local(2018, 10, 27), approved: true, verified: true)
10
+ create_review(email: 'foo@workarea.com', user_id: '123', rating: 3, created_at: Time.zone.local(2018, 10, 28), approved: true, verified: true)
11
+ create_review(email: 'foo@workarea.com', user_id: '123', rating: 4, created_at: Time.zone.local(2018, 10, 28), approved: true)
12
+ create_review(email: 'foo@workarea.com', user_id: nil, rating: 4, created_at: Time.zone.local(2018, 10, 29), approved: true)
13
+ create_review(email: 'foo@workarea.com', user_id: nil, rating: 3, created_at: Time.zone.local(2018, 10, 29), approved: false)
14
+ create_review(email: 'foo@workarea.com', user_id: nil, rating: 2, created_at: Time.zone.local(2018, 10, 29), approved: false)
15
+
16
+ create_review(email: 'bar@workarea.com', user_id: nil, rating: 3, created_at: Time.zone.local(2018, 10, 27), approved: true, verified: true)
17
+ create_review(email: 'bar@workarea.com', user_id: '456', rating: 4, created_at: Time.zone.local(2018, 10, 28), approved: true)
18
+ create_review(email: 'bar@workarea.com', user_id: nil, rating: 5, created_at: Time.zone.local(2018, 10, 28), approved: false)
19
+ create_review(email: 'bar@workarea.com', user_id: nil, rating: 5, created_at: Time.zone.local(2018, 10, 29), approved: false)
20
+ create_review(email: 'bar@workarea.com', user_id: nil, rating: 3, created_at: Time.zone.local(2018, 10, 29), approved: false)
21
+ end
22
+
23
+ def time_travel
24
+ travel_to Time.zone.local(2018, 10, 30)
25
+ end
26
+
27
+ def test_grouping_and_summing
28
+ report = ReviewsByUser.new
29
+ assert_equal(2, report.results.length)
30
+
31
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
32
+ assert_equal(6, foo['reviews'])
33
+ assert_equal(2, foo['verified'])
34
+ assert_equal(3.33, foo['average_rating'].round(2))
35
+ assert_equal('123', foo['user_id'])
36
+ assert_equal(7.5, foo['activity_score'])
37
+
38
+ bar = report.results.detect { |r| r['_id'] == 'bar@workarea.com' }
39
+ assert_equal(5, bar['reviews'])
40
+ assert_equal(1, bar['verified'])
41
+ assert_equal(4, bar['average_rating'])
42
+ assert_equal('456', bar['user_id'])
43
+ assert_equal(5.75, bar['activity_score'])
44
+ end
45
+
46
+ def test_date_ranges
47
+ report = ReviewsByUser.new
48
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
49
+ assert_equal(6, foo['reviews'])
50
+
51
+ report = ReviewsByUser.new(starts_at: '2018-10-28', ends_at: '2018-10-28')
52
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
53
+ assert_equal(2, foo['reviews'])
54
+
55
+ report = ReviewsByUser.new(starts_at: '2018-10-28', ends_at: '2018-10-29')
56
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
57
+ assert_equal(5, foo['reviews'])
58
+
59
+ report = ReviewsByUser.new(starts_at: '2018-10-28')
60
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
61
+ assert_equal(5, foo['reviews'])
62
+
63
+ report = ReviewsByUser.new(ends_at: '2018-10-28')
64
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
65
+ assert_equal(3, foo['reviews'])
66
+ end
67
+
68
+ def test_sorting
69
+ report = ReviewsByUser.new(sort_by: 'average_rating', sort_direction: 'asc')
70
+ assert_equal('foo@workarea.com', report.results.first['_id'])
71
+
72
+ report = ReviewsByUser.new(sort_by: 'average_rating', sort_direction: 'desc')
73
+ assert_equal('bar@workarea.com', report.results.first['_id'])
74
+
75
+ report = ReviewsByUser.new(sort_by: 'reviews', sort_direction: 'desc')
76
+ assert_equal('foo@workarea.com', report.results.first['_id'])
77
+ end
78
+
79
+ def test_filtering
80
+ report = ReviewsByUser.new(results_filter: 'unapproved')
81
+
82
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
83
+ assert_equal(2, foo['reviews'])
84
+ assert_equal(2.5, foo['average_rating'].round(2))
85
+
86
+ bar = report.results.detect { |r| r['_id'] == 'bar@workarea.com' }
87
+ assert_equal(3, bar['reviews'])
88
+ assert_equal(4.33, bar['average_rating'].round(2))
89
+
90
+ report = ReviewsByUser.new(results_filter: 'approved')
91
+
92
+ foo = report.results.detect { |r| r['_id'] == 'foo@workarea.com' }
93
+ assert_equal(4, foo['reviews'])
94
+ assert_equal(3.75, foo['average_rating'].round(2))
95
+
96
+ bar = report.results.detect { |r| r['_id'] == 'bar@workarea.com' }
97
+ assert_equal(2, bar['reviews'])
98
+ assert_equal(3.5, bar['average_rating'])
99
+ end
100
+ end
101
+ end
102
+ end
data/test/system/workarea/admin/reviews_by_product_system_test.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Admin
5
+ class ReviewsByProductSystemTest < Workarea::SystemTest
6
+ include Admin::IntegrationTest
7
+
8
+ def test_report
9
+ create_product(id: 'foo', name: 'Foo')
10
+
11
+ create_review(product_id: 'foo', rating: 4, approved: false)
12
+ create_review(product_id: 'foo', rating: 3)
13
+ create_review(product_id: 'bar', rating: 1, approved: false)
14
+ create_review(product_id: 'bar', rating: 1, approved: false)
15
+ create_review(product_id: 'bar', rating: 2)
16
+ create_review(product_id: 'bar', rating: 2, approved: false)
17
+
18
+ visit admin.reviews_by_product_report_path
19
+ assert(page.has_content?('Foo'))
20
+ assert(page.has_content?('bar'))
21
+ assert(page.has_content?('2'))
22
+ assert(page.has_content?('3.5'))
23
+ assert(page.has_content?('3.08'))
24
+ assert(page.has_content?('4'))
25
+ assert(page.has_content?('1.5'))
26
+ assert(page.has_content?('2.5'))
27
+
28
+ click_link t('workarea.admin.fields.average_rating')
29
+ assert(page.has_content?("#{t('workarea.admin.fields.average_rating')} ↓"))
30
+ assert(page.has_ordered_text?('Foo', 'bar'))
31
+
32
+ click_link t('workarea.admin.fields.reviews')
33
+ assert(page.has_content?("#{t('workarea.admin.fields.reviews')} ↓"))
34
+ assert(page.has_ordered_text?('bar', 'Foo'))
35
+
36
+ click_link t('workarea.admin.fields.reviews')
37
+ assert(page.has_content?("#{t('workarea.admin.fields.reviews')} ↑"))
38
+ assert(page.has_ordered_text?('Foo', 'bar'))
39
+
40
+ select t('workarea.admin.reports.reviews_by_product.filters.unapproved'), from: :results_filter
41
+
42
+ assert(page.has_content?('Foo'))
43
+ assert(page.has_content?('1'))
44
+ assert(page.has_content?('4'))
45
+ assert(page.has_content?('bar'))
46
+ assert(page.has_content?('3'))
47
+ assert(page.has_content?('1.33'))
48
+
49
+ select t('workarea.admin.reports.reviews_by_product.filters.approved'), from: :results_filter
50
+
51
+ assert(page.has_content?('Foo'))
52
+ assert(page.has_content?('1'))
53
+ assert(page.has_content?('3'))
54
+ assert(page.has_content?('bar'))
55
+ assert(page.has_content?('1'))
56
+ assert(page.has_content?('2'))
57
+ end
58
+ end
59
+ end
60
+ end
data/workarea-reviews.gemspec CHANGED
@@ -17,6 +17,6 @@ Gem::Specification.new do |s|
17
17
18
18
s.required_ruby_version = '>= 2.3.0'
19
19
20
- s.add_dependency 'workarea', '~> 3.x', '>= 3.3.x'
20
+ s.add_dependency 'workarea', '~> 3.x', '>= 3.5.x'
21
21
s.add_dependency 'recaptcha', '~> 4.3.1'
22
22
end
data/yarn.lock ADDED
@@ -0,0 +1,3265 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
6
+ version "7.5.5"
7
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
8
+ integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
9
+ dependencies:
10
+ "@babel/highlight" "^7.0.0"
11
+
12
+ "@babel/core@>=7.2.2":
13
+ version "7.5.5"
14
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
15
+ integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==
16
+ dependencies:
17
+ "@babel/code-frame" "^7.5.5"
18
+ "@babel/generator" "^7.5.5"
19
+ "@babel/helpers" "^7.5.5"
20
+ "@babel/parser" "^7.5.5"
21
+ "@babel/template" "^7.4.4"
22
+ "@babel/traverse" "^7.5.5"
23
+ "@babel/types" "^7.5.5"
24
+ convert-source-map "^1.1.0"
25
+ debug "^4.1.0"
26
+ json5 "^2.1.0"
27
+ lodash "^4.17.13"
28
+ resolve "^1.3.2"
29
+ semver "^5.4.1"
30
+ source-map "^0.5.0"
31
+
32
+ "@babel/generator@^7.5.5":
33
+ version "7.5.5"
34
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
35
+ integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
36
+ dependencies:
37
+ "@babel/types" "^7.5.5"
38
+ jsesc "^2.5.1"
39
+ lodash "^4.17.13"
40
+ source-map "^0.5.0"
41
+ trim-right "^1.0.1"
42
+
43
+ "@babel/helper-function-name@^7.1.0":
44
+ version "7.1.0"
45
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
46
+ integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
47
+ dependencies:
48
+ "@babel/helper-get-function-arity" "^7.0.0"
49
+ "@babel/template" "^7.1.0"
50
+ "@babel/types" "^7.0.0"
51
+
52
+ "@babel/helper-get-function-arity@^7.0.0":
53
+ version "7.0.0"
54
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
55
+ integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
56
+ dependencies:
57
+ "@babel/types" "^7.0.0"
58
+
59
+ "@babel/helper-split-export-declaration@^7.4.4":
60
+ version "7.4.4"
61
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
62
+ integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
63
+ dependencies:
64
+ "@babel/types" "^7.4.4"
65
+
66
+ "@babel/helpers@^7.5.5":
67
+ version "7.5.5"
68
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e"
69
+ integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==
70
+ dependencies:
71
+ "@babel/template" "^7.4.4"
72
+ "@babel/traverse" "^7.5.5"
73
+ "@babel/types" "^7.5.5"
74
+
75
+ "@babel/highlight@^7.0.0":
76
+ version "7.5.0"
77
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
78
+ integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
79
+ dependencies:
80
+ chalk "^2.0.0"
81
+ esutils "^2.0.2"
82
+ js-tokens "^4.0.0"
83
+
84
+ "@babel/parser@^7.0.0", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
85
+ version "7.5.5"
86
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
87
+ integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
88
+
89
+ "@babel/template@^7.1.0", "@babel/template@^7.4.4":
90
+ version "7.4.4"
91
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
92
+ integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
93
+ dependencies:
94
+ "@babel/code-frame" "^7.0.0"
95
+ "@babel/parser" "^7.4.4"
96
+ "@babel/types" "^7.4.4"
97
+
98
+ "@babel/traverse@^7.0.0", "@babel/traverse@^7.5.5":
99
+ version "7.5.5"
100
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
101
+ integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
102
+ dependencies:
103
+ "@babel/code-frame" "^7.5.5"
104
+ "@babel/generator" "^7.5.5"
105
+ "@babel/helper-function-name" "^7.1.0"
106
+ "@babel/helper-split-export-declaration" "^7.4.4"
107
+ "@babel/parser" "^7.5.5"
108
+ "@babel/types" "^7.5.5"
109
+ debug "^4.1.0"
110
+ globals "^11.1.0"
111
+ lodash "^4.17.13"
112
+
113
+ "@babel/types@^7.0.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
114
+ version "7.5.5"
115
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
116
+ integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
117
+ dependencies:
118
+ esutils "^2.0.2"
119
+ lodash "^4.17.13"
120
+ to-fast-properties "^2.0.0"
121
+
122
+ "@mrmlnc/readdir-enhanced@^2.2.1":
123
+ version "2.2.1"
124
+ resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
125
+ integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
126
+ dependencies:
127
+ call-me-maybe "^1.0.1"
128
+ glob-to-regexp "^0.3.0"
129
+
130
+ "@nodelib/fs.stat@^1.1.2":
131
+ version "1.1.3"
132
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
133
+ integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
134
+
135
+ "@types/events@*":
136
+ version "3.0.0"
137
+ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
138
+ integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
139
+
140
+ "@types/glob@^7.1.1":
141
+ version "7.1.1"
142
+ resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
143
+ integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
144
+ dependencies:
145
+ "@types/events" "*"
146
+ "@types/minimatch" "*"
147
+ "@types/node" "*"
148
+
149
+ "@types/minimatch@*":
150
+ version "3.0.3"
151
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
152
+ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
153
+
154
+ "@types/node@*":
155
+ version "12.7.4"
156
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04"
157
+ integrity sha512-W0+n1Y+gK/8G2P/piTkBBN38Qc5Q1ZSO6B5H3QmPCUewaiXOo2GCAWZ4ElZCcNhjJuBSUSLGFUJnmlCn5+nxOQ==
158
+
159
+ "@types/unist@*", "@types/unist@^2.0.0":
160
+ version "2.0.3"
161
+ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
162
+ integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
163
+
164
+ "@types/vfile-message@*":
165
+ version "1.0.1"
166
+ resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-1.0.1.tgz#e1e9895cc6b36c462d4244e64e6d0b6eaf65355a"
167
+ integrity sha512-mlGER3Aqmq7bqR1tTTIVHq8KSAFFRyGbrxuM8C/H82g6k7r2fS+IMEkIu3D7JHzG10NvPdR8DNx0jr0pwpp4dA==
168
+ dependencies:
169
+ "@types/node" "*"
170
+ "@types/unist" "*"
171
+
172
+ "@types/vfile@^3.0.0":
173
+ version "3.0.2"
174
+ resolved "https://registry.yarnpkg.com/@types/vfile/-/vfile-3.0.2.tgz#19c18cd232df11ce6fa6ad80259bc86c366b09b9"
175
+ integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw==
176
+ dependencies:
177
+ "@types/node" "*"
178
+ "@types/unist" "*"
179
+ "@types/vfile-message" "*"
180
+
181
+ acorn-jsx@^5.0.0:
182
+ version "5.0.2"
183
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f"
184
+ integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==
185
+
186
+ acorn@^6.0.7:
187
+ version "6.3.0"
188
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
189
+ integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
190
+
191
+ ajv@^6.10.2, ajv@^6.9.1:
192
+ version "6.10.2"
193
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
194
+ integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
195
+ dependencies:
196
+ fast-deep-equal "^2.0.1"
197
+ fast-json-stable-stringify "^2.0.0"
198
+ json-schema-traverse "^0.4.1"
199
+ uri-js "^4.2.2"
200
+
201
+ ansi-escapes@^3.2.0:
202
+ version "3.2.0"
203
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
204
+ integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
205
+
206
+ ansi-regex@^3.0.0:
207
+ version "3.0.0"
208
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
209
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
210
+
211
+ ansi-regex@^4.1.0:
212
+ version "4.1.0"
213
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
214
+ integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
215
+
216
+ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
217
+ version "3.2.1"
218
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
219
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
220
+ dependencies:
221
+ color-convert "^1.9.0"
222
+
223
+ argparse@^1.0.7:
224
+ version "1.0.10"
225
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
226
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
227
+ dependencies:
228
+ sprintf-js "~1.0.2"
229
+
230
+ arr-diff@^4.0.0:
231
+ version "4.0.0"
232
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
233
+ integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
234
+
235
+ arr-flatten@^1.1.0:
236
+ version "1.1.0"
237
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
238
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
239
+
240
+ arr-union@^3.1.0:
241
+ version "3.1.0"
242
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
243
+ integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
244
+
245
+ array-find-index@^1.0.1:
246
+ version "1.0.2"
247
+ resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
248
+ integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
249
+
250
+ array-includes@^3.0.3:
251
+ version "3.0.3"
252
+ resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
253
+ integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=
254
+ dependencies:
255
+ define-properties "^1.1.2"
256
+ es-abstract "^1.7.0"
257
+
258
+ array-union@^1.0.2:
259
+ version "1.0.2"
260
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
261
+ integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
262
+ dependencies:
263
+ array-uniq "^1.0.1"
264
+
265
+ array-uniq@^1.0.1:
266
+ version "1.0.3"
267
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
268
+ integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
269
+
270
+ array-unique@^0.3.2:
271
+ version "0.3.2"
272
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
273
+ integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
274
+
275
+ arrify@^1.0.1:
276
+ version "1.0.1"
277
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
278
+ integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
279
+
280
+ assign-symbols@^1.0.0:
281
+ version "1.0.0"
282
+ resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
283
+ integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
284
+
285
+ astral-regex@^1.0.0:
286
+ version "1.0.0"
287
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
288
+ integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
289
+
290
+ atob@^2.1.1:
291
+ version "2.1.2"
292
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
293
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
294
+
295
+ autoprefixer@^9.5.1:
296
+ version "9.6.1"
297
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47"
298
+ integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==
299
+ dependencies:
300
+ browserslist "^4.6.3"
301
+ caniuse-lite "^1.0.30000980"
302
+ chalk "^2.4.2"
303
+ normalize-range "^0.1.2"
304
+ num2fraction "^1.2.2"
305
+ postcss "^7.0.17"
306
+ postcss-value-parser "^4.0.0"
307
+
308
+ babel-eslint@^10.0.1:
309
+ version "10.0.3"
310
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
311
+ integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==
312
+ dependencies:
313
+ "@babel/code-frame" "^7.0.0"
314
+ "@babel/parser" "^7.0.0"
315
+ "@babel/traverse" "^7.0.0"
316
+ "@babel/types" "^7.0.0"
317
+ eslint-visitor-keys "^1.0.0"
318
+ resolve "^1.12.0"
319
+
320
+ bail@^1.0.0:
321
+ version "1.0.4"
322
+ resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.4.tgz#7181b66d508aa3055d3f6c13f0a0c720641dde9b"
323
+ integrity sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww==
324
+
325
+ balanced-match@^1.0.0:
326
+ version "1.0.0"
327
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
328
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
329
+
330
+ base@^0.11.1:
331
+ version "0.11.2"
332
+ resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
333
+ integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
334
+ dependencies:
335
+ cache-base "^1.0.1"
336
+ class-utils "^0.3.5"
337
+ component-emitter "^1.2.1"
338
+ define-property "^1.0.0"
339
+ isobject "^3.0.1"
340
+ mixin-deep "^1.2.0"
341
+ pascalcase "^0.1.1"
342
+
343
+ brace-expansion@^1.1.7:
344
+ version "1.1.11"
345
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
346
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
347
+ dependencies:
348
+ balanced-match "^1.0.0"
349
+ concat-map "0.0.1"
350
+
351
+ braces@^2.3.1:
352
+ version "2.3.2"
353
+ resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
354
+ integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
355
+ dependencies:
356
+ arr-flatten "^1.1.0"
357
+ array-unique "^0.3.2"
358
+ extend-shallow "^2.0.1"
359
+ fill-range "^4.0.0"
360
+ isobject "^3.0.1"
361
+ repeat-element "^1.1.2"
362
+ snapdragon "^0.8.1"
363
+ snapdragon-node "^2.0.1"
364
+ split-string "^3.0.2"
365
+ to-regex "^3.0.1"
366
+
367
+ braces@^3.0.1:
368
+ version "3.0.2"
369
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
370
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
371
+ dependencies:
372
+ fill-range "^7.0.1"
373
+
374
+ browserslist@^4.6.3:
375
+ version "4.7.0"
376
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17"
377
+ integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==
378
+ dependencies:
379
+ caniuse-lite "^1.0.30000989"
380
+ electron-to-chromium "^1.3.247"
381
+ node-releases "^1.1.29"
382
+
383
+ cache-base@^1.0.1:
384
+ version "1.0.1"
385
+ resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
386
+ integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
387
+ dependencies:
388
+ collection-visit "^1.0.0"
389
+ component-emitter "^1.2.1"
390
+ get-value "^2.0.6"
391
+ has-value "^1.0.0"
392
+ isobject "^3.0.1"
393
+ set-value "^2.0.0"
394
+ to-object-path "^0.3.0"
395
+ union-value "^1.0.0"
396
+ unset-value "^1.0.0"
397
+
398
+ call-me-maybe@^1.0.1:
399
+ version "1.0.1"
400
+ resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
401
+ integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
402
+
403
+ caller-callsite@^2.0.0:
404
+ version "2.0.0"
405
+ resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
406
+ integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
407
+ dependencies:
408
+ callsites "^2.0.0"
409
+
410
+ caller-path@^2.0.0:
411
+ version "2.0.0"
412
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
413
+ integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
414
+ dependencies:
415
+ caller-callsite "^2.0.0"
416
+
417
+ callsites@^2.0.0:
418
+ version "2.0.0"
419
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
420
+ integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
421
+
422
+ callsites@^3.0.0:
423
+ version "3.1.0"
424
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
425
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
426
+
427
+ camelcase-keys@^4.0.0:
428
+ version "4.2.0"
429
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
430
+ integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=
431
+ dependencies:
432
+ camelcase "^4.1.0"
433
+ map-obj "^2.0.0"
434
+ quick-lru "^1.0.0"
435
+
436
+ camelcase@^4.1.0:
437
+ version "4.1.0"
438
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
439
+ integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
440
+
441
+ caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000989:
442
+ version "1.0.30000989"
443
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9"
444
+ integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==
445
+
446
+ ccount@^1.0.0:
447
+ version "1.0.4"
448
+ resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.4.tgz#9cf2de494ca84060a2a8d2854edd6dfb0445f386"
449
+ integrity sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w==
450
+
451
+ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2:
452
+ version "2.4.2"
453
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
454
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
455
+ dependencies:
456
+ ansi-styles "^3.2.1"
457
+ escape-string-regexp "^1.0.5"
458
+ supports-color "^5.3.0"
459
+
460
+ character-entities-html4@^1.0.0:
461
+ version "1.1.3"
462
+ resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.3.tgz#5ce6e01618e47048ac22f34f7f39db5c6fd679ef"
463
+ integrity sha512-SwnyZ7jQBCRHELk9zf2CN5AnGEc2nA+uKMZLHvcqhpPprjkYhiLn0DywMHgN5ttFZuITMATbh68M6VIVKwJbcg==
464
+
465
+ character-entities-legacy@^1.0.0:
466
+ version "1.1.3"
467
+ resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz#3c729991d9293da0ede6dddcaf1f2ce1009ee8b4"
468
+ integrity sha512-YAxUpPoPwxYFsslbdKkhrGnXAtXoHNgYjlBM3WMXkWGTl5RsY3QmOyhwAgL8Nxm9l5LBThXGawxKPn68y6/fww==
469
+
470
+ character-entities@^1.0.0:
471
+ version "1.2.3"
472
+ resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.3.tgz#bbed4a52fe7ef98cc713c6d80d9faa26916d54e6"
473
+ integrity sha512-yB4oYSAa9yLcGyTbB4ItFwHw43QHdH129IJ5R+WvxOkWlyFnR5FAaBNnUq4mcxsTVZGh28bHoeTHMKXH1wZf3w==
474
+
475
+ character-reference-invalid@^1.0.0:
476
+ version "1.1.3"
477
+ resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.3.tgz#1647f4f726638d3ea4a750cf5d1975c1c7919a85"
478
+ integrity sha512-VOq6PRzQBam/8Jm6XBGk2fNEnHXAdGd6go0rtd4weAGECBamHDwwCQSOT12TACIYUZegUXnV6xBXqUssijtxIg==
479
+
480
+ chardet@^0.7.0:
481
+ version "0.7.0"
482
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
483
+ integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
484
+
485
+ class-utils@^0.3.5:
486
+ version "0.3.6"
487
+ resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
488
+ integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
489
+ dependencies:
490
+ arr-union "^3.1.0"
491
+ define-property "^0.2.5"
492
+ isobject "^3.0.0"
493
+ static-extend "^0.1.1"
494
+
495
+ cli-cursor@^2.1.0:
496
+ version "2.1.0"
497
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
498
+ integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
499
+ dependencies:
500
+ restore-cursor "^2.0.0"
501
+
502
+ cli-width@^2.0.0:
503
+ version "2.2.0"
504
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
505
+ integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
506
+
507
+ clone-regexp@^2.1.0:
508
+ version "2.2.0"
509
+ resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f"
510
+ integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==
511
+ dependencies:
512
+ is-regexp "^2.0.0"
513
+
514
+ collapse-white-space@^1.0.2:
515
+ version "1.0.5"
516
+ resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.5.tgz#c2495b699ab1ed380d29a1091e01063e75dbbe3a"
517
+ integrity sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ==
518
+
519
+ collection-visit@^1.0.0:
520
+ version "1.0.0"
521
+ resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
522
+ integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
523
+ dependencies:
524
+ map-visit "^1.0.0"
525
+ object-visit "^1.0.0"
526
+
527
+ color-convert@^1.9.0:
528
+ version "1.9.3"
529
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
530
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
531
+ dependencies:
532
+ color-name "1.1.3"
533
+
534
+ color-name@1.1.3:
535
+ version "1.1.3"
536
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
537
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
538
+
539
+ component-emitter@^1.2.1:
540
+ version "1.3.0"
541
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
542
+ integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
543
+
544
+ concat-map@0.0.1:
545
+ version "0.0.1"
546
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
547
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
548
+
549
+ contains-path@^0.1.0:
550
+ version "0.1.0"
551
+ resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
552
+ integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
553
+
554
+ convert-source-map@^1.1.0:
555
+ version "1.6.0"
556
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
557
+ integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
558
+ dependencies:
559
+ safe-buffer "~5.1.1"
560
+
561
+ copy-descriptor@^0.1.0:
562
+ version "0.1.1"
563
+ resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
564
+ integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
565
+
566
+ cosmiconfig@^5.2.0:
567
+ version "5.2.1"
568
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
569
+ integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
570
+ dependencies:
571
+ import-fresh "^2.0.0"
572
+ is-directory "^0.3.1"
573
+ js-yaml "^3.13.1"
574
+ parse-json "^4.0.0"
575
+
576
+ cross-spawn@^6.0.5:
577
+ version "6.0.5"
578
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
579
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
580
+ dependencies:
581
+ nice-try "^1.0.4"
582
+ path-key "^2.0.1"
583
+ semver "^5.5.0"
584
+ shebang-command "^1.2.0"
585
+ which "^1.2.9"
586
+
587
+ cssesc@^3.0.0:
588
+ version "3.0.0"
589
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
590
+ integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
591
+
592
+ currently-unhandled@^0.4.1:
593
+ version "0.4.1"
594
+ resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
595
+ integrity sha1-mI3zP+qxke95mmE2nddsF635V+o=
596
+ dependencies:
597
+ array-find-index "^1.0.1"
598
+
599
+ debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
600
+ version "2.6.9"
601
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
602
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
603
+ dependencies:
604
+ ms "2.0.0"
605
+
606
+ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
607
+ version "4.1.1"
608
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
609
+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
610
+ dependencies:
611
+ ms "^2.1.1"
612
+
613
+ decamelize-keys@^1.0.0:
614
+ version "1.1.0"
615
+ resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
616
+ integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
617
+ dependencies:
618
+ decamelize "^1.1.0"
619
+ map-obj "^1.0.0"
620
+
621
+ decamelize@^1.1.0:
622
+ version "1.2.0"
623
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
624
+ integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
625
+
626
+ decode-uri-component@^0.2.0:
627
+ version "0.2.0"
628
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
629
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
630
+
631
+ deep-is@~0.1.3:
632
+ version "0.1.3"
633
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
634
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
635
+
636
+ define-properties@^1.1.2, define-properties@^1.1.3:
637
+ version "1.1.3"
638
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
639
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
640
+ dependencies:
641
+ object-keys "^1.0.12"
642
+
643
+ define-property@^0.2.5:
644
+ version "0.2.5"
645
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
646
+ integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
647
+ dependencies:
648
+ is-descriptor "^0.1.0"
649
+
650
+ define-property@^1.0.0:
651
+ version "1.0.0"
652
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
653
+ integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
654
+ dependencies:
655
+ is-descriptor "^1.0.0"
656
+
657
+ define-property@^2.0.2:
658
+ version "2.0.2"
659
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
660
+ integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
661
+ dependencies:
662
+ is-descriptor "^1.0.2"
663
+ isobject "^3.0.1"
664
+
665
+ dir-glob@^2.2.2:
666
+ version "2.2.2"
667
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
668
+ integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
669
+ dependencies:
670
+ path-type "^3.0.0"
671
+
672
+ doctrine@1.5.0:
673
+ version "1.5.0"
674
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
675
+ integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
676
+ dependencies:
677
+ esutils "^2.0.2"
678
+ isarray "^1.0.0"
679
+
680
+ doctrine@^2.1.0:
681
+ version "2.1.0"
682
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
683
+ integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
684
+ dependencies:
685
+ esutils "^2.0.2"
686
+
687
+ doctrine@^3.0.0:
688
+ version "3.0.0"
689
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
690
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
691
+ dependencies:
692
+ esutils "^2.0.2"
693
+
694
+ dom-serializer@0:
695
+ version "0.2.1"
696
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb"
697
+ integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==
698
+ dependencies:
699
+ domelementtype "^2.0.1"
700
+ entities "^2.0.0"
701
+
702
+ domelementtype@1, domelementtype@^1.3.1:
703
+ version "1.3.1"
704
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
705
+ integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
706
+
707
+ domelementtype@^2.0.1:
708
+ version "2.0.1"
709
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
710
+ integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
711
+
712
+ domhandler@^2.3.0:
713
+ version "2.4.2"
714
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
715
+ integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
716
+ dependencies:
717
+ domelementtype "1"
718
+
719
+ domutils@^1.5.1:
720
+ version "1.7.0"
721
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
722
+ integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
723
+ dependencies:
724
+ dom-serializer "0"
725
+ domelementtype "1"
726
+
727
+ dot-prop@^4.1.1:
728
+ version "4.2.0"
729
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
730
+ integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
731
+ dependencies:
732
+ is-obj "^1.0.0"
733
+
734
+ electron-to-chromium@^1.3.247:
735
+ version "1.3.252"
736
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.252.tgz#5b6261965b564a0f4df0f1c86246487897017f52"
737
+ integrity sha512-NWJ5TztDnjExFISZHFwpoJjMbLUifsNBnx7u2JI0gCw6SbKyQYYWWtBHasO/jPtHym69F4EZuTpRNGN11MT/jg==
738
+
739
+ emoji-regex@^7.0.1:
740
+ version "7.0.3"
741
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
742
+ integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
743
+
744
+ emoji-regex@^8.0.0:
745
+ version "8.0.0"
746
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
747
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
748
+
749
+ entities@^1.1.1:
750
+ version "1.1.2"
751
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
752
+ integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
753
+
754
+ entities@^2.0.0:
755
+ version "2.0.0"
756
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
757
+ integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
758
+
759
+ error-ex@^1.2.0, error-ex@^1.3.1:
760
+ version "1.3.2"
761
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
762
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
763
+ dependencies:
764
+ is-arrayish "^0.2.1"
765
+
766
+ es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.7.0:
767
+ version "1.14.1"
768
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.1.tgz#6e8d84b445ec9c610781e74a6d52cc31aac5b4ca"
769
+ integrity sha512-cp/Tb1oA/rh2X7vqeSOvM+TSo3UkJLX70eNihgVEvnzwAgikjkTFr/QVgRCaxjm0knCNQzNoxxxcw2zO2LJdZA==
770
+ dependencies:
771
+ es-to-primitive "^1.2.0"
772
+ function-bind "^1.1.1"
773
+ has "^1.0.3"
774
+ has-symbols "^1.0.0"
775
+ is-callable "^1.1.4"
776
+ is-regex "^1.0.4"
777
+ object-inspect "^1.6.0"
778
+ object-keys "^1.1.1"
779
+ string.prototype.trimleft "^2.0.0"
780
+ string.prototype.trimright "^2.0.0"
781
+
782
+ es-to-primitive@^1.2.0:
783
+ version "1.2.0"
784
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
785
+ integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
786
+ dependencies:
787
+ is-callable "^1.1.4"
788
+ is-date-object "^1.0.1"
789
+ is-symbol "^1.0.2"
790
+
791
+ escape-string-regexp@^1.0.5:
792
+ version "1.0.5"
793
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
794
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
795
+
796
+ eslint-config-esnext@^4.0.0:
797
+ version "4.0.0"
798
+ resolved "https://registry.yarnpkg.com/eslint-config-esnext/-/eslint-config-esnext-4.0.0.tgz#b3f91bd59c515824fca0d9e553ab241833386230"
799
+ integrity sha512-UOovbox5WIgG9VSJPxtCsfwOkK96yNp8hBBi+WZ66OTr5zc7PxJCkE4MS7vON2Z1md5PNhwFHVzE9Uu+owBg1Q==
800
+ dependencies:
801
+ babel-eslint "^10.0.1"
802
+ eslint "^5.6.0"
803
+ eslint-plugin-babel "^5.2.1"
804
+ eslint-plugin-import "^2.14.0"
805
+
806
+ eslint-config-node@^4.0.0:
807
+ version "4.0.0"
808
+ resolved "https://registry.yarnpkg.com/eslint-config-node/-/eslint-config-node-4.0.0.tgz#4ebb40defd29a261ce2e7f6fe1b2d7aee38cf2a5"
809
+ integrity sha512-sdr7zqVTQddLEBpsNzTFASOAk8bSbWatZqxLD9J1nBI/H83lGOknODaCCJFWMDN+36LNUMVWVWo+0LhxQJc+wg==
810
+ dependencies:
811
+ eslint "^5.6.0"
812
+ eslint-config-esnext "^4.0.0"
813
+
814
+ eslint-config-react-native@^4.0.0:
815
+ version "4.0.0"
816
+ resolved "https://registry.yarnpkg.com/eslint-config-react-native/-/eslint-config-react-native-4.0.0.tgz#1eaeb4296a025998423e811599463e5a08ccc76f"
817
+ integrity sha512-0b+yie+RJVugAxeFUX6RrR9jQKTSkBaPeBTHGbhGqmGZ/R9fErtMcMutBoc//7TfRmITmmDuVPnJAZ8tM/T8Yw==
818
+ dependencies:
819
+ eslint "^5.6.0"
820
+ eslint-config-esnext "^4.0.0"
821
+ eslint-plugin-react "^7.11.1"
822
+ eslint-plugin-react-native "^3.3.0"
823
+
824
+ eslint-config-recommended@~4.0.0:
825
+ version "4.0.0"
826
+ resolved "https://registry.yarnpkg.com/eslint-config-recommended/-/eslint-config-recommended-4.0.0.tgz#9b4964d99ed732faad07e3fece5936730ef46690"
827
+ integrity sha512-opy/c7QsbeBumzILCessr3aRgesWjeOQiRTRHzCJZwH02ZIt+9Iu893sZbXF9/qsH8Jxo+0ylHKvlomvxTX97Q==
828
+ dependencies:
829
+ eslint "^5.6.0"
830
+ eslint-config-esnext "^4.0.0"
831
+ eslint-config-node "^4.0.0"
832
+ eslint-config-react-native "^4.0.0"
833
+
834
+ eslint-import-resolver-node@^0.3.2:
835
+ version "0.3.2"
836
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
837
+ integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==
838
+ dependencies:
839
+ debug "^2.6.9"
840
+ resolve "^1.5.0"
841
+
842
+ eslint-module-utils@^2.4.0:
843
+ version "2.4.1"
844
+ resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c"
845
+ integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==
846
+ dependencies:
847
+ debug "^2.6.8"
848
+ pkg-dir "^2.0.0"
849
+
850
+ eslint-plugin-babel@^5.2.1:
851
+ version "5.3.0"
852
+ resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.3.0.tgz#2e7f251ccc249326da760c1a4c948a91c32d0023"
853
+ integrity sha512-HPuNzSPE75O+SnxHIafbW5QB45r2w78fxqwK3HmjqIUoPfPzVrq6rD+CINU3yzoDSzEhUkX07VUphbF73Lth/w==
854
+ dependencies:
855
+ eslint-rule-composer "^0.3.0"
856
+
857
+ eslint-plugin-import@^2.14.0:
858
+ version "2.18.2"
859
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
860
+ integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
861
+ dependencies:
862
+ array-includes "^3.0.3"
863
+ contains-path "^0.1.0"
864
+ debug "^2.6.9"
865
+ doctrine "1.5.0"
866
+ eslint-import-resolver-node "^0.3.2"
867
+ eslint-module-utils "^2.4.0"
868
+ has "^1.0.3"
869
+ minimatch "^3.0.4"
870
+ object.values "^1.1.0"
871
+ read-pkg-up "^2.0.0"
872
+ resolve "^1.11.0"
873
+
874
+ eslint-plugin-react-native-globals@^0.1.1:
875
+ version "0.1.2"
876
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz#ee1348bc2ceb912303ce6bdbd22e2f045ea86ea2"
877
+ integrity sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==
878
+
879
+ eslint-plugin-react-native@^3.3.0:
880
+ version "3.7.0"
881
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-native/-/eslint-plugin-react-native-3.7.0.tgz#7e2cc1f3cf24919c4c0ea7fac13301e7444e105f"
882
+ integrity sha512-krLtQmGih/uJDPxF8DBpnU8J3kRUsDm/Dey5yEhOO8LN1I3Wesbk4PGCg8Zah57azKFU+9YtGooFjJcDJWUs+g==
883
+ dependencies:
884
+ eslint-plugin-react-native-globals "^0.1.1"
885
+
886
+ eslint-plugin-react@^7.11.1:
887
+ version "7.14.3"
888
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13"
889
+ integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==
890
+ dependencies:
891
+ array-includes "^3.0.3"
892
+ doctrine "^2.1.0"
893
+ has "^1.0.3"
894
+ jsx-ast-utils "^2.1.0"
895
+ object.entries "^1.1.0"
896
+ object.fromentries "^2.0.0"
897
+ object.values "^1.1.0"
898
+ prop-types "^15.7.2"
899
+ resolve "^1.10.1"
900
+
901
+ eslint-rule-composer@^0.3.0:
902
+ version "0.3.0"
903
+ resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
904
+ integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
905
+
906
+ eslint-scope@^4.0.3:
907
+ version "4.0.3"
908
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
909
+ integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
910
+ dependencies:
911
+ esrecurse "^4.1.0"
912
+ estraverse "^4.1.1"
913
+
914
+ eslint-utils@^1.3.1:
915
+ version "1.4.2"
916
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab"
917
+ integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==
918
+ dependencies:
919
+ eslint-visitor-keys "^1.0.0"
920
+
921
+ eslint-visitor-keys@^1.0.0:
922
+ version "1.1.0"
923
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
924
+ integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
925
+
926
+ eslint@^5.6.0, eslint@~5.16.0:
927
+ version "5.16.0"
928
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
929
+ integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
930
+ dependencies:
931
+ "@babel/code-frame" "^7.0.0"
932
+ ajv "^6.9.1"
933
+ chalk "^2.1.0"
934
+ cross-spawn "^6.0.5"
935
+ debug "^4.0.1"
936
+ doctrine "^3.0.0"
937
+ eslint-scope "^4.0.3"
938
+ eslint-utils "^1.3.1"
939
+ eslint-visitor-keys "^1.0.0"
940
+ espree "^5.0.1"
941
+ esquery "^1.0.1"
942
+ esutils "^2.0.2"
943
+ file-entry-cache "^5.0.1"
944
+ functional-red-black-tree "^1.0.1"
945
+ glob "^7.1.2"
946
+ globals "^11.7.0"
947
+ ignore "^4.0.6"
948
+ import-fresh "^3.0.0"
949
+ imurmurhash "^0.1.4"
950
+ inquirer "^6.2.2"
951
+ js-yaml "^3.13.0"
952
+ json-stable-stringify-without-jsonify "^1.0.1"
953
+ levn "^0.3.0"
954
+ lodash "^4.17.11"
955
+ minimatch "^3.0.4"
956
+ mkdirp "^0.5.1"
957
+ natural-compare "^1.4.0"
958
+ optionator "^0.8.2"
959
+ path-is-inside "^1.0.2"
960
+ progress "^2.0.0"
961
+ regexpp "^2.0.1"
962
+ semver "^5.5.1"
963
+ strip-ansi "^4.0.0"
964
+ strip-json-comments "^2.0.1"
965
+ table "^5.2.3"
966
+ text-table "^0.2.0"
967
+
968
+ espree@^5.0.1:
969
+ version "5.0.1"
970
+ resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
971
+ integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==
972
+ dependencies:
973
+ acorn "^6.0.7"
974
+ acorn-jsx "^5.0.0"
975
+ eslint-visitor-keys "^1.0.0"
976
+
977
+ esprima@^4.0.0:
978
+ version "4.0.1"
979
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
980
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
981
+
982
+ esquery@^1.0.1:
983
+ version "1.0.1"
984
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
985
+ integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
986
+ dependencies:
987
+ estraverse "^4.0.0"
988
+
989
+ esrecurse@^4.1.0:
990
+ version "4.2.1"
991
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
992
+ integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
993
+ dependencies:
994
+ estraverse "^4.1.0"
995
+
996
+ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
997
+ version "4.3.0"
998
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
999
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
1000
+
1001
+ esutils@^2.0.2:
1002
+ version "2.0.3"
1003
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
1004
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
1005
+
1006
+ execall@^2.0.0:
1007
+ version "2.0.0"
1008
+ resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
1009
+ integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==
1010
+ dependencies:
1011
+ clone-regexp "^2.1.0"
1012
+
1013
+ expand-brackets@^2.1.4:
1014
+ version "2.1.4"
1015
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
1016
+ integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
1017
+ dependencies:
1018
+ debug "^2.3.3"
1019
+ define-property "^0.2.5"
1020
+ extend-shallow "^2.0.1"
1021
+ posix-character-classes "^0.1.0"
1022
+ regex-not "^1.0.0"
1023
+ snapdragon "^0.8.1"
1024
+ to-regex "^3.0.1"
1025
+
1026
+ extend-shallow@^2.0.1:
1027
+ version "2.0.1"
1028
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
1029
+ integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
1030
+ dependencies:
1031
+ is-extendable "^0.1.0"
1032
+
1033
+ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
1034
+ version "3.0.2"
1035
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
1036
+ integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
1037
+ dependencies:
1038
+ assign-symbols "^1.0.0"
1039
+ is-extendable "^1.0.1"
1040
+
1041
+ extend@^3.0.0:
1042
+ version "3.0.2"
1043
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
1044
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
1045
+
1046
+ external-editor@^3.0.3:
1047
+ version "3.1.0"
1048
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
1049
+ integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
1050
+ dependencies:
1051
+ chardet "^0.7.0"
1052
+ iconv-lite "^0.4.24"
1053
+ tmp "^0.0.33"
1054
+
1055
+ extglob@^2.0.4:
1056
+ version "2.0.4"
1057
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
1058
+ integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
1059
+ dependencies:
1060
+ array-unique "^0.3.2"
1061
+ define-property "^1.0.0"
1062
+ expand-brackets "^2.1.4"
1063
+ extend-shallow "^2.0.1"
1064
+ fragment-cache "^0.2.1"
1065
+ regex-not "^1.0.0"
1066
+ snapdragon "^0.8.1"
1067
+ to-regex "^3.0.1"
1068
+
1069
+ fast-deep-equal@^2.0.1:
1070
+ version "2.0.1"
1071
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
1072
+ integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
1073
+
1074
+ fast-glob@^2.2.6:
1075
+ version "2.2.7"
1076
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
1077
+ integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
1078
+ dependencies:
1079
+ "@mrmlnc/readdir-enhanced" "^2.2.1"
1080
+ "@nodelib/fs.stat" "^1.1.2"
1081
+ glob-parent "^3.1.0"
1082
+ is-glob "^4.0.0"
1083
+ merge2 "^1.2.3"
1084
+ micromatch "^3.1.10"
1085
+
1086
+ fast-json-stable-stringify@^2.0.0:
1087
+ version "2.0.0"
1088
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
1089
+ integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
1090
+
1091
+ fast-levenshtein@~2.0.4:
1092
+ version "2.0.6"
1093
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
1094
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
1095
+
1096
+ figures@^2.0.0:
1097
+ version "2.0.0"
1098
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
1099
+ integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
1100
+ dependencies:
1101
+ escape-string-regexp "^1.0.5"
1102
+
1103
+ file-entry-cache@^5.0.1:
1104
+ version "5.0.1"
1105
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c"
1106
+ integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==
1107
+ dependencies:
1108
+ flat-cache "^2.0.1"
1109
+
1110
+ fill-range@^4.0.0:
1111
+ version "4.0.0"
1112