theme-check 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.github/probots.yml +3 -0
  3. data/.github/workflows/theme-check.yml +28 -0
  4. data/.gitignore +13 -0
  5. data/.rubocop.yml +18 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/CONTRIBUTING.md +132 -0
  8. data/Gemfile +26 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.md +8 -0
  11. data/README.md +71 -0
  12. data/Rakefile +14 -0
  13. data/bin/liquid-server +4 -0
  14. data/config/default.yml +63 -0
  15. data/data/shopify_liquid/filters.yml +174 -0
  16. data/data/shopify_liquid/objects.yml +81 -0
  17. data/dev.yml +23 -0
  18. data/docs/preview.png +0 -0
  19. data/exe/theme-check +6 -0
  20. data/exe/theme-check-language-server +12 -0
  21. data/lib/theme_check.rb +25 -0
  22. data/lib/theme_check/analyzer.rb +43 -0
  23. data/lib/theme_check/check.rb +92 -0
  24. data/lib/theme_check/checks.rb +12 -0
  25. data/lib/theme_check/checks/convert_include_to_render.rb +13 -0
  26. data/lib/theme_check/checks/default_locale.rb +12 -0
  27. data/lib/theme_check/checks/liquid_tag.rb +48 -0
  28. data/lib/theme_check/checks/matching_schema_translations.rb +73 -0
  29. data/lib/theme_check/checks/matching_translations.rb +29 -0
  30. data/lib/theme_check/checks/missing_required_template_files.rb +29 -0
  31. data/lib/theme_check/checks/missing_template.rb +25 -0
  32. data/lib/theme_check/checks/nested_snippet.rb +46 -0
  33. data/lib/theme_check/checks/required_directories.rb +24 -0
  34. data/lib/theme_check/checks/required_layout_theme_object.rb +40 -0
  35. data/lib/theme_check/checks/space_inside_braces.rb +58 -0
  36. data/lib/theme_check/checks/syntax_error.rb +29 -0
  37. data/lib/theme_check/checks/template_length.rb +18 -0
  38. data/lib/theme_check/checks/translation_key_exists.rb +35 -0
  39. data/lib/theme_check/checks/undefined_object.rb +86 -0
  40. data/lib/theme_check/checks/unknown_filter.rb +25 -0
  41. data/lib/theme_check/checks/unused_assign.rb +54 -0
  42. data/lib/theme_check/checks/unused_snippet.rb +34 -0
  43. data/lib/theme_check/checks/valid_html_translation.rb +43 -0
  44. data/lib/theme_check/checks/valid_json.rb +14 -0
  45. data/lib/theme_check/checks/valid_schema.rb +13 -0
  46. data/lib/theme_check/checks_tracking.rb +8 -0
  47. data/lib/theme_check/cli.rb +78 -0
  48. data/lib/theme_check/config.rb +108 -0
  49. data/lib/theme_check/json_check.rb +11 -0
  50. data/lib/theme_check/json_file.rb +47 -0
  51. data/lib/theme_check/json_helpers.rb +9 -0
  52. data/lib/theme_check/language_server.rb +11 -0
  53. data/lib/theme_check/language_server/handler.rb +117 -0
  54. data/lib/theme_check/language_server/server.rb +140 -0
  55. data/lib/theme_check/liquid_check.rb +13 -0
  56. data/lib/theme_check/locale_diff.rb +69 -0
  57. data/lib/theme_check/node.rb +117 -0
  58. data/lib/theme_check/offense.rb +104 -0
  59. data/lib/theme_check/parsing_helpers.rb +17 -0
  60. data/lib/theme_check/printer.rb +74 -0
  61. data/lib/theme_check/shopify_liquid.rb +3 -0
  62. data/lib/theme_check/shopify_liquid/filter.rb +18 -0
  63. data/lib/theme_check/shopify_liquid/object.rb +16 -0
  64. data/lib/theme_check/tags.rb +146 -0
  65. data/lib/theme_check/template.rb +73 -0
  66. data/lib/theme_check/theme.rb +60 -0
  67. data/lib/theme_check/version.rb +4 -0
  68. data/lib/theme_check/visitor.rb +37 -0
  69. data/theme-check.gemspec +28 -0
  70. metadata +156 -0
@@ -0,0 +1,174 @@
1
+ ---
2
+ Liquid::StandardFilters:
3
+ - url_encode
4
+ - where
5
+ - uniq
6
+ - escape
7
+ - replace
8
+ - remove
9
+ - h
10
+ - upcase
11
+ - downcase
12
+ - capitalize
13
+ - date
14
+ - last
15
+ - split
16
+ - size
17
+ - append
18
+ - reverse
19
+ - join
20
+ - concat
21
+ - prepend
22
+ - escape_once
23
+ - url_decode
24
+ - truncatewords
25
+ - strip_html
26
+ - strip
27
+ - lstrip
28
+ - rstrip
29
+ - strip_newlines
30
+ - plus
31
+ - sort_natural
32
+ - compact
33
+ - replace_first
34
+ - remove_first
35
+ - newline_to_br
36
+ - minus
37
+ - divided_by
38
+ - at_least
39
+ - at_most
40
+ - sort
41
+ - default
42
+ - modulo
43
+ - slice
44
+ - abs
45
+ - map
46
+ - floor
47
+ - ceil
48
+ - round
49
+ - truncate
50
+ - first
51
+ - times
52
+ FormFilter:
53
+ - payment_button
54
+ - payment_terms
55
+ - installments_pricing
56
+ - default_errors
57
+ DateFilter:
58
+ - date
59
+ I18nFilter:
60
+ - translate
61
+ - t
62
+ - date
63
+ - sentence
64
+ - time_tag
65
+ UrlFilter:
66
+ - image_url
67
+ - stylesheet_tag
68
+ - script_tag
69
+ - img_tag
70
+ - shopify_asset_url
71
+ - payment_type_img_url
72
+ - payment_type_svg_tag
73
+ - placeholder_svg_tag
74
+ - link_to
75
+ - asset_url
76
+ - img_url
77
+ - asset_img_url
78
+ - global_asset_url
79
+ - file_url
80
+ - file_img_url
81
+ - product_img_url
82
+ - collection_img_url
83
+ - article_img_url
84
+ JsonFilter:
85
+ - json
86
+ ColorFilter:
87
+ - color_contrast
88
+ - color_difference
89
+ - brightness_difference
90
+ - hex_to_rgba
91
+ - color_to_rgb
92
+ - color_to_hsl
93
+ - color_to_hex
94
+ - color_extract
95
+ - color_brightness
96
+ - color_modify
97
+ - color_lighten
98
+ - color_darken
99
+ - color_saturate
100
+ - color_desaturate
101
+ - color_mix
102
+ MoneyFilter:
103
+ - money_without_currency
104
+ - money_without_trailing_zeros
105
+ - money
106
+ - money_with_currency
107
+ StringFilter:
108
+ - handleize
109
+ - url_param_escape
110
+ - url_escape
111
+ - camelcase
112
+ - camelize
113
+ - encode_url_component
114
+ - format_code
115
+ - md5
116
+ - handle
117
+ CollectionFilter:
118
+ - within
119
+ - link_to_vendor
120
+ - url_for_vendor
121
+ - link_to_type
122
+ - url_for_type
123
+ - sort_by
124
+ TagFilter:
125
+ - link_to_add_tag
126
+ - link_to_remove_tag
127
+ - link_to_tag
128
+ - highlight_active_tag
129
+ CryptoFilter:
130
+ - hmac_sha1
131
+ - sha256
132
+ - hmac_sha256
133
+ - md5
134
+ - sha1
135
+ CustomerAccountFilter:
136
+ - customer_login_link
137
+ - customer_logout_link
138
+ - customer_register_link
139
+ - recover_password_link
140
+ - delete_customer_address_link
141
+ - edit_customer_address_link
142
+ - cancel_customer_order_link
143
+ CurrencyFormFilter:
144
+ - currency_selector
145
+ PaginationFilter:
146
+ - default_pagination
147
+ WeightFilter:
148
+ - weight
149
+ - weight_with_unit
150
+ - unit
151
+ TextFilter:
152
+ - pad_spaces
153
+ - paragraphize
154
+ - highlight
155
+ - format_address
156
+ - excerpt
157
+ - pluralize
158
+ FontFilter:
159
+ - font_face
160
+ - font_url
161
+ - font_modify
162
+ DistanceFilter:
163
+ - distance_from
164
+ MediaFilter:
165
+ - external_video_url
166
+ - external_video_tag
167
+ - video_tag
168
+ - model_viewer_tag
169
+ - media_tag
170
+ ThemeFilter:
171
+ - theme_url
172
+ - link_to_theme
173
+ DebugFilter:
174
+ - debug
@@ -0,0 +1,81 @@
1
+ ---
2
+ - additional_checkout_buttons
3
+ - address
4
+ - all_country_option_tags
5
+ - all_products
6
+ - article
7
+ - articles
8
+ - block
9
+ - blog
10
+ - blogs
11
+ - canonical_url
12
+ - cart
13
+ - checkout
14
+ - collection
15
+ - collections
16
+ - comment
17
+ - content_for_additional_checkout_buttons
18
+ - content_for_header
19
+ - content_for_index
20
+ - content_for_layout
21
+ - country_option_tags
22
+ - currency
23
+ - current_page
24
+ - current_tags
25
+ - customer
26
+ - customer_address
27
+ - discount_allocation
28
+ - discount_application
29
+ - external_video
30
+ - font
31
+ - forloop
32
+ - form
33
+ - fulfillment
34
+ - gift_card
35
+ - handle
36
+ - image
37
+ - images
38
+ - line_item
39
+ - link
40
+ - linklist
41
+ - linklists
42
+ - location
43
+ - metafield
44
+ - model
45
+ - model_source
46
+ - order
47
+ - page
48
+ - page_description
49
+ - page_image
50
+ - page_title
51
+ - pages
52
+ - paginate
53
+ - part
54
+ - policy
55
+ - powered_by_link
56
+ - product
57
+ - product_option
58
+ - recommendations
59
+ - request
60
+ - routes
61
+ - script
62
+ - scripts
63
+ - search
64
+ - section
65
+ - selling_plan
66
+ - selling_plan_allocation
67
+ - selling_plan_group
68
+ - settings
69
+ - shipping_method
70
+ - shop
71
+ - shop_locale
72
+ - store_availability
73
+ - tablerow
74
+ - tax_line
75
+ - template
76
+ - theme
77
+ - transaction
78
+ - unit_price_measurement
79
+ - variant
80
+ - video
81
+ - video_source
data/dev.yml ADDED
@@ -0,0 +1,23 @@
1
+ name: theme-check
2
+
3
+ type: ruby
4
+
5
+ up:
6
+ - ruby: 2.7.1
7
+ - bundler
8
+
9
+ commands:
10
+ check:
11
+ desc: 'Check a theme'
12
+ syntax:
13
+ argument: file
14
+ run: bundle exec theme-check
15
+ test:
16
+ syntax:
17
+ argument: file
18
+ optional: args...
19
+ run: bundle exec rake test
20
+ style:
21
+ run: bundle exec rake rubocop
22
+ language-server:
23
+ run: bundle exec theme-check-language-server
Binary file
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "theme_check"
5
+
6
+ ThemeCheck::Cli.new.run!(ARGV)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'theme_check'
5
+
6
+ if ENV["THEME_CHECK_DEBUG"] == "true"
7
+ $DEBUG = true
8
+ end
9
+ # Force encoding to UTF-8 to fix VSCode
10
+ Encoding.default_external = Encoding::UTF_8
11
+ status_code = ThemeCheck::LanguageServer.start
12
+ exit! status_code
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ require "liquid"
3
+
4
+ require_relative "theme_check/analyzer"
5
+ require_relative "theme_check/check"
6
+ require_relative "theme_check/checks_tracking"
7
+ require_relative "theme_check/cli"
8
+ require_relative "theme_check/liquid_check"
9
+ require_relative "theme_check/locale_diff"
10
+ require_relative "theme_check/json_check"
11
+ require_relative "theme_check/json_file"
12
+ require_relative "theme_check/json_helpers"
13
+ require_relative "theme_check/language_server"
14
+ require_relative "theme_check/checks"
15
+ require_relative "theme_check/config"
16
+ require_relative "theme_check/node"
17
+ require_relative "theme_check/offense"
18
+ require_relative "theme_check/printer"
19
+ require_relative "theme_check/shopify_liquid"
20
+ require_relative "theme_check/tags"
21
+ require_relative "theme_check/template"
22
+ require_relative "theme_check/theme"
23
+ require_relative "theme_check/visitor"
24
+
25
+ Dir[__dir__ + "/theme_check/checks/*.rb"].each { |file| require file }
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ module ThemeCheck
3
+ class Analyzer
4
+ attr_reader :offenses
5
+
6
+ def initialize(theme, checks = Check.all.map(&:new))
7
+ @theme = theme
8
+ @offenses = []
9
+
10
+ @liquid_checks = Checks.new
11
+ @json_checks = Checks.new
12
+
13
+ checks.each do |check|
14
+ check.theme = @theme
15
+ check.offenses = @offenses
16
+
17
+ case check
18
+ when LiquidCheck
19
+ @liquid_checks << check
20
+ when JsonCheck
21
+ @json_checks << check
22
+ end
23
+ end
24
+
25
+ @visitor = Visitor.new(@liquid_checks)
26
+ end
27
+
28
+ def analyze_theme
29
+ @offenses.clear
30
+ @theme.liquid.each { |template| @visitor.visit_template(template) }
31
+ @theme.json.each { |json_file| @json_checks.call(:on_file, json_file) }
32
+ @liquid_checks.call(:on_end)
33
+ @json_checks.call(:on_end)
34
+ @offenses
35
+ end
36
+
37
+ def analyze_file(path)
38
+ path = Pathname.new(path)
39
+ analyze_theme
40
+ @offenses.reject! { |offense| offense.template.path != path }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+ require_relative "json_helpers"
3
+
4
+ module ThemeCheck
5
+ class Check
6
+ include JsonHelpers
7
+
8
+ attr_accessor :theme
9
+ attr_accessor :offenses
10
+ attr_accessor :options
11
+
12
+ SEVERITIES = [
13
+ :error,
14
+ :suggestion,
15
+ :style,
16
+ ]
17
+
18
+ CATEGORIES = [
19
+ :liquid,
20
+ :translation,
21
+ :json,
22
+ ]
23
+
24
+ class << self
25
+ def all
26
+ @all ||= []
27
+ end
28
+
29
+ def severity(severity = nil)
30
+ if severity
31
+ unless SEVERITIES.include?(severity)
32
+ raise ArgumentError, "unknown severity. Use: #{SEVERITIES.join(', ')}"
33
+ end
34
+ @severity = severity
35
+ end
36
+ @severity if defined?(@severity)
37
+ end
38
+
39
+ def category(category = nil)
40
+ if category
41
+ unless CATEGORIES.include?(category)
42
+ raise ArgumentError, "unknown category. Use: #{CATEGORIES.join(', ')}"
43
+ end
44
+ @category = category
45
+ end
46
+ @category if defined?(@category)
47
+ end
48
+
49
+ def doc(doc = nil)
50
+ @doc = doc if doc
51
+ @doc if defined?(@doc)
52
+ end
53
+ end
54
+
55
+ def severity
56
+ self.class.severity
57
+ end
58
+
59
+ def category
60
+ self.class.category
61
+ end
62
+
63
+ def doc
64
+ self.class.doc
65
+ end
66
+
67
+ def code_name
68
+ self.class.name.demodulize
69
+ end
70
+
71
+ def ignore!
72
+ @ignored = true
73
+ end
74
+
75
+ def unignore!
76
+ @ignored = false
77
+ end
78
+
79
+ def ignored?
80
+ defined?(@ignored) && @ignored
81
+ end
82
+
83
+ def to_s
84
+ s = +"#{code_name}:\n"
85
+ properties = { severity: severity, category: category, doc: doc }.merge(options)
86
+ properties.each_pair do |name, value|
87
+ s << " #{name}: #{value}\n" if value
88
+ end
89
+ s
90
+ end
91
+ end
92
+ end