theme-check 1.11.0 → 1.12.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +18 -0
  4. data/CONTRIBUTING.md +82 -0
  5. data/README.md +4 -0
  6. data/Rakefile +7 -0
  7. data/TROUBLESHOOTING.md +65 -0
  8. data/data/shopify_liquid/built_in_liquid_objects.json +60 -0
  9. data/data/shopify_liquid/deprecated_filters.json +22 -0
  10. data/data/shopify_liquid/documentation/filters.json +5528 -0
  11. data/data/shopify_liquid/documentation/latest.json +1 -0
  12. data/data/shopify_liquid/documentation/objects.json +19272 -0
  13. data/data/shopify_liquid/documentation/tags.json +1252 -0
  14. data/data/shopify_liquid/plus_labels.json +15 -0
  15. data/data/shopify_liquid/theme_app_extension_labels.json +3 -0
  16. data/lib/theme_check/checks/undefined_object.rb +4 -0
  17. data/lib/theme_check/language_server/completion_context.rb +52 -0
  18. data/lib/theme_check/language_server/completion_engine.rb +15 -21
  19. data/lib/theme_check/language_server/completion_provider.rb +26 -1
  20. data/lib/theme_check/language_server/completion_providers/assignments_completion_provider.rb +40 -0
  21. data/lib/theme_check/language_server/completion_providers/filter_completion_provider.rb +49 -6
  22. data/lib/theme_check/language_server/completion_providers/object_attribute_completion_provider.rb +48 -0
  23. data/lib/theme_check/language_server/completion_providers/object_completion_provider.rb +15 -10
  24. data/lib/theme_check/language_server/completion_providers/render_snippet_completion_provider.rb +5 -1
  25. data/lib/theme_check/language_server/completion_providers/tag_completion_provider.rb +8 -1
  26. data/lib/theme_check/language_server/handler.rb +3 -1
  27. data/lib/theme_check/language_server/protocol.rb +9 -0
  28. data/lib/theme_check/language_server/type_helper.rb +22 -0
  29. data/lib/theme_check/language_server/variable_lookup_finder/assignments_finder/node_handler.rb +63 -0
  30. data/lib/theme_check/language_server/variable_lookup_finder/assignments_finder/scope.rb +60 -0
  31. data/lib/theme_check/language_server/variable_lookup_finder/assignments_finder/scope_visitor.rb +44 -0
  32. data/lib/theme_check/language_server/variable_lookup_finder/assignments_finder.rb +76 -0
  33. data/lib/theme_check/language_server/variable_lookup_finder/constants.rb +44 -0
  34. data/lib/theme_check/language_server/variable_lookup_finder/liquid_fixer.rb +103 -0
  35. data/lib/theme_check/language_server/variable_lookup_finder/potential_lookup.rb +10 -0
  36. data/lib/theme_check/language_server/variable_lookup_finder/tolerant_parser.rb +114 -0
  37. data/lib/theme_check/language_server/variable_lookup_finder.rb +67 -100
  38. data/lib/theme_check/language_server/variable_lookup_traverser.rb +70 -0
  39. data/lib/theme_check/language_server.rb +12 -0
  40. data/lib/theme_check/remote_asset_file.rb +13 -7
  41. data/lib/theme_check/shopify_liquid/deprecated_filter.rb +1 -1
  42. data/lib/theme_check/shopify_liquid/documentation/markdown_template.rb +51 -0
  43. data/lib/theme_check/shopify_liquid/documentation.rb +44 -0
  44. data/lib/theme_check/shopify_liquid/filter.rb +35 -3
  45. data/lib/theme_check/shopify_liquid/object.rb +8 -3
  46. data/lib/theme_check/shopify_liquid/source_index/base_entry.rb +60 -0
  47. data/lib/theme_check/shopify_liquid/source_index/base_state.rb +23 -0
  48. data/lib/theme_check/shopify_liquid/source_index/filter_entry.rb +18 -0
  49. data/lib/theme_check/shopify_liquid/source_index/filter_state.rb +11 -0
  50. data/lib/theme_check/shopify_liquid/source_index/object_entry.rb +14 -0
  51. data/lib/theme_check/shopify_liquid/source_index/object_state.rb +11 -0
  52. data/lib/theme_check/shopify_liquid/source_index/parameter_entry.rb +21 -0
  53. data/lib/theme_check/shopify_liquid/source_index/property_entry.rb +9 -0
  54. data/lib/theme_check/shopify_liquid/source_index/return_type_entry.rb +37 -0
  55. data/lib/theme_check/shopify_liquid/source_index/tag_entry.rb +20 -0
  56. data/lib/theme_check/shopify_liquid/source_index/tag_state.rb +11 -0
  57. data/lib/theme_check/shopify_liquid/source_index.rb +76 -0
  58. data/lib/theme_check/shopify_liquid/source_manager.rb +111 -0
  59. data/lib/theme_check/shopify_liquid/tag.rb +11 -1
  60. data/lib/theme_check/shopify_liquid.rb +17 -1
  61. data/lib/theme_check/version.rb +1 -1
  62. data/shipit.rubygems.yml +3 -0
  63. data/theme-check.gemspec +3 -1
  64. metadata +40 -8
  65. data/data/shopify_liquid/deprecated_filters.yml +0 -14
  66. data/data/shopify_liquid/filters.yml +0 -211
  67. data/data/shopify_liquid/objects.yml +0 -84
  68. data/data/shopify_liquid/plus_objects.yml +0 -15
  69. data/data/shopify_liquid/tags.yml +0 -30
  70. data/data/shopify_liquid/theme_app_extension_objects.yml +0 -2
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "language_server/protocol"
3
4
  require_relative "language_server/constants"
4
5
  require_relative "language_server/configuration"
@@ -7,9 +8,19 @@ require_relative "language_server/messenger"
7
8
  require_relative "language_server/io_messenger"
8
9
  require_relative "language_server/bridge"
9
10
  require_relative "language_server/uri_helper"
11
+ require_relative "language_server/type_helper"
10
12
  require_relative "language_server/server"
11
13
  require_relative "language_server/tokens"
14
+ require_relative "language_server/variable_lookup_finder/potential_lookup"
15
+ require_relative "language_server/variable_lookup_finder/tolerant_parser"
16
+ require_relative "language_server/variable_lookup_finder/assignments_finder/node_handler"
17
+ require_relative "language_server/variable_lookup_finder/assignments_finder/scope_visitor"
18
+ require_relative "language_server/variable_lookup_finder/assignments_finder/scope"
19
+ require_relative "language_server/variable_lookup_finder/assignments_finder"
20
+ require_relative "language_server/variable_lookup_finder/constants"
21
+ require_relative "language_server/variable_lookup_finder/liquid_fixer"
12
22
  require_relative "language_server/variable_lookup_finder"
23
+ require_relative "language_server/variable_lookup_traverser"
13
24
  require_relative "language_server/diagnostic"
14
25
  require_relative "language_server/diagnostics_manager"
15
26
  require_relative "language_server/diagnostics_engine"
@@ -17,6 +28,7 @@ require_relative "language_server/document_change_corrector"
17
28
  require_relative "language_server/versioned_in_memory_storage"
18
29
  require_relative "language_server/client_capabilities"
19
30
 
31
+ require_relative "language_server/completion_context"
20
32
  require_relative "language_server/completion_helper"
21
33
  require_relative "language_server/completion_provider"
22
34
  require_relative "language_server/completion_engine"
@@ -31,13 +31,7 @@ module ThemeCheck
31
31
  return if @uri.nil?
32
32
  return @content unless @content.nil?
33
33
 
34
- res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: @uri.scheme == 'https') do |http|
35
- req = Net::HTTP::Get.new(@uri)
36
- req['Accept-Encoding'] = 'gzip, deflate, br'
37
- http.request(req)
38
- end
39
-
40
- @content = res.body
34
+ @content = request(@uri)
41
35
 
42
36
  rescue OpenSSL::SSL::SSLError, Zlib::StreamError, *NET_HTTP_EXCEPTIONS
43
37
  @contents = ''
@@ -47,5 +41,17 @@ module ThemeCheck
47
41
  return if @uri.nil?
48
42
  @gzipped_size ||= content.bytesize
49
43
  end
44
+
45
+ private
46
+
47
+ def request(uri)
48
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
49
+ req = Net::HTTP::Get.new(uri)
50
+ req['Accept-Encoding'] = 'gzip, deflate, br'
51
+ http.request(req)
52
+ end
53
+
54
+ res.body
55
+ end
50
56
  end
51
57
  end
@@ -17,7 +17,7 @@ module ThemeCheck
17
17
  private
18
18
 
19
19
  def all
20
- @all ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/deprecated_filters.yml"))
20
+ @all ||= SourceIndex.deprecated_filters
21
21
  .values
22
22
  .each_with_object({}) do |filters, acc|
23
23
  filters.each do |(filter, alternatives)|
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class Documentation
6
+ class MarkdownTemplate
7
+ MARKDOWN_RELATIVE_LINK = %r{(\[([^\[]+)\]\((/[^\)]+)\))*}
8
+
9
+ def render(entry)
10
+ [
11
+ title(entry),
12
+ body(entry),
13
+ ].reject(&:empty?).join("\n")
14
+ end
15
+
16
+ private
17
+
18
+ def title(entry)
19
+ "### #{entry.name}"
20
+ end
21
+
22
+ def body(entry)
23
+ [entry.deprecation_reason, entry.summary, entry.description]
24
+ .reject(&:nil?)
25
+ .reject(&:empty?)
26
+ .join(horizontal_rule)
27
+ .tap { |body| break(patch_urls!(body)) }
28
+ end
29
+
30
+ def horizontal_rule
31
+ "\n\n---\n\n"
32
+ end
33
+
34
+ def patch_urls!(body)
35
+ body.gsub(MARKDOWN_RELATIVE_LINK) do |original_link|
36
+ match = Regexp.last_match
37
+
38
+ text = match[2]
39
+ path = match[3]
40
+
41
+ if text && path
42
+ "[#{text}](https://shopify.dev#{path})"
43
+ else
44
+ original_link
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'documentation/markdown_template'
4
+
5
+ module ThemeCheck
6
+ module ShopifyLiquid
7
+ class Documentation
8
+ class << self
9
+ def filter_doc(filter_name)
10
+ render_doc(SourceIndex.filters.find { |entry| entry.name == filter_name })
11
+ end
12
+
13
+ def object_doc(object_name)
14
+ render_doc(SourceIndex.objects.find { |entry| entry.name == object_name })
15
+ end
16
+
17
+ def tag_doc(tag_name)
18
+ render_doc(SourceIndex.tags.find { |entry| entry.name == tag_name })
19
+ end
20
+
21
+ def object_property_doc(object_name, property_name)
22
+ property_entry = SourceIndex
23
+ .objects
24
+ .find { |entry| entry.name == object_name }
25
+ &.properties
26
+ &.find { |prop| prop.name == property_name }
27
+
28
+ render_doc(property_entry)
29
+ end
30
+
31
+ def render_doc(entry)
32
+ return nil unless entry
33
+ markdown_template.render(entry)
34
+ end
35
+
36
+ private
37
+
38
+ def markdown_template
39
+ @markdown_template ||= MarkdownTemplate.new
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -6,10 +6,42 @@ module ThemeCheck
6
6
  module Filter
7
7
  extend self
8
8
 
9
+ LABELS_NOT_IN_SOURCE_INDEX = [
10
+ "h",
11
+ "installments_pricing",
12
+ "sentence",
13
+ "t",
14
+ "app_block_path_for",
15
+ "dev_shop?",
16
+ "app_extension_path_for",
17
+ "global_block_type?",
18
+ "app_block_path?",
19
+ "app_extension_path?",
20
+ "app_snippet_path?",
21
+ "registration_uuid_from",
22
+ "handle_from",
23
+ "camelcase",
24
+ "format_code",
25
+ "handle",
26
+ "encode_url_component",
27
+ "recover_password_link",
28
+ "delete_customer_address_link",
29
+ "edit_customer_address_link",
30
+ "cancel_customer_order_link",
31
+ "unit",
32
+ "weight",
33
+ "paragraphize",
34
+ "excerpt",
35
+ "pad_spaces",
36
+ "distance_from",
37
+ "theme_url",
38
+ "link_to_theme",
39
+ "_online_store_editor_live_setting",
40
+ "debug",
41
+ ]
42
+
9
43
  def labels
10
- @labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/filters.yml"))
11
- .values
12
- .flatten
44
+ @labels ||= SourceIndex.filters.map(&:name) + LABELS_NOT_IN_SOURCE_INDEX
13
45
  end
14
46
  end
15
47
  end
@@ -6,16 +6,21 @@ module ThemeCheck
6
6
  module Object
7
7
  extend self
8
8
 
9
+ LABELS_NOT_IN_SOURCE_INDEX = [
10
+ "customer_address",
11
+ "product_variant",
12
+ ].freeze
13
+
9
14
  def labels
10
- @labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/objects.yml"))
15
+ @labels ||= SourceIndex.objects.map(&:name) + LABELS_NOT_IN_SOURCE_INDEX
11
16
  end
12
17
 
13
18
  def plus_labels
14
- @plus_labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/plus_objects.yml"))
19
+ @plus_labels ||= SourceIndex.plus_labels
15
20
  end
16
21
 
17
22
  def theme_app_extension_labels
18
- @theme_app_extension_labels ||= YAML.load(File.read("#{__dir__}/../../../data/shopify_liquid/theme_app_extension_objects.yml"))
23
+ @theme_app_extension_labels ||= SourceIndex.theme_app_extension_labels
19
24
  end
20
25
  end
21
26
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module ThemeCheck
6
+ module ShopifyLiquid
7
+ class SourceIndex
8
+ class BaseEntry
9
+ extend Forwardable
10
+
11
+ attr_reader :hash
12
+
13
+ def_delegators :return_type_instance, :generic_type?, :array_type?, :array_type, :to_s
14
+
15
+ def initialize(hash = {})
16
+ @hash = hash || {}
17
+ @return_type = nil
18
+ end
19
+
20
+ def name
21
+ hash['name']
22
+ end
23
+
24
+ def summary
25
+ hash['summary'] || ''
26
+ end
27
+
28
+ def description
29
+ hash['description'] || ''
30
+ end
31
+
32
+ def deprecated?
33
+ hash['deprecated']
34
+ end
35
+
36
+ def deprecation_reason
37
+ return nil unless deprecated?
38
+
39
+ hash['deprecation_reason'] || nil
40
+ end
41
+
42
+ attr_writer :return_type
43
+
44
+ def return_type
45
+ @return_type || to_s
46
+ end
47
+
48
+ def return_type_instance
49
+ ReturnTypeEntry.new(return_type_hash)
50
+ end
51
+
52
+ private
53
+
54
+ def return_type_hash
55
+ hash['return_type']&.first
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class BaseState
7
+ class << self
8
+ def mark_outdated
9
+ @up_to_date = false
10
+ end
11
+
12
+ def mark_up_to_date
13
+ @up_to_date = true
14
+ end
15
+
16
+ def outdated?
17
+ @up_to_date == false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class FilterEntry < BaseEntry
7
+ def parameters
8
+ (hash['parameters'] || [])
9
+ .map { |hash| ParameterEntry.new(hash) }
10
+ end
11
+
12
+ def input_type
13
+ hash['syntax'].split(' | ')[0]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class FilterState < BaseState
7
+ @up_to_date = false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class ObjectEntry < BaseEntry
7
+ def properties
8
+ (hash['properties'] || [])
9
+ .map { |hash| PropertyEntry.new(hash) }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class ObjectState < BaseState
7
+ @up_to_date = false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class ParameterEntry < BaseEntry
7
+ def summary
8
+ nil
9
+ end
10
+
11
+ private
12
+
13
+ def return_type_hash
14
+ {
15
+ 'type' => (hash['types'] || ['untyped']).first,
16
+ }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class PropertyEntry < BaseEntry; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class ReturnTypeEntry < BaseEntry
7
+ def summary
8
+ nil
9
+ end
10
+
11
+ def to_s
12
+ hash['type']
13
+ end
14
+
15
+ def generic_type?
16
+ hash['type'] == 'generic'
17
+ end
18
+
19
+ def array_type?
20
+ !array_type.nil? && !array_type.empty?
21
+ end
22
+
23
+ def array_type
24
+ hash['array_value']
25
+ end
26
+
27
+ private
28
+
29
+ def return_type_hash
30
+ {
31
+ 'type' => "type<#{self}>",
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class TagEntry < BaseEntry
7
+ def parameters
8
+ (hash['parameters'] || [])
9
+ .map { |hash| ParameterEntry.new(hash) }
10
+ end
11
+
12
+ def return_type_hash
13
+ {
14
+ 'type' => "tag<#{name}>",
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ThemeCheck
4
+ module ShopifyLiquid
5
+ class SourceIndex
6
+ class TagState < BaseState
7
+ @up_to_date = false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'pathname'
5
+
6
+ module ThemeCheck
7
+ module ShopifyLiquid
8
+ class SourceIndex
9
+ class << self
10
+ def filters
11
+ @filters = nil if FilterState.outdated?
12
+
13
+ @filters ||= FilterState.mark_up_to_date &&
14
+ load_file(:filters)
15
+ .map { |hash| FilterEntry.new(hash) }
16
+ end
17
+
18
+ def objects
19
+ @objects = nil if ObjectState.outdated?
20
+
21
+ @objects ||= ObjectState.mark_up_to_date &&
22
+ load_file(:objects)
23
+ .concat(built_in_objects)
24
+ .filter_map do |hash|
25
+ next if (theme_app_extension_labels + labels_only_exposed_in_certain_contexts).include?(hash['name'])
26
+
27
+ ObjectEntry.new(hash)
28
+ end
29
+ end
30
+
31
+ def tags
32
+ @tags = nil if TagState.outdated?
33
+
34
+ @tags ||= TagState.mark_up_to_date &&
35
+ load_file(:tags)
36
+ .map { |hash| TagEntry.new(hash) }
37
+ end
38
+
39
+ def plus_labels
40
+ @plus_objects ||= load_file("../plus_labels")
41
+ end
42
+
43
+ def theme_app_extension_labels
44
+ @theme_app_extension_labels ||= load_file("../theme_app_extension_labels")
45
+ end
46
+
47
+ def labels_only_exposed_in_certain_contexts
48
+ ['robots'].freeze
49
+ end
50
+
51
+ def deprecated_filters
52
+ @deprecated_filters ||= load_file("../deprecated_filters")
53
+ end
54
+
55
+ private
56
+
57
+ def load_file(file_name)
58
+ read_json(local_path!(file_name))
59
+ end
60
+
61
+ def local_path!(file_name)
62
+ SourceManager.download unless SourceManager.has_required_files?
63
+ SourceManager.local_path(file_name)
64
+ end
65
+
66
+ def read_json(path)
67
+ JSON.parse(path.read)
68
+ end
69
+
70
+ def built_in_objects
71
+ load_file('../built_in_liquid_objects')
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'pathname'
5
+ require 'tmpdir'
6
+
7
+ module ThemeCheck
8
+ module ShopifyLiquid
9
+ class SourceManager
10
+ REQUIRED_FILE_NAMES = [:filters, :objects, :tags, :latest].freeze
11
+
12
+ class << self
13
+ def download_or_refresh_files(destination = default_destination)
14
+ if has_required_files?(destination)
15
+ refresh(destination)
16
+ else
17
+ download(destination)
18
+ end
19
+ end
20
+
21
+ def download(destination = default_destination)
22
+ Dir.mkdir(destination) unless destination.exist?
23
+
24
+ REQUIRED_FILE_NAMES.each do |file_name|
25
+ download_file(local_path(file_name, destination), remote_path(file_name))
26
+ end
27
+ end
28
+
29
+ def refresh(destination = default_destination)
30
+ refresh_threads << Thread.new { refresh_thread(destination) }
31
+ end
32
+
33
+ def local_path(file_name, destination = default_destination)
34
+ destination + "#{file_name}.json"
35
+ end
36
+
37
+ def has_required_files?(destination = default_destination)
38
+ REQUIRED_FILE_NAMES.all? { |file_name| local_path(file_name, destination).exist? }
39
+ end
40
+
41
+ def wait_downloads
42
+ refresh_threads.each(&:join)
43
+ end
44
+
45
+ private
46
+
47
+ def refresh_thread(destination)
48
+ return unless refresh_needed?(destination)
49
+
50
+ Dir.mktmpdir do |tmp_dir|
51
+ download(Pathname.new(tmp_dir))
52
+
53
+ FileUtils.cp_r("#{tmp_dir}/.", destination)
54
+
55
+ mark_all_indexes_outdated
56
+ end
57
+ end
58
+
59
+ def refresh_needed?(destination)
60
+ local_latest_content = local_path(:latest, destination).read
61
+ remote_latest_content = open_uri(remote_path(:latest))
62
+
63
+ revision(local_latest_content) != revision(remote_latest_content)
64
+ end
65
+
66
+ def revision(json_content)
67
+ # Raise an error if revision isn't found to avoid returning nil
68
+ JSON.parse(json_content).fetch('revision')
69
+ end
70
+
71
+ def remote_path(file_name)
72
+ "https://raw.githubusercontent.com/Shopify/theme-liquid-docs/main/data/#{file_name}.json"
73
+ end
74
+
75
+ def download_file(local_path, remote_uri)
76
+ File.open(local_path, "wb") do |file|
77
+ content = open_uri(remote_uri)
78
+ file.write(content)
79
+ end
80
+ end
81
+
82
+ def mark_all_indexes_outdated
83
+ SourceIndex::FilterState.mark_outdated
84
+ SourceIndex::ObjectState.mark_outdated
85
+ SourceIndex::TagState.mark_outdated
86
+ end
87
+
88
+ # State
89
+
90
+ def default_destination
91
+ @default_destination ||= Pathname.new("#{__dir__}/../../../data/shopify_liquid/documentation")
92
+ end
93
+
94
+ def refresh_threads
95
+ @refresh_threads ||= []
96
+ end
97
+
98
+ def open_uri(uri_str)
99
+ uri = URI.parse(uri_str)
100
+
101
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
102
+ req = Net::HTTP::Get.new(uri)
103
+ http.request(req)
104
+ end
105
+
106
+ res.body
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end