theme-check 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -3
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +5 -3
  5. data/README.md +2 -0
  6. data/config/default.yml +8 -1
  7. data/data/shopify_translation_keys.yml +850 -0
  8. data/docs/checks/asset_size_css.md +52 -0
  9. data/docs/checks/img_width_and_height.md +79 -0
  10. data/docs/checks/parser_blocking_javascript.md +3 -3
  11. data/lib/theme_check/checks/asset_size_css.rb +89 -0
  12. data/lib/theme_check/checks/asset_size_javascript.rb +1 -7
  13. data/lib/theme_check/checks/img_width_and_height.rb +74 -0
  14. data/lib/theme_check/checks/parser_blocking_javascript.rb +5 -13
  15. data/lib/theme_check/checks/translation_key_exists.rb +16 -1
  16. data/lib/theme_check/cli.rb +19 -8
  17. data/lib/theme_check/config.rb +3 -0
  18. data/lib/theme_check/in_memory_storage.rb +11 -3
  19. data/lib/theme_check/language_server.rb +2 -0
  20. data/lib/theme_check/language_server/completion_engine.rb +1 -1
  21. data/lib/theme_check/language_server/completion_provider.rb +4 -0
  22. data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +5 -1
  23. data/lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb +43 -0
  24. data/lib/theme_check/language_server/constants.rb +10 -0
  25. data/lib/theme_check/language_server/document_link_engine.rb +47 -0
  26. data/lib/theme_check/language_server/handler.rb +33 -2
  27. data/lib/theme_check/language_server/server.rb +3 -2
  28. data/lib/theme_check/liquid_check.rb +11 -0
  29. data/lib/theme_check/remote_asset_file.rb +1 -1
  30. data/lib/theme_check/shopify_liquid/deprecated_filter.rb +4 -0
  31. data/lib/theme_check/version.rb +1 -1
  32. metadata +10 -2
@@ -10,6 +10,8 @@ module ThemeCheck
10
10
  attr_accessor :only_categories, :exclude_categories, :auto_correct
11
11
 
12
12
  class << self
13
+ attr_reader :last_loaded_config
14
+
13
15
  def from_path(path)
14
16
  if (filename = find(path))
15
17
  new(root: filename.dirname, configuration: load_file(filename))
@@ -36,6 +38,7 @@ module ThemeCheck
36
38
  end
37
39
 
38
40
  def load_file(absolute_path)
41
+ @last_loaded_config = absolute_path
39
42
  YAML.load_file(absolute_path)
40
43
  end
41
44
 
@@ -6,20 +6,28 @@
6
6
  # as a big hash already, leave it like that and save yourself some IO.
7
7
  module ThemeCheck
8
8
  class InMemoryStorage < Storage
9
- def initialize(files = {})
9
+ def initialize(files = {}, root = nil)
10
10
  @files = files
11
+ @root = root
11
12
  end
12
13
 
13
14
  def path(name)
15
+ return File.join(@root, name) unless @root.nil?
16
+ name
17
+ end
18
+
19
+ def relative_path(name)
20
+ path = Pathname.new(name)
21
+ return path.relative_path_from(@root).to_s unless path.relative? || @root.nil?
14
22
  name
15
23
  end
16
24
 
17
25
  def read(name)
18
- @files[name]
26
+ @files[relative_path(name)]
19
27
  end
20
28
 
21
29
  def write(name, content)
22
- @files[name] = content
30
+ @files[relative_path(name)] = content
23
31
  end
24
32
 
25
33
  def files
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative "language_server/protocol"
3
+ require_relative "language_server/constants"
3
4
  require_relative "language_server/handler"
4
5
  require_relative "language_server/server"
5
6
  require_relative "language_server/tokens"
@@ -7,6 +8,7 @@ require_relative "language_server/position_helper"
7
8
  require_relative "language_server/completion_helper"
8
9
  require_relative "language_server/completion_provider"
9
10
  require_relative "language_server/completion_engine"
11
+ require_relative "language_server/document_link_engine"
10
12
 
11
13
  Dir[__dir__ + "/language_server/completion_providers/*.rb"].each do |file|
12
14
  require file
@@ -7,7 +7,7 @@ module ThemeCheck
7
7
 
8
8
  def initialize(storage)
9
9
  @storage = storage
10
- @providers = CompletionProvider.all.map(&:new)
10
+ @providers = CompletionProvider.all.map { |x| x.new(storage) }
11
11
  end
12
12
 
13
13
  def completions(name, line, col)
@@ -16,6 +16,10 @@ module ThemeCheck
16
16
  end
17
17
  end
18
18
 
19
+ def initialize(storage = InMemoryStorage.new)
20
+ @storage = storage
21
+ end
22
+
19
23
  def completions(content, cursor)
20
24
  raise NotImplementedError
21
25
  end
@@ -7,7 +7,7 @@ module ThemeCheck
7
7
 
8
8
  def completions(content, cursor)
9
9
  return [] unless can_complete?(content, cursor)
10
- ShopifyLiquid::Filter.labels
10
+ available_labels
11
11
  .select { |w| w.starts_with?(partial(content, cursor)) }
12
12
  .map { |filter| filter_to_completion(filter) }
13
13
  end
@@ -21,6 +21,10 @@ module ThemeCheck
21
21
 
22
22
  private
23
23
 
24
+ def available_labels
25
+ @labels ||= ShopifyLiquid::Filter.labels - ShopifyLiquid::DeprecatedFilter.labels
26
+ end
27
+
24
28
  def cursor_on_filter?(content, cursor)
25
29
  return false unless content.match?(NAMED_FILTER)
26
30
  matches(content, NAMED_FILTER).any? do |match|
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class RenderSnippetCompletionProvider < CompletionProvider
6
+ def completions(content, cursor)
7
+ return [] unless cursor_on_quoted_argument?(content, cursor)
8
+ partial = snippet(content) || ''
9
+ snippets
10
+ .select { |x| x.starts_with?(partial) }
11
+ .map { |x| snippet_to_completion(x) }
12
+ end
13
+
14
+ private
15
+
16
+ def cursor_on_quoted_argument?(content, cursor)
17
+ match = content.match(PARTIAL_RENDER)
18
+ return false if match.nil?
19
+ match.begin(:partial) <= cursor && cursor <= match.end(:partial)
20
+ end
21
+
22
+ def snippet(content)
23
+ match = content.match(PARTIAL_RENDER)
24
+ return if match.nil?
25
+ match[:partial]
26
+ end
27
+
28
+ def snippets
29
+ @storage
30
+ .files
31
+ .select { |x| x.include?('snippets/') }
32
+ end
33
+
34
+ def snippet_to_completion(file)
35
+ {
36
+ label: File.basename(file, '.liquid'),
37
+ kind: CompletionItemKinds::SNIPPET,
38
+ detail: file,
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ PARTIAL_RENDER = %r{
6
+ \{\%\s*render\s+'(?<partial>[^']*)'|
7
+ \{\%\s*render\s+"(?<partial>[^"]*)"
8
+ }mix
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module LanguageServer
5
+ class DocumentLinkEngine
6
+ include PositionHelper
7
+ include RegexHelpers
8
+
9
+ def initialize(storage)
10
+ @storage = storage
11
+ end
12
+
13
+ def document_links(uri)
14
+ buffer = @storage.read(uri)
15
+ matches(buffer, PARTIAL_RENDER).map do |match|
16
+ start_line, start_character = from_index_to_line_column(
17
+ buffer,
18
+ match.begin(:partial),
19
+ )
20
+
21
+ end_line, end_character = from_index_to_line_column(
22
+ buffer,
23
+ match.end(:partial)
24
+ )
25
+
26
+ {
27
+ target: link(match[:partial]),
28
+ range: {
29
+ start: {
30
+ line: start_line,
31
+ character: start_character,
32
+ },
33
+ end: {
34
+ line: end_line,
35
+ character: end_character,
36
+ },
37
+ },
38
+ }
39
+ end
40
+ end
41
+
42
+ def link(partial)
43
+ 'file://' + @storage.path('snippets/' + partial + '.liquid')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -8,6 +8,7 @@ module ThemeCheck
8
8
  triggerCharacters: ['.', '{{ ', '{% '],
9
9
  context: true,
10
10
  },
11
+ documentLinkProvider: true,
11
12
  textDocumentSync: {
12
13
  openClose: true,
13
14
  change: TextDocumentSyncKind::FULL,
@@ -19,12 +20,13 @@ module ThemeCheck
19
20
  def initialize(server)
20
21
  @server = server
21
22
  @previously_reported_files = Set.new
22
- @storage = InMemoryStorage.new
23
- @completion_engine = CompletionEngine.new(@storage)
24
23
  end
25
24
 
26
25
  def on_initialize(id, params)
27
26
  @root_path = params["rootPath"]
27
+ @storage = in_memory_storage(@root_path)
28
+ @completion_engine = CompletionEngine.new(@storage)
29
+ @document_link_engine = DocumentLinkEngine.new(@storage)
28
30
  # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#responseMessage
29
31
  send_response(
30
32
  id: id,
@@ -59,6 +61,14 @@ module ThemeCheck
59
61
  analyze_and_send_offenses(text_document_uri(params))
60
62
  end
61
63
 
64
+ def on_text_document_document_link(id, params)
65
+ uri = text_document_uri(params)
66
+ send_response(
67
+ id: id,
68
+ result: document_links(uri)
69
+ )
70
+ end
71
+
62
72
  def on_text_document_completion(id, params)
63
73
  uri = text_document_uri(params)
64
74
  line = params.dig('position', 'line')
@@ -71,6 +81,23 @@ module ThemeCheck
71
81
 
72
82
  private
73
83
 
84
+ def in_memory_storage(root)
85
+ config = ThemeCheck::Config.from_path(root)
86
+
87
+ # Make a real FS to get the files from the snippets folder
88
+ fs = ThemeCheck::FileSystemStorage.new(
89
+ config.root,
90
+ ignored_patterns: config.ignored_patterns
91
+ )
92
+
93
+ # Turn that into a hash of empty buffers
94
+ files = fs.files
95
+ .map { |fn| [fn, ""] }
96
+ .to_h
97
+
98
+ InMemoryStorage.new(files, root)
99
+ end
100
+
74
101
  def text_document_uri(params)
75
102
  params.dig('textDocument', 'uri').sub('file://', '')
76
103
  end
@@ -108,6 +135,10 @@ module ThemeCheck
108
135
  @completion_engine.completions(uri, line, col)
109
136
  end
110
137
 
138
+ def document_links(uri)
139
+ @document_link_engine.document_links(uri)
140
+ end
141
+
111
142
  def send_diagnostics(offenses)
112
143
  reported_files = Set.new
113
144
 
@@ -6,6 +6,7 @@ require 'active_support/core_ext/string/inflections'
6
6
  module ThemeCheck
7
7
  module LanguageServer
8
8
  class DoneStreaming < StandardError; end
9
+
9
10
  class IncompatibleStream < StandardError; end
10
11
 
11
12
  class Server
@@ -15,7 +16,7 @@ module ThemeCheck
15
16
  def initialize(
16
17
  in_stream: STDIN,
17
18
  out_stream: STDOUT,
18
- err_stream: $DEBUG ? File.open('/tmp/lsp.log', 'a') : STDERR,
19
+ err_stream: STDERR,
19
20
  should_raise_errors: false
20
21
  )
21
22
  validate!([in_stream, out_stream, err_stream])
@@ -50,7 +51,7 @@ module ThemeCheck
50
51
 
51
52
  def send_response(response)
52
53
  response_body = JSON.dump(response)
53
- log(response_body) if $DEBUG
54
+ log(JSON.pretty_generate(response)) if $DEBUG
54
55
 
55
56
  @out.write("Content-Length: #{response_body.size}\r\n")
56
57
  @out.write("\r\n")
@@ -6,6 +6,17 @@ module ThemeCheck
6
6
  extend ChecksTracking
7
7
  include ParsingHelpers
8
8
 
9
+ TAG = /#{Liquid::TagStart}.*?#{Liquid::TagEnd}/om
10
+ VARIABLE = /#{Liquid::VariableStart}.*?#{Liquid::VariableEnd}/om
11
+ START_OR_END_QUOTE = /(^['"])|(['"]$)/
12
+ QUOTED_LIQUID_ATTRIBUTE = %r{
13
+ '(?:#{TAG}|#{VARIABLE}|[^'])*'| # any combination of tag/variable or non straight quote inside straight quotes
14
+ "(?:#{TAG}|#{VARIABLE}|[^"])*" # any combination of tag/variable or non double quotes inside double quotes
15
+ }omix
16
+ ATTR = /[a-z0-9-]+/i
17
+ HTML_ATTRIBUTE = /#{ATTR}(?:=#{QUOTED_LIQUID_ATTRIBUTE})?/omix
18
+ HTML_ATTRIBUTES = /(?:#{HTML_ATTRIBUTE}|\s)*/omix
19
+
9
20
  def add_offense(message, node: nil, template: node&.template, markup: nil, line_number: nil, &block)
10
21
  offenses << Offense.new(check: self, message: message, template: template, node: node, markup: markup, line_number: line_number, correction: block)
11
22
  end
@@ -38,7 +38,7 @@ module ThemeCheck
38
38
  end
39
39
 
40
40
  def gzipped_size
41
- @gzipped_size ||= @content.bytesize
41
+ @gzipped_size ||= content.bytesize
42
42
  end
43
43
  end
44
44
  end
@@ -10,6 +10,10 @@ module ThemeCheck
10
10
  all.fetch(filter, nil)
11
11
  end
12
12
 
13
+ def labels
14
+ @labels ||= all.keys
15
+ end
16
+
13
17
  private
14
18
 
15
19
  def all
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ThemeCheck
3
- VERSION = "0.5.0"
3
+ VERSION = "0.6.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theme-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Cournoyer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-12 00:00:00.000000000 Z
11
+ date: 2021-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -81,12 +81,15 @@ files:
81
81
  - data/shopify_liquid/objects.yml
82
82
  - data/shopify_liquid/plus_objects.yml
83
83
  - data/shopify_liquid/tags.yml
84
+ - data/shopify_translation_keys.yml
84
85
  - dev.yml
85
86
  - docs/checks/CHECK_DOCS_TEMPLATE.md
87
+ - docs/checks/asset_size_css.md
86
88
  - docs/checks/asset_size_javascript.md
87
89
  - docs/checks/convert_include_to_render.md
88
90
  - docs/checks/default_locale.md
89
91
  - docs/checks/deprecated_filter.md
92
+ - docs/checks/img_width_and_height.md
90
93
  - docs/checks/liquid_tag.md
91
94
  - docs/checks/matching_schema_translations.md
92
95
  - docs/checks/matching_translations.md
@@ -116,10 +119,12 @@ files:
116
119
  - lib/theme_check/asset_file.rb
117
120
  - lib/theme_check/check.rb
118
121
  - lib/theme_check/checks.rb
122
+ - lib/theme_check/checks/asset_size_css.rb
119
123
  - lib/theme_check/checks/asset_size_javascript.rb
120
124
  - lib/theme_check/checks/convert_include_to_render.rb
121
125
  - lib/theme_check/checks/default_locale.rb
122
126
  - lib/theme_check/checks/deprecated_filter.rb
127
+ - lib/theme_check/checks/img_width_and_height.rb
123
128
  - lib/theme_check/checks/liquid_tag.rb
124
129
  - lib/theme_check/checks/matching_schema_translations.rb
125
130
  - lib/theme_check/checks/matching_translations.rb
@@ -157,7 +162,10 @@ files:
157
162
  - lib/theme_check/language_server/completion_provider.rb
158
163
  - lib/theme_check/language_server/completion_providers/filter_completion_provider.rb
159
164
  - lib/theme_check/language_server/completion_providers/object_completion_provider.rb
165
+ - lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb
160
166
  - lib/theme_check/language_server/completion_providers/tag_completion_provider.rb
167
+ - lib/theme_check/language_server/constants.rb
168
+ - lib/theme_check/language_server/document_link_engine.rb
161
169
  - lib/theme_check/language_server/handler.rb
162
170
  - lib/theme_check/language_server/position_helper.rb
163
171
  - lib/theme_check/language_server/protocol.rb