shopify-cli 2.24.0 → 2.26.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/CHANGELOG.md +14 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/dev.yml +0 -3
- data/lib/project_types/extension/commands/serve.rb +57 -3
- data/lib/project_types/extension/extension_project.rb +8 -1
- data/lib/project_types/extension/loaders/project.rb +3 -2
- data/lib/project_types/extension/messages/messages.rb +21 -6
- data/lib/project_types/extension/models/server_config/development_renderer.rb +1 -1
- data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +18 -6
- data/lib/project_types/script/cli.rb +0 -79
- data/lib/project_types/script/commands/connect.rb +3 -8
- data/lib/project_types/script/commands/create.rb +4 -29
- data/lib/project_types/script/commands/javy.rb +3 -8
- data/lib/project_types/script/commands/push.rb +4 -41
- data/lib/project_types/script/messages/messages.rb +1 -258
- data/lib/project_types/theme/commands/common/shop_helper.rb +13 -0
- data/lib/project_types/theme/commands/delete.rb +4 -1
- data/lib/project_types/theme/commands/list.rb +3 -4
- data/lib/project_types/theme/commands/open.rb +4 -1
- data/lib/project_types/theme/commands/publish.rb +4 -1
- data/lib/project_types/theme/commands/pull.rb +3 -1
- data/lib/project_types/theme/commands/push.rb +3 -1
- data/lib/project_types/theme/commands/serve.rb +15 -3
- data/lib/project_types/theme/messages/messages.rb +9 -7
- data/lib/shopify_cli/commands/logout.rb +13 -2
- data/lib/shopify_cli/environment.rb +1 -1
- data/lib/shopify_cli/file_system_listener.rb +30 -0
- data/lib/shopify_cli/git.rb +116 -33
- data/lib/shopify_cli/identity_auth.rb +1 -0
- data/lib/shopify_cli/messages/messages.rb +1 -1
- data/lib/shopify_cli/packager.rb +12 -3
- data/lib/shopify_cli/project.rb +1 -1
- data/lib/shopify_cli/release.rb +4 -2
- data/lib/shopify_cli/tasks/ensure_project_type.rb +3 -1
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/certificate_manager.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/errors.rb +9 -0
- data/lib/shopify_cli/theme/dev_server/header_hash.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/hooks/file_change_hook.rb +77 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_deleter.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/{hot-reload-no-script.html → hot_reload/resources/hot-reload-no-script.html} +0 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/resources/hot_reload.js +48 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/resources/sse_client.js +43 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/resources/theme.js +114 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/resources/theme_extension.js +121 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/script_injector.rb +57 -0
- data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +8 -76
- data/lib/shopify_cli/theme/dev_server/local_assets.rb +28 -28
- data/lib/shopify_cli/theme/dev_server/proxy.rb +33 -25
- data/lib/shopify_cli/theme/dev_server/proxy_param_builder.rb +82 -0
- data/lib/shopify_cli/theme/dev_server/reload_mode.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/remote_watcher/json_files_update_job.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/remote_watcher.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/sse.rb +1 -1
- data/lib/shopify_cli/theme/dev_server/watcher.rb +8 -9
- data/lib/shopify_cli/theme/dev_server/web_server.rb +1 -1
- data/lib/shopify_cli/theme/dev_server.rb +287 -99
- data/lib/shopify_cli/theme/extension/app_extension.rb +40 -0
- data/lib/shopify_cli/theme/extension/dev_server/hooks/file_change_hook.rb +68 -0
- data/lib/shopify_cli/theme/extension/dev_server/hot_reload/script_injector.rb +30 -0
- data/lib/shopify_cli/theme/extension/dev_server/hot_reload.rb +13 -0
- data/lib/shopify_cli/theme/extension/dev_server/local_assets.rb +30 -0
- data/lib/shopify_cli/theme/{dev_server/proxy/template_param_builder.rb → extension/dev_server/proxy_param_builder.rb} +26 -16
- data/lib/shopify_cli/theme/extension/dev_server/watcher.rb +47 -0
- data/lib/shopify_cli/theme/extension/dev_server.rb +150 -0
- data/lib/shopify_cli/theme/extension/host_theme.rb +104 -0
- data/lib/shopify_cli/theme/extension/syncer/extension_serve_job.rb +133 -0
- data/lib/shopify_cli/theme/extension/syncer/operation.rb +21 -0
- data/lib/shopify_cli/theme/extension/syncer.rb +81 -0
- data/lib/shopify_cli/theme/extension/ui/host_theme_progress_bar.rb +35 -0
- data/lib/shopify_cli/theme/ignore_helper.rb +31 -0
- data/lib/shopify_cli/theme/root.rb +62 -0
- data/lib/shopify_cli/theme/syncer.rb +12 -6
- data/lib/shopify_cli/theme/theme.rb +10 -52
- data/lib/shopify_cli/version.rb +1 -1
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +1 -1
- metadata +28 -53
- data/.github/workflows/triage.yml +0 -22
- data/lib/project_types/script/config/extension_points.yml +0 -45
- data/lib/project_types/script/errors.rb +0 -10
- data/lib/project_types/script/forms/ask_app.rb +0 -27
- data/lib/project_types/script/forms/ask_org.rb +0 -30
- data/lib/project_types/script/forms/ask_script_uuid.rb +0 -22
- data/lib/project_types/script/forms/create.rb +0 -33
- data/lib/project_types/script/forms/run_against_shopify_org.rb +0 -14
- data/lib/project_types/script/graphql/app_script_set.graphql +0 -46
- data/lib/project_types/script/graphql/get_app_scripts.graphql +0 -6
- data/lib/project_types/script/graphql/module_upload_url_generate.graphql +0 -13
- data/lib/project_types/script/graphql/script_service_proxy.graphql +0 -7
- data/lib/project_types/script/layers/application/build_script.rb +0 -25
- data/lib/project_types/script/layers/application/connect_app.rb +0 -86
- data/lib/project_types/script/layers/application/create_script.rb +0 -90
- data/lib/project_types/script/layers/application/extension_points.rb +0 -66
- data/lib/project_types/script/layers/application/project_dependencies.rb +0 -26
- data/lib/project_types/script/layers/application/push_script.rb +0 -74
- data/lib/project_types/script/layers/domain/app_bridge.rb +0 -16
- data/lib/project_types/script/layers/domain/errors.rb +0 -47
- data/lib/project_types/script/layers/domain/extension_point.rb +0 -81
- data/lib/project_types/script/layers/domain/metadata.rb +0 -46
- data/lib/project_types/script/layers/domain/push_package.rb +0 -41
- data/lib/project_types/script/layers/domain/script_config.rb +0 -32
- data/lib/project_types/script/layers/domain/script_project.rb +0 -61
- data/lib/project_types/script/layers/infrastructure/api_clients/partners_proxy_api_client.rb +0 -53
- data/lib/project_types/script/layers/infrastructure/api_clients/script_service_api_client.rb +0 -35
- data/lib/project_types/script/layers/infrastructure/command_runner.rb +0 -19
- data/lib/project_types/script/layers/infrastructure/errors.rb +0 -211
- data/lib/project_types/script/layers/infrastructure/extension_point_repository.rb +0 -37
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -62
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +0 -47
- data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +0 -26
- data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +0 -45
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +0 -103
- data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -12
- data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +0 -32
- data/lib/project_types/script/layers/infrastructure/metadata_repository.rb +0 -18
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +0 -36
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +0 -273
- data/lib/project_types/script/layers/infrastructure/script_service.rb +0 -135
- data/lib/project_types/script/layers/infrastructure/script_uploader.rb +0 -40
- data/lib/project_types/script/layers/infrastructure/service_locator.rb +0 -20
- data/lib/project_types/script/layers/infrastructure/sparse_checkout_details.rb +0 -35
- data/lib/project_types/script/ui/error_handler.rb +0 -331
- data/lib/project_types/script/ui/printing_spinner.rb +0 -75
- data/lib/project_types/script/ui/strict_spinner.rb +0 -20
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +0 -194
- data/lib/shopify_cli/theme/syncer/ignore_helper.rb +0 -33
data/lib/shopify_cli/git.rb
CHANGED
|
@@ -46,12 +46,77 @@ module ShopifyCLI
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
##
|
|
49
|
-
#
|
|
50
|
-
# it will also output progress of the cloning process.
|
|
49
|
+
# returns array with components of git clone command
|
|
51
50
|
#
|
|
52
51
|
# #### Parameters
|
|
53
52
|
#
|
|
54
|
-
# * `
|
|
53
|
+
# * `repo` - repo url without branch name
|
|
54
|
+
# * `dest` - a filepath to where the repo should be cloned to
|
|
55
|
+
# * `branch` - branch name when cloning
|
|
56
|
+
#
|
|
57
|
+
# #### Returns
|
|
58
|
+
#
|
|
59
|
+
# * array of strings
|
|
60
|
+
#
|
|
61
|
+
# #### Example
|
|
62
|
+
#
|
|
63
|
+
# ["clone", "--single-branch", "--branch", "test-branch", "test-app"]
|
|
64
|
+
#
|
|
65
|
+
def git_clone_command(repo, dest, branch)
|
|
66
|
+
if branch
|
|
67
|
+
["clone", "--single-branch", "--branch", branch, repo, dest]
|
|
68
|
+
else
|
|
69
|
+
["clone", "--single-branch", repo, dest]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# calls git to clone a new repo into a supplied destination,
|
|
75
|
+
# it will also call a supplied block with the percentage of clone completion
|
|
76
|
+
#
|
|
77
|
+
# #### Parameters
|
|
78
|
+
#
|
|
79
|
+
# * `repo_with_branch` - a git url for git to clone the repo from
|
|
80
|
+
# * `dest` - a filepath to where the repo should be cloned to
|
|
81
|
+
# * `ctx` - the current running context of your command, defaults to a new context.
|
|
82
|
+
#
|
|
83
|
+
# #### Returns
|
|
84
|
+
#
|
|
85
|
+
# * `sha_string` - string of the sha of the most recent commit to the repo
|
|
86
|
+
#
|
|
87
|
+
# #### Example
|
|
88
|
+
#
|
|
89
|
+
# ShopifyCLI::Git.raw_clone('git@github.com:shopify/test.git', 'test-app')
|
|
90
|
+
#
|
|
91
|
+
def raw_clone(repo_with_branch, dest, ctx: Context.new)
|
|
92
|
+
if Dir.exist?(dest) && !Dir.empty?(dest)
|
|
93
|
+
ctx.abort(ctx.message("core.git.error.directory_exists"))
|
|
94
|
+
else
|
|
95
|
+
msg = []
|
|
96
|
+
# require at usage point to not slow down CLI startup
|
|
97
|
+
# https://github.com/Shopify/shopify-cli/pull/698#discussion_r444342445
|
|
98
|
+
require "open3"
|
|
99
|
+
|
|
100
|
+
repo, branch = repo_with_branch.split("#")
|
|
101
|
+
git_cmd = git_clone_command(repo, dest, branch)
|
|
102
|
+
|
|
103
|
+
success = Open3.popen3("git", *git_cmd, "--progress") do |_stdin, _stdout, stderr, thread|
|
|
104
|
+
msg = clone_progress(stderr, bar: nil)
|
|
105
|
+
|
|
106
|
+
thread.value
|
|
107
|
+
end.success?
|
|
108
|
+
|
|
109
|
+
ctx.abort((msg.join("\n"))) unless success
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# calls git to clone a new repo into a supplied destination,
|
|
115
|
+
# it will also output progress of the cloning process into a new progress bar
|
|
116
|
+
#
|
|
117
|
+
# #### Parameters
|
|
118
|
+
#
|
|
119
|
+
# * `repo_with_branch` - a git url for git to clone the repo from
|
|
55
120
|
# * `dest` - a filepath to where the repo should be cloned to
|
|
56
121
|
# * `ctx` - the current running context of your command, defaults to a new context.
|
|
57
122
|
#
|
|
@@ -63,17 +128,30 @@ module ShopifyCLI
|
|
|
63
128
|
#
|
|
64
129
|
# ShopifyCLI::Git.clone('git@github.com:shopify/test.git', 'test-app')
|
|
65
130
|
#
|
|
66
|
-
def clone(
|
|
67
|
-
if Dir.exist?(dest)
|
|
131
|
+
def clone(repo_with_branch, dest, ctx: Context.new)
|
|
132
|
+
if Dir.exist?(dest) && !Dir.empty?(dest)
|
|
68
133
|
ctx.abort(ctx.message("core.git.error.directory_exists"))
|
|
69
134
|
else
|
|
70
|
-
|
|
135
|
+
msg = []
|
|
136
|
+
# require at usage point to not slow down CLI startup
|
|
137
|
+
# https://github.com/Shopify/shopify-cli/pull/698#discussion_r444342445
|
|
138
|
+
require "open3"
|
|
139
|
+
|
|
140
|
+
repo, branch = repo_with_branch.split("#")
|
|
141
|
+
git_cmd = git_clone_command(repo, dest, branch)
|
|
142
|
+
|
|
71
143
|
success_message = ctx.message("core.git.cloned", dest)
|
|
144
|
+
|
|
72
145
|
CLI::UI::Frame.open(ctx.message("core.git.cloning", repo, dest), success_text: success_message) do
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
146
|
+
CLI::UI::Progress.progress do |bar|
|
|
147
|
+
success = Open3.popen3("git", *git_cmd, "--progress") do |_stdin, _stdout, stderr, thread|
|
|
148
|
+
msg = clone_progress(stderr, bar: bar)
|
|
149
|
+
|
|
150
|
+
thread.value
|
|
151
|
+
end.success?
|
|
152
|
+
|
|
153
|
+
ctx.abort((msg.join("\n"))) unless success
|
|
154
|
+
bar.tick(set_percent: 1.0)
|
|
77
155
|
end
|
|
78
156
|
end
|
|
79
157
|
end
|
|
@@ -197,6 +275,34 @@ module ShopifyCLI
|
|
|
197
275
|
end
|
|
198
276
|
end
|
|
199
277
|
|
|
278
|
+
##
|
|
279
|
+
# handles showing the progress of the git clone command.
|
|
280
|
+
# if block given, assumes passing percent to block, otherwise
|
|
281
|
+
# increments bar for progress bar
|
|
282
|
+
#
|
|
283
|
+
# #### Parameters
|
|
284
|
+
#
|
|
285
|
+
# * `stderr` - Open3.popen3 output stream
|
|
286
|
+
# * `bar` - progress bar object to set percent
|
|
287
|
+
#
|
|
288
|
+
def clone_progress(stderr, bar: nil)
|
|
289
|
+
msg = []
|
|
290
|
+
|
|
291
|
+
while (line = stderr.gets)
|
|
292
|
+
msg << line.chomp
|
|
293
|
+
next unless line.strip.start_with?("Receiving objects:")
|
|
294
|
+
percent = (line.match(/Receiving objects:\s+(\d+)/)[1].to_f / 100).round(2)
|
|
295
|
+
|
|
296
|
+
if block_given?
|
|
297
|
+
yield percent
|
|
298
|
+
elsif !bar.nil?
|
|
299
|
+
bar.tick(set_percent: percent)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
msg
|
|
304
|
+
end
|
|
305
|
+
|
|
200
306
|
private
|
|
201
307
|
|
|
202
308
|
def exec(*args, dir: Dir.pwd, default: nil, ctx: Context.new)
|
|
@@ -209,29 +315,6 @@ module ShopifyCLI
|
|
|
209
315
|
def rev_parse(*args, dir: nil, ctx: Context.new)
|
|
210
316
|
exec("rev-parse", *args, dir: dir, ctx: ctx)
|
|
211
317
|
end
|
|
212
|
-
|
|
213
|
-
def clone_progress(*git_command, ctx:)
|
|
214
|
-
CLI::UI::Progress.progress do |bar|
|
|
215
|
-
msg = []
|
|
216
|
-
require "open3"
|
|
217
|
-
|
|
218
|
-
success = Open3.popen3("git", *git_command, "--progress") do |_stdin, _stdout, stderr, thread|
|
|
219
|
-
while (line = stderr.gets)
|
|
220
|
-
msg << line.chomp
|
|
221
|
-
next unless line.strip.start_with?("Receiving objects:")
|
|
222
|
-
percent = (line.match(/Receiving objects:\s+(\d+)/)[1].to_f / 100).round(2)
|
|
223
|
-
bar.tick(set_percent: percent)
|
|
224
|
-
next
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
thread.value
|
|
228
|
-
end.success?
|
|
229
|
-
|
|
230
|
-
ctx.abort(msg.join("\n")) unless success
|
|
231
|
-
bar.tick(set_percent: 1.0)
|
|
232
|
-
success
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
318
|
end
|
|
236
319
|
end
|
|
237
320
|
end
|
|
@@ -87,6 +87,7 @@ module ShopifyCLI
|
|
|
87
87
|
|
|
88
88
|
def fetch_or_auth_partners_token
|
|
89
89
|
if EnvAuthToken.partners_token_present?
|
|
90
|
+
return Environment.auth_token if Environment.run_as_subprocess?
|
|
90
91
|
return EnvAuthToken.fetch_exchanged_partners_token do |env_token|
|
|
91
92
|
exchange_partners_auth_token(env_token)
|
|
92
93
|
end
|
|
@@ -788,7 +788,7 @@ module ShopifyCLI
|
|
|
788
788
|
{{*}} {{yellow:A new version of Shopify CLI is available! You have version %s and the latest version is %s.
|
|
789
789
|
|
|
790
790
|
To upgrade, follow the instructions for the package manager you’re using:
|
|
791
|
-
{{underline:https://shopify.dev/tools/cli/
|
|
791
|
+
{{underline:https://shopify.dev/themes/tools/cli/upgrade-uninstall}}}}
|
|
792
792
|
|
|
793
793
|
MESSAGE
|
|
794
794
|
|
data/lib/shopify_cli/packager.rb
CHANGED
|
@@ -70,9 +70,10 @@ module ShopifyCLI
|
|
|
70
70
|
root_dir = File.join(PACKAGING_DIR, "homebrew")
|
|
71
71
|
|
|
72
72
|
build_path = File.join(BUILDS_DIR, "shopify-cli.rb")
|
|
73
|
-
|
|
73
|
+
build_path_2 = File.join(BUILDS_DIR, "shopify-cli@2.rb")
|
|
74
|
+
puts "\nBuilding Homebrew packages"
|
|
74
75
|
|
|
75
|
-
puts "Generating
|
|
76
|
+
puts "Generating formulae…"
|
|
76
77
|
File.delete(build_path) if File.exist?(build_path)
|
|
77
78
|
|
|
78
79
|
spec_contents = File.read(File.join(root_dir, "shopify-cli.base.rb"))
|
|
@@ -89,7 +90,15 @@ module ShopifyCLI
|
|
|
89
90
|
spec_contents = spec_contents.gsub("SHOPIFY_CLI_GEM_CHECKSUM", gem_checksum)
|
|
90
91
|
|
|
91
92
|
puts "Writing generated formula\n To: #{build_path}\n\n"
|
|
92
|
-
File.write(build_path, spec_contents)
|
|
93
|
+
File.write(build_path, spec_contents.gsub("SHOPIFY_CLI_BINSTUB_SUFFIX", ""))
|
|
94
|
+
|
|
95
|
+
puts "Writing generated formula\n To: #{build_path_2}\n\n"
|
|
96
|
+
File.write(
|
|
97
|
+
build_path_2,
|
|
98
|
+
spec_contents
|
|
99
|
+
.sub("class ShopifyCli < Formula", "class ShopifyCliAT2 < Formula")
|
|
100
|
+
.sub("SHOPIFY_CLI_BINSTUB_SUFFIX", "2")
|
|
101
|
+
)
|
|
93
102
|
end
|
|
94
103
|
|
|
95
104
|
private
|
data/lib/shopify_cli/project.rb
CHANGED
|
@@ -109,7 +109,7 @@ module ShopifyCLI
|
|
|
109
109
|
|
|
110
110
|
def at(dir)
|
|
111
111
|
proj_dir = directory(dir)
|
|
112
|
-
|
|
112
|
+
if !proj_dir && !ShopifyCLI::Environment.run_as_subprocess?
|
|
113
113
|
raise(ShopifyCLI::Abort, Context.message("core.project.error.not_in_project"))
|
|
114
114
|
end
|
|
115
115
|
@at ||= Hash.new { |h, k| h[k] = new(directory: k) }
|
data/lib/shopify_cli/release.rb
CHANGED
|
@@ -120,8 +120,10 @@ module ShopifyCLI
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def update_homebrew_repo
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
%w(shopify-cli.rb shopify-cli@2.rb).each do |source_filename|
|
|
124
|
+
source_file = File.join(package_dir, source_filename)
|
|
125
|
+
FileUtils.copy(source_file, homebrew_path)
|
|
126
|
+
end
|
|
125
127
|
Dir.chdir(homebrew_path) do
|
|
126
128
|
system_or_fail("git commit -am '#{homebrew_update_message}'", "commit homebrew update")
|
|
127
129
|
system_or_fail("git push -u origin #{homebrew_release_branch}", "push homebrew branch")
|
|
@@ -4,7 +4,9 @@ module ShopifyCLI
|
|
|
4
4
|
module Tasks
|
|
5
5
|
class EnsureProjectType < ShopifyCLI::Task
|
|
6
6
|
def call(ctx, project_type)
|
|
7
|
-
|
|
7
|
+
if project_type.to_sym == ShopifyCLI::Project.current_project_type || Environment.run_as_subprocess?
|
|
8
|
+
return true
|
|
9
|
+
end
|
|
8
10
|
ctx.abort(ctx.message("core.tasks.ensure_project_type.wrong_project_type", project_type))
|
|
9
11
|
end
|
|
10
12
|
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative "../hot_reload/remote_file_reloader"
|
|
3
|
+
require_relative "../hot_reload/remote_file_deleter"
|
|
4
|
+
require "shopify_cli/theme/ignore_helper"
|
|
5
|
+
|
|
6
|
+
module ShopifyCLI
|
|
7
|
+
module Theme
|
|
8
|
+
class DevServer
|
|
9
|
+
module Hooks
|
|
10
|
+
class FileChangeHook
|
|
11
|
+
include ShopifyCLI::Theme::IgnoreHelper
|
|
12
|
+
|
|
13
|
+
attr_reader :include_filter, :ignore_filter
|
|
14
|
+
|
|
15
|
+
def initialize(ctx, theme:, include_filter: nil, ignore_filter: nil)
|
|
16
|
+
@ctx = ctx
|
|
17
|
+
@theme = theme
|
|
18
|
+
@include_filter = include_filter
|
|
19
|
+
@ignore_filter = ignore_filter
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call(modified, added, removed, streams: nil)
|
|
23
|
+
@streams = streams
|
|
24
|
+
files = (modified + added)
|
|
25
|
+
.map { |f| @theme[f] }
|
|
26
|
+
.reject { |f| ignore_file?(f) }
|
|
27
|
+
files -= liquid_css_files = files.select(&:liquid_css?)
|
|
28
|
+
deleted_files = removed
|
|
29
|
+
.map { |f| @theme[f] }
|
|
30
|
+
.reject { |f| ignore_file?(f) }
|
|
31
|
+
|
|
32
|
+
remote_delete(deleted_files) unless deleted_files.empty?
|
|
33
|
+
reload_page(removed) unless deleted_files.empty?
|
|
34
|
+
|
|
35
|
+
hot_reload(files) unless files.empty?
|
|
36
|
+
remote_reload(liquid_css_files)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def hot_reload(files)
|
|
42
|
+
paths = files.map(&:relative_path)
|
|
43
|
+
@streams.broadcast(JSON.generate(modified: paths))
|
|
44
|
+
@ctx.debug("[HotReload] Modified #{paths.join(", ")}")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def reload_page(removed)
|
|
48
|
+
@streams.broadcast(JSON.generate(reload_page: true))
|
|
49
|
+
@ctx.debug("[ReloadPage] Deleted #{removed.join(", ")}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def remote_reload(files)
|
|
53
|
+
files.each do |file|
|
|
54
|
+
@ctx.debug("reload file each -> file.relative_path #{file.relative_path}")
|
|
55
|
+
remote_file_reloader.reload(file)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def remote_delete(files)
|
|
60
|
+
files.each do |file|
|
|
61
|
+
@ctx.debug("delete file each -> file.relative_path #{file.relative_path}")
|
|
62
|
+
remote_file_deleter.delete(file)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def remote_file_deleter
|
|
67
|
+
@remote_file_deleter ||= HotReload::RemoteFileDeleter.new(@ctx, theme: @theme, streams: @streams)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def remote_file_reloader
|
|
71
|
+
@remote_file_reloader ||= HotReload::RemoteFileReloader.new(@ctx, theme: @theme, streams: @streams)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class HotReload {
|
|
2
|
+
static reloadMode = () => {
|
|
3
|
+
const namespace = window.__SHOPIFY_CLI_ENV__;
|
|
4
|
+
return namespace.mode;
|
|
5
|
+
};
|
|
6
|
+
static isFullPageReloadMode = () => {
|
|
7
|
+
return HotReload.reloadMode() === "full-page";
|
|
8
|
+
};
|
|
9
|
+
static isReloadModeActive = () => {
|
|
10
|
+
return HotReload.reloadMode() !== "off";
|
|
11
|
+
};
|
|
12
|
+
static setHotReloadCookie = (files) => {
|
|
13
|
+
const date = new Date();
|
|
14
|
+
|
|
15
|
+
// Hot reload cookie expires in 3 seconds
|
|
16
|
+
date.setSeconds(date.getSeconds() + 3);
|
|
17
|
+
|
|
18
|
+
const sections = files.join(",");
|
|
19
|
+
const expires = date.toUTCString();
|
|
20
|
+
|
|
21
|
+
document.cookie = `hot_reload_files=${sections}; expires=${expires}; path=/`;
|
|
22
|
+
};
|
|
23
|
+
static refreshPage = (files) => {
|
|
24
|
+
HotReload.setHotReloadCookie(files);
|
|
25
|
+
console.log("[HotReload] Refreshing entire page");
|
|
26
|
+
window.location.reload();
|
|
27
|
+
};
|
|
28
|
+
static isCSSFile = (filename) => {
|
|
29
|
+
return filename.endsWith(".css");
|
|
30
|
+
};
|
|
31
|
+
static reloadCssFile = (filename) => {
|
|
32
|
+
// Find a stylesheet link starting with /assets (locally-served only) containing the filename
|
|
33
|
+
let links = document.querySelectorAll(
|
|
34
|
+
`link[href^="/assets"][href*="${filename}"][rel="stylesheet"]`
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
Array.from(links).forEach((link) => {
|
|
38
|
+
if (!link) {
|
|
39
|
+
console.log(
|
|
40
|
+
`[HotReload] Could not find link for stylesheet ${filename}`
|
|
41
|
+
);
|
|
42
|
+
} else {
|
|
43
|
+
link.href = new URL(link.href).pathname + `?v=${Date.now()}`;
|
|
44
|
+
console.log(`[HotReload] Reloaded stylesheet ${filename}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class SSEClient {
|
|
2
|
+
constructor(eventsUrl, eventHandler) {
|
|
3
|
+
SSEClient.verifySSE();
|
|
4
|
+
this.eventsUrl = eventsUrl;
|
|
5
|
+
this.eventHandler = eventHandler;
|
|
6
|
+
}
|
|
7
|
+
static verifySSE() {
|
|
8
|
+
if (typeof EventSource === "undefined") {
|
|
9
|
+
console.error(
|
|
10
|
+
"[HotReload] Error: SSE features are not supported. Try a different browser."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log("[HotReload] Initializing...");
|
|
15
|
+
}
|
|
16
|
+
connect() {
|
|
17
|
+
const eventSource = new EventSource(this.eventsUrl);
|
|
18
|
+
eventSource.onmessage = (msg) => {
|
|
19
|
+
this.handleMessage(msg);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
eventSource.onopen = () => console.log("[HotReload] SSE connected.");
|
|
23
|
+
|
|
24
|
+
eventSource.onclose = () => {
|
|
25
|
+
console.log("[HotReload] SSE closed. Attempting to reconnect...");
|
|
26
|
+
|
|
27
|
+
setTimeout(this.connect, 5000);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
eventSource.onerror = () => {
|
|
31
|
+
console.log("[HotReload] SSE closed.");
|
|
32
|
+
eventSource.close();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
handleMessage(message) {
|
|
36
|
+
const data = JSON.parse(message.data);
|
|
37
|
+
if (data.reload_page) {
|
|
38
|
+
HotReload.refreshPage([]);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.eventHandler(data);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
function sectionNamesByType(type) {
|
|
3
|
+
const namespace = window.__SHOPIFY_CLI_ENV__;
|
|
4
|
+
return namespace.section_names_by_type[type] || [];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function querySelectDOMSections(idSuffix) {
|
|
8
|
+
const elements = document.querySelectorAll(
|
|
9
|
+
`[id^='shopify-section'][id$='${idSuffix}']`
|
|
10
|
+
);
|
|
11
|
+
return Array.from(elements);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function fetchDOMSections(name) {
|
|
15
|
+
const domSections = sectionNamesByType(name).flatMap((n) =>
|
|
16
|
+
querySelectDOMSections(n)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (domSections.length > 0) {
|
|
20
|
+
return domSections;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return querySelectDOMSections(name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isRefreshRequired(files) {
|
|
27
|
+
if (HotReload.isFullPageReloadMode()) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return files.some(
|
|
31
|
+
(file) => !HotReload.isCSSFile(file) && !isSectionFile(file)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function refreshFile(file) {
|
|
36
|
+
if (HotReload.isCSSFile(file)) {
|
|
37
|
+
HotReload.reloadCssFile(file);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isSectionFile(file)) {
|
|
42
|
+
reloadSection(file);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleUpdate(data) {
|
|
47
|
+
const modifiedFiles = data.modified;
|
|
48
|
+
|
|
49
|
+
if (modifiedFiles === undefined) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isRefreshRequired(modifiedFiles)) {
|
|
54
|
+
HotReload.refreshPage(modifiedFiles);
|
|
55
|
+
} else {
|
|
56
|
+
modifiedFiles.forEach(refreshFile);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isSectionFile(filename) {
|
|
61
|
+
return new Section(filename).valid();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function reloadSection(filename) {
|
|
65
|
+
new Section(filename).refresh();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class Section {
|
|
69
|
+
constructor(filename) {
|
|
70
|
+
this.filename = filename;
|
|
71
|
+
this.name = filename.split("/").pop().replace(".liquid", "");
|
|
72
|
+
this.elements = fetchDOMSections(this.name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
valid() {
|
|
76
|
+
return this.filename.startsWith("sections/") && this.elements.length > 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async refreshElement(element) {
|
|
80
|
+
const sectionId = element.id.replace(/^shopify-section-/, "");
|
|
81
|
+
const url = new URL(window.location.href);
|
|
82
|
+
|
|
83
|
+
url.searchParams.append("section_id", sectionId);
|
|
84
|
+
|
|
85
|
+
const response = await fetch(url);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
if (response.headers.get("x-templates-from-params") === "1") {
|
|
89
|
+
element.outerHTML = await response.text();
|
|
90
|
+
} else {
|
|
91
|
+
window.location.reload();
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
`[HotReload] Hot-reloading not supported, fully reloading ${this.name} section`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.log(
|
|
99
|
+
`[HotReload] Failed to reload ${this.name} section: ${e.message}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async refresh() {
|
|
105
|
+
console.log(`[HotReload] Reloaded ${this.name} sections`);
|
|
106
|
+
this.elements.forEach(this.refreshElement);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (HotReload.isReloadModeActive()) {
|
|
111
|
+
let client = new SSEClient("/hot-reload", handleUpdate);
|
|
112
|
+
client.connect();
|
|
113
|
+
}
|
|
114
|
+
})();
|