shopify-cli 2.7.2 → 2.9.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.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +2 -2
- data/.github/workflows/shopify.yml +1 -1
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +52 -0
- data/Codespace.dockerfile +2 -2
- data/Gemfile.lock +4 -4
- data/RELEASING.md +4 -3
- data/Tests.dockerfile +2 -2
- data/dev.yml +3 -3
- data/ext/javy/hashes/javy-arm-macos-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-linux-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-macos-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/hashes/javy-x86_64-windows-v0.1.0.gz.sha256 +1 -0
- data/ext/javy/javy.rb +31 -13
- data/lib/graphql/get_extension_registrations.graphql +27 -0
- data/lib/project_types/extension/cli.rb +27 -2
- data/lib/project_types/extension/commands/build.rb +10 -10
- data/lib/project_types/extension/commands/create.rb +2 -3
- data/lib/project_types/extension/commands/push.rb +36 -8
- data/lib/project_types/extension/extension_project.rb +1 -1
- data/lib/project_types/extension/features/argo_serve.rb +6 -5
- data/lib/project_types/extension/forms/questions/ask_registration.rb +6 -2
- data/lib/project_types/extension/loaders/project.rb +29 -0
- data/lib/project_types/extension/loaders/specification_handler.rb +22 -0
- data/lib/project_types/extension/messages/messages.rb +4 -0
- data/lib/project_types/extension/models/app.rb +1 -1
- data/lib/project_types/extension/models/development_server.rb +2 -4
- data/lib/project_types/extension/models/specification_handlers/default.rb +4 -0
- data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
- data/lib/project_types/extension/tasks/execute_commands/base.rb +13 -0
- data/lib/project_types/extension/tasks/execute_commands/build.rb +29 -0
- data/lib/project_types/extension/tasks/execute_commands/create.rb +33 -0
- data/lib/project_types/extension/tasks/execute_commands/serve.rb +35 -0
- data/lib/project_types/extension/tasks/merge_server_config.rb +33 -22
- data/lib/project_types/rails/commands/create.rb +1 -1
- data/lib/project_types/rails/gem.rb +1 -2
- data/lib/project_types/script/cli.rb +8 -1
- data/lib/project_types/script/commands/connect.rb +19 -0
- data/lib/project_types/script/commands/create.rb +8 -2
- data/lib/project_types/script/commands/push.rb +35 -12
- data/lib/project_types/script/config/extension_points.yml +10 -2
- data/lib/project_types/script/graphql/app_script_set.graphql +2 -2
- data/lib/project_types/script/layers/application/connect_app.rb +15 -3
- data/lib/project_types/script/layers/application/create_script.rb +17 -17
- data/lib/project_types/script/layers/application/extension_points.rb +50 -26
- data/lib/project_types/script/layers/application/push_script.rb +6 -3
- data/lib/project_types/script/layers/domain/errors.rb +1 -4
- data/lib/project_types/script/layers/domain/extension_point.rb +14 -0
- data/lib/project_types/script/layers/domain/push_package.rb +3 -3
- data/lib/project_types/script/layers/domain/{script_json.rb → script_config.rb} +2 -2
- data/lib/project_types/script/layers/domain/script_project.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +30 -5
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +2 -2
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +125 -27
- data/lib/project_types/script/layers/infrastructure/script_service.rb +11 -11
- data/lib/project_types/script/loaders/project.rb +44 -0
- data/lib/project_types/script/loaders/specification_handler.rb +22 -0
- data/lib/project_types/script/messages/messages.rb +34 -3
- data/lib/project_types/script/ui/error_handler.rb +35 -15
- data/lib/project_types/theme/commands/pull.rb +39 -16
- data/lib/project_types/theme/commands/push.rb +60 -29
- data/lib/project_types/theme/commands/serve.rb +5 -0
- data/lib/project_types/theme/messages/messages.rb +30 -18
- data/lib/shopify_cli/command.rb +6 -0
- data/lib/shopify_cli/commands/login.rb +11 -5
- data/lib/shopify_cli/commands/switch.rb +1 -1
- data/lib/shopify_cli/constants.rb +5 -0
- data/lib/shopify_cli/context.rb +66 -11
- data/lib/shopify_cli/environment.rb +15 -4
- data/lib/shopify_cli/form.rb +2 -0
- data/lib/shopify_cli/git.rb +2 -0
- data/lib/shopify_cli/identity_auth.rb +1 -0
- data/lib/shopify_cli/messages/messages.rb +10 -2
- data/lib/shopify_cli/partners_api/app_extensions/job.rb +36 -0
- data/lib/shopify_cli/partners_api/app_extensions.rb +46 -0
- data/lib/shopify_cli/partners_api/organizations.rb +2 -5
- data/lib/shopify_cli/partners_api.rb +1 -0
- data/lib/shopify_cli/project.rb +8 -7
- data/lib/shopify_cli/resources/env_file.rb +18 -6
- data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +73 -0
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +57 -10
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
- data/lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb +84 -0
- data/lib/shopify_cli/theme/dev_server/proxy.rb +10 -15
- data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
- data/lib/shopify_cli/theme/dev_server.rb +8 -21
- data/lib/shopify_cli/theme/theme.rb +26 -4
- data/lib/shopify_cli/thread_pool/job.rb +27 -0
- data/lib/shopify_cli/thread_pool.rb +37 -0
- data/lib/shopify_cli/tunnel.rb +1 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/lib/shopify_cli.rb +4 -0
- data/shopify-cli.gemspec +1 -1
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
- metadata +26 -7
- data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
- data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
data/lib/shopify_cli/git.rb
CHANGED
|
@@ -229,6 +229,7 @@ module ShopifyCLI
|
|
|
229
229
|
uri = URI.parse("#{auth_url}#{endpoint}")
|
|
230
230
|
https = Net::HTTP.new(uri.host, uri.port)
|
|
231
231
|
https.use_ssl = true
|
|
232
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV["SSL_VERIFY_NONE"]
|
|
232
233
|
request = Net::HTTP::Post.new(uri.path)
|
|
233
234
|
request["User-Agent"] = "Shopify CLI #{::ShopifyCLI::VERSION}"
|
|
234
235
|
request.body = URI.encode_www_form(params)
|
|
@@ -14,6 +14,12 @@ module ShopifyCLI
|
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
core: {
|
|
17
|
+
errors: {
|
|
18
|
+
option_parser: {
|
|
19
|
+
invalid_option: "The option {{command:%s}} is not supported.",
|
|
20
|
+
missing_argument: "The required argument {{command:%s}} is missing.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
17
23
|
app: {
|
|
18
24
|
help: <<~HELP,
|
|
19
25
|
Suite of commands for developing apps. See {{command:%1$s app <command> --help}} for usage of each command.
|
|
@@ -409,7 +415,7 @@ module ShopifyCLI
|
|
|
409
415
|
Usage: {{command:%s login [--store=STORE]}}
|
|
410
416
|
HELP
|
|
411
417
|
invalid_shop: <<~MESSAGE,
|
|
412
|
-
Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
|
|
418
|
+
Invalid store provided (%s). Please make sure that the store belongs to your partner organization, and provide the store in the following format: my-store.myshopify.com
|
|
413
419
|
MESSAGE
|
|
414
420
|
shop_prompt: <<~PROMPT,
|
|
415
421
|
What store are you connecting to? (e.g. my-store.myshopify.com; do {{bold:NOT}} include protocol part, e.g., https://)
|
|
@@ -719,7 +725,7 @@ module ShopifyCLI
|
|
|
719
725
|
signup_suggestion: <<~MESSAGE,
|
|
720
726
|
{{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
|
|
721
727
|
account at {{underline:https://ngrok.com/signup}}. After you signup, install your
|
|
722
|
-
personalized authorization token using {{command:%s
|
|
728
|
+
personalized authorization token using {{command:%s app tunnel auth <token>}}.
|
|
723
729
|
MESSAGE
|
|
724
730
|
start: "{{v}} ngrok tunnel running at {{underline:%s}}",
|
|
725
731
|
start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
|
|
@@ -784,6 +790,8 @@ module ShopifyCLI
|
|
|
784
790
|
logged_in_partner_only: "Logged into partner organization {{green:%s}}",
|
|
785
791
|
logged_in_partner_and_shop: "Logged into store {{green:%s}} in partner organization {{green:%s}}",
|
|
786
792
|
},
|
|
793
|
+
error: "Error",
|
|
794
|
+
try_this: "Try this",
|
|
787
795
|
},
|
|
788
796
|
}.freeze
|
|
789
797
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "shopify_cli/thread_pool/job"
|
|
4
|
+
|
|
5
|
+
module ShopifyCLI
|
|
6
|
+
class PartnersAPI
|
|
7
|
+
class AppExtensions
|
|
8
|
+
class Job < ShopifyCLI::ThreadPool::Job
|
|
9
|
+
attr_reader :result
|
|
10
|
+
|
|
11
|
+
def initialize(ctx, app, type)
|
|
12
|
+
super()
|
|
13
|
+
@ctx = ctx
|
|
14
|
+
@app = app
|
|
15
|
+
@api_key = @app["apiKey"]
|
|
16
|
+
@type = type
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def perform!
|
|
20
|
+
resp = PartnersAPI.query(@ctx, "get_extension_registrations", **params)
|
|
21
|
+
@result = resp&.dig("data", "app") || {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def patch_app_with_extensions!
|
|
25
|
+
@app.merge!(result)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def params
|
|
31
|
+
{ api_key: @api_key, type: @type }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "shopify_cli/thread_pool"
|
|
4
|
+
|
|
5
|
+
require_relative "app_extensions/job"
|
|
6
|
+
|
|
7
|
+
module ShopifyCLI
|
|
8
|
+
class PartnersAPI
|
|
9
|
+
class AppExtensions
|
|
10
|
+
class << self
|
|
11
|
+
def fetch_apps_extensions(ctx, orgs, type)
|
|
12
|
+
jobs = apps(orgs).map { |app| AppExtensions::Job.new(ctx, app, type) }
|
|
13
|
+
|
|
14
|
+
consume_jobs!(jobs)
|
|
15
|
+
patch_apps_with_extensions!(jobs)
|
|
16
|
+
|
|
17
|
+
orgs
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def apps(orgs)
|
|
23
|
+
orgs.flat_map { |org| org["apps"] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def consume_jobs!(jobs)
|
|
27
|
+
thread_pool = ShopifyCLI::ThreadPool.new
|
|
28
|
+
jobs.each do |job|
|
|
29
|
+
thread_pool.schedule(job)
|
|
30
|
+
end
|
|
31
|
+
thread_pool.shutdown
|
|
32
|
+
|
|
33
|
+
raise_if_any_error(jobs)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def patch_apps_with_extensions!(jobs)
|
|
37
|
+
jobs.each(&:patch_app_with_extensions!)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def raise_if_any_error(jobs)
|
|
41
|
+
jobs.find(&:error?).tap { |job| raise job.error if job }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -28,11 +28,8 @@ module ShopifyCLI
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def fetch_with_extensions(ctx, type)
|
|
31
|
-
|
|
32
|
-
(
|
|
33
|
-
org["apps"] = (org.dig("apps", "nodes") || [])
|
|
34
|
-
org
|
|
35
|
-
end
|
|
31
|
+
orgs = fetch_with_app(ctx)
|
|
32
|
+
AppExtensions.fetch_apps_extensions(ctx, orgs, type)
|
|
36
33
|
end
|
|
37
34
|
end
|
|
38
35
|
end
|
data/lib/shopify_cli/project.rb
CHANGED
|
@@ -107,13 +107,6 @@ module ShopifyCLI
|
|
|
107
107
|
@dir = nil
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
private
|
|
111
|
-
|
|
112
|
-
def directory(dir)
|
|
113
|
-
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
|
114
|
-
@dir[dir]
|
|
115
|
-
end
|
|
116
|
-
|
|
117
110
|
def at(dir)
|
|
118
111
|
proj_dir = directory(dir)
|
|
119
112
|
unless proj_dir
|
|
@@ -123,6 +116,13 @@ module ShopifyCLI
|
|
|
123
116
|
@at[proj_dir]
|
|
124
117
|
end
|
|
125
118
|
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def directory(dir)
|
|
122
|
+
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
|
123
|
+
@dir[dir]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
126
|
def __directory(curr)
|
|
127
127
|
loop do
|
|
128
128
|
return nil if curr == "/" || /^[A-Z]:\/$/.match?(curr)
|
|
@@ -134,6 +134,7 @@ module ShopifyCLI
|
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
property :directory # :nodoc:
|
|
137
|
+
property :env # :nodoc:
|
|
137
138
|
|
|
138
139
|
##
|
|
139
140
|
# will read, parse and return the envfile for the project
|
|
@@ -14,13 +14,21 @@ module ShopifyCLI
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
class << self
|
|
17
|
-
def
|
|
18
|
-
|
|
17
|
+
def path(directory)
|
|
18
|
+
File.join(directory, FILENAME)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read(_directory = Dir.pwd, overrides: {})
|
|
22
|
+
input = parse_external_env(overrides: overrides)
|
|
19
23
|
new(input)
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
def from_hash(hash)
|
|
27
|
+
new(env_input(hash))
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
def parse(directory)
|
|
23
|
-
File.read(
|
|
31
|
+
File.read(path(directory))
|
|
24
32
|
.gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
|
|
25
33
|
match = /\A([A-Za-z_0-9]+)\s*=\s*(.*)\z/.match(line)
|
|
26
34
|
if match
|
|
@@ -37,10 +45,14 @@ module ShopifyCLI
|
|
|
37
45
|
end
|
|
38
46
|
end
|
|
39
47
|
|
|
40
|
-
def parse_external_env(directory = Dir.pwd)
|
|
48
|
+
def parse_external_env(directory = Dir.pwd, overrides: {})
|
|
49
|
+
env_input(parse(directory), overrides: overrides)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def env_input(parsed_source, overrides: {})
|
|
41
53
|
env_details = {}
|
|
42
54
|
extra = {}
|
|
43
|
-
|
|
55
|
+
parsed_source.merge(overrides).each do |key, value|
|
|
44
56
|
if KEY_MAP[key]
|
|
45
57
|
env_details[KEY_MAP[key]] = value
|
|
46
58
|
else
|
|
@@ -53,7 +65,7 @@ module ShopifyCLI
|
|
|
53
65
|
end
|
|
54
66
|
|
|
55
67
|
property :api_key, required: true
|
|
56
|
-
property :secret
|
|
68
|
+
property :secret
|
|
57
69
|
property :shop
|
|
58
70
|
property :scopes
|
|
59
71
|
property :host
|
|
@@ -159,10 +159,10 @@ module ShopifyCLI
|
|
|
159
159
|
|
|
160
160
|
CLI::UI::Frame.open(context.message("core.app.create.rails.generating_app", name)) do
|
|
161
161
|
new_command = %w(rails new)
|
|
162
|
+
new_command << name
|
|
162
163
|
new_command += DEFAULT_RAILS_FLAGS
|
|
163
164
|
new_command << "--database=#{db}"
|
|
164
165
|
new_command += rails_opts.split unless rails_opts.nil?
|
|
165
|
-
new_command << name
|
|
166
166
|
|
|
167
167
|
syscall(new_command)
|
|
168
168
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyCLI
|
|
4
|
+
module Theme
|
|
5
|
+
module DevServer
|
|
6
|
+
class CdnFonts
|
|
7
|
+
FONTS_PATH = "/fonts"
|
|
8
|
+
FONTS_CDN = "https://fonts.shopifycdn.com"
|
|
9
|
+
FONTS_REGEX = %r{#{FONTS_CDN}}
|
|
10
|
+
|
|
11
|
+
def initialize(app, theme:)
|
|
12
|
+
@app = app
|
|
13
|
+
@theme = theme
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
path = env["PATH_INFO"]
|
|
18
|
+
|
|
19
|
+
# Serve from fonts CDN
|
|
20
|
+
return serve_font(env) if path.start_with?(FONTS_PATH)
|
|
21
|
+
|
|
22
|
+
# Proxy the request, and replace the URLs in the response
|
|
23
|
+
status, headers, body = @app.call(env)
|
|
24
|
+
body = replace_font_urls(body)
|
|
25
|
+
[status, headers, body]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def serve_font(env)
|
|
31
|
+
parameters = %w(PATH_INFO QUERY_STRING REQUEST_METHOD rack.input)
|
|
32
|
+
path, query, method, body_stream = *env.slice(*parameters).values
|
|
33
|
+
|
|
34
|
+
uri = fonts_cdn_uri(path, query)
|
|
35
|
+
|
|
36
|
+
response = Net::HTTP.start(uri.host, 443, use_ssl: true) do |http|
|
|
37
|
+
req_class = Net::HTTP.const_get(method.capitalize)
|
|
38
|
+
req = req_class.new(uri)
|
|
39
|
+
req.initialize_http_header(fonts_cdn_headers)
|
|
40
|
+
req.body_stream = body_stream
|
|
41
|
+
http.request(req)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
[
|
|
45
|
+
response.code.to_s,
|
|
46
|
+
{
|
|
47
|
+
"Content-Type" => response.content_type,
|
|
48
|
+
"Content-Length" => response.content_length.to_s,
|
|
49
|
+
},
|
|
50
|
+
[response.body],
|
|
51
|
+
]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def fonts_cdn_headers
|
|
55
|
+
{
|
|
56
|
+
"Referer" => "https://#{@theme.shop}",
|
|
57
|
+
"Transfer-Encoding" => "chunked",
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def fonts_cdn_uri(path, query)
|
|
62
|
+
uri = URI.join("#{FONTS_CDN}/", path.gsub(%r{^#{FONTS_PATH}\/}, ""))
|
|
63
|
+
uri.query = query.split("&").last
|
|
64
|
+
uri
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def replace_font_urls(body)
|
|
68
|
+
[body.join.gsub(FONTS_REGEX, FONTS_PATH)]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -15,21 +15,64 @@
|
|
|
15
15
|
eventSource.onerror = () => eventSource.close();
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
function reloadMode() {
|
|
19
|
+
var namespace = window.__SHOPIFY_CLI_ENV__;
|
|
20
|
+
return namespace.mode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isFullPageReloadMode(){
|
|
24
|
+
return reloadMode() === "full-page";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isReloadModeActive(){
|
|
28
|
+
return reloadMode() !== "off";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRefreshRequired(files) {
|
|
32
|
+
if (isFullPageReloadMode()) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return files.some((file) => !isCssFile(file) && !isSectionFile(file));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function refreshFile(file) {
|
|
39
|
+
if (isCssFile(file)) {
|
|
40
|
+
reloadCssFile(file);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (isSectionFile(file)) {
|
|
45
|
+
reloadSection(file);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function setHotReloadCookie(files) {
|
|
51
|
+
var date = new Date();
|
|
52
|
+
|
|
53
|
+
// Hot reload cookie expires in 3 seconds
|
|
54
|
+
date.setSeconds(date.getSeconds() + 3);
|
|
55
|
+
|
|
56
|
+
var sections = files.join(',');
|
|
57
|
+
var expires = date.toUTCString();
|
|
58
|
+
|
|
59
|
+
document.cookie = `hot_reload_sections=${sections}; expires=${expires}; path=/`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function refreshPage(files) {
|
|
63
|
+
setHotReloadCookie(files);
|
|
64
|
+
console.log('[HotReload] Refreshing entire page');
|
|
65
|
+
window.location.reload();
|
|
66
|
+
}
|
|
19
67
|
|
|
20
68
|
function handleUpdate(message) {
|
|
21
69
|
var data = JSON.parse(message.data);
|
|
70
|
+
var modifiedFiles = data.modified;
|
|
22
71
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (isCssFile(modified)) {
|
|
27
|
-
reloadCssFile(modified)
|
|
28
|
-
} else if (isSectionFile(modified)) {
|
|
29
|
-
reloadSection(modified);
|
|
72
|
+
if (isRefreshRequired(modifiedFiles)) {
|
|
73
|
+
refreshPage(modifiedFiles);
|
|
30
74
|
} else {
|
|
31
|
-
|
|
32
|
-
window.location.reload();
|
|
75
|
+
modifiedFiles.forEach(refreshFile);
|
|
33
76
|
}
|
|
34
77
|
}
|
|
35
78
|
|
|
@@ -90,4 +133,8 @@
|
|
|
90
133
|
}
|
|
91
134
|
}
|
|
92
135
|
}
|
|
136
|
+
|
|
137
|
+
if (isReloadModeActive()) {
|
|
138
|
+
connect();
|
|
139
|
+
}
|
|
93
140
|
})();
|
|
@@ -4,10 +4,11 @@ module ShopifyCLI
|
|
|
4
4
|
module Theme
|
|
5
5
|
module DevServer
|
|
6
6
|
class HotReload
|
|
7
|
-
def initialize(ctx, app, theme:, watcher:, ignore_filter: nil)
|
|
7
|
+
def initialize(ctx, app, theme:, watcher:, mode:, ignore_filter: nil)
|
|
8
8
|
@ctx = ctx
|
|
9
9
|
@app = app
|
|
10
10
|
@theme = theme
|
|
11
|
+
@mode = mode
|
|
11
12
|
@streams = SSE::Streams.new
|
|
12
13
|
@watcher = watcher
|
|
13
14
|
@watcher.add_observer(self, :notify_streams_of_file_change)
|
|
@@ -48,12 +49,27 @@ module ShopifyCLI
|
|
|
48
49
|
|
|
49
50
|
def inject_hot_reload_javascript(body)
|
|
50
51
|
hot_reload_js = ::File.read("#{__dir__}/hot-reload.js")
|
|
51
|
-
hot_reload_script =
|
|
52
|
+
hot_reload_script = [
|
|
53
|
+
"<script>",
|
|
54
|
+
params_js,
|
|
55
|
+
hot_reload_js,
|
|
56
|
+
"</script>",
|
|
57
|
+
].join("\n")
|
|
58
|
+
|
|
52
59
|
body = body.join.gsub("</body>", "#{hot_reload_script}\n</body>")
|
|
53
60
|
|
|
54
61
|
[body]
|
|
55
62
|
end
|
|
56
63
|
|
|
64
|
+
def params_js
|
|
65
|
+
env = { mode: @mode }
|
|
66
|
+
<<~JS
|
|
67
|
+
(() => {
|
|
68
|
+
window.__SHOPIFY_CLI_ENV__ = #{env.to_json};
|
|
69
|
+
})();
|
|
70
|
+
JS
|
|
71
|
+
end
|
|
72
|
+
|
|
57
73
|
def create_stream
|
|
58
74
|
stream = @streams.new
|
|
59
75
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
|
|
5
|
+
module ShopifyCLI
|
|
6
|
+
module Theme
|
|
7
|
+
module DevServer
|
|
8
|
+
class Proxy
|
|
9
|
+
class TemplateParamBuilder
|
|
10
|
+
def build
|
|
11
|
+
# Core doesn't support replace_templates
|
|
12
|
+
return {} if core?(current_path)
|
|
13
|
+
|
|
14
|
+
(syncer_templates + request_templates)
|
|
15
|
+
.select { |file| file.liquid? || file.json? }
|
|
16
|
+
.uniq(&:relative_path)
|
|
17
|
+
.map { |file| as_param(file) }
|
|
18
|
+
.to_h
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def with_core_endpoints(core_endpoints)
|
|
22
|
+
@core_endpoints = core_endpoints
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def with_syncer(syncer)
|
|
27
|
+
@syncer = syncer
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def with_rack_env(rack_env)
|
|
32
|
+
@rack_env = rack_env
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def with_theme(theme)
|
|
37
|
+
@theme = theme
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def as_param(file)
|
|
44
|
+
["replace_templates[#{file.relative_path}]", file.read]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def syncer_templates
|
|
48
|
+
@syncer&.pending_updates || []
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def request_templates
|
|
52
|
+
cookie_sections
|
|
53
|
+
.map { |section| @theme[section] unless @theme.nil? }
|
|
54
|
+
.compact
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def cookie_sections
|
|
58
|
+
CGI::Cookie.parse(cookie)["hot_reload_sections"].join.split(",") || []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def core?(path)
|
|
62
|
+
core_endpoints.include?(path)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def current_path
|
|
66
|
+
rack_env["PATH_INFO"]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def cookie
|
|
70
|
+
rack_env["HTTP_COOKIE"]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def core_endpoints
|
|
74
|
+
@core_endpoints || []
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def rack_env
|
|
78
|
+
@rack_env || {}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
require "net/http"
|
|
3
3
|
require "stringio"
|
|
4
4
|
require "time"
|
|
5
|
+
require "cgi"
|
|
6
|
+
|
|
7
|
+
require_relative "proxy/template_param_builder"
|
|
5
8
|
|
|
6
9
|
module ShopifyCLI
|
|
7
10
|
module Theme
|
|
@@ -15,6 +18,7 @@ module ShopifyCLI
|
|
|
15
18
|
"trailer",
|
|
16
19
|
"transfer-encoding",
|
|
17
20
|
"upgrade",
|
|
21
|
+
"content-security-policy",
|
|
18
22
|
]
|
|
19
23
|
|
|
20
24
|
class Proxy
|
|
@@ -112,21 +116,12 @@ module ShopifyCLI
|
|
|
112
116
|
end
|
|
113
117
|
|
|
114
118
|
def build_replace_templates_param(env)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# Only replace Liquid or JSON files
|
|
122
|
-
file.liquid? || file.json?
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
pending_templates.each do |path|
|
|
126
|
-
params["replace_templates[#{path.relative_path}]"] = path.read
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
params
|
|
119
|
+
TemplateParamBuilder.new
|
|
120
|
+
.with_core_endpoints(@core_endpoints)
|
|
121
|
+
.with_syncer(@syncer)
|
|
122
|
+
.with_theme(@theme)
|
|
123
|
+
.with_rack_env(env)
|
|
124
|
+
.build
|
|
130
125
|
end
|
|
131
126
|
|
|
132
127
|
def add_session_cookie(cookie_header)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyCLI
|
|
4
|
+
module Theme
|
|
5
|
+
module DevServer
|
|
6
|
+
class ReloadMode
|
|
7
|
+
MODES = [:"hot-reload", :"full-page", :off]
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def default
|
|
11
|
+
:"hot-reload"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get!(mode)
|
|
15
|
+
MODES.find { |m| m == mode.to_sym } || raise_error(mode)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def raise_error(invalid_mode)
|
|
21
|
+
error_message = ShopifyCLI::Context.message("theme.serve.reload_mode_is_not_valid", invalid_mode)
|
|
22
|
+
help_message = ShopifyCLI::Context.message("theme.serve.try_a_valid_reload_mode", valid_modes)
|
|
23
|
+
|
|
24
|
+
ShopifyCLI::Context.abort(error_message, help_message)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def valid_modes
|
|
28
|
+
MODES.map { |v| "`#{v}`" }.join(", ")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|