theme-check 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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