shopify-cli 2.18.0 → 2.20.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.yaml +2 -1
  3. data/.github/ISSUE_TEMPLATE/config.yml +9 -0
  4. data/CHANGELOG.md +16 -2
  5. data/Gemfile.lock +4 -4
  6. data/README.md +7 -6
  7. data/dev.yml +0 -1
  8. data/docs/users/installation.md +1 -1
  9. data/lib/project_types/extension/forms/questions/ask_template.rb +3 -3
  10. data/lib/project_types/extension/messages/messages.rb +17 -1
  11. data/lib/project_types/extension/models/development_server_requirements.rb +2 -7
  12. data/lib/project_types/extension/models/server_config/development.rb +9 -0
  13. data/lib/project_types/extension/models/server_config/development_renderer.rb +1 -1
  14. data/lib/project_types/extension/models/specification_handlers/{beacon_extension.rb → web_pixel_extension.rb} +11 -10
  15. data/lib/project_types/extension/models/specification_handlers/{beacon_extension_utils → web_pixel_extension_utils}/script_config.rb +1 -1
  16. data/lib/project_types/extension/models/specification_handlers/{beacon_extension_utils → web_pixel_extension_utils}/script_config_repository.rb +2 -2
  17. data/lib/project_types/extension/tasks/configure_options.rb +1 -1
  18. data/lib/project_types/extension/tasks/execute_commands/outdated_extension_detection.rb +5 -1
  19. data/lib/project_types/extension/tasks/fetch_specifications.rb +4 -1
  20. data/lib/project_types/script/commands/create.rb +1 -1
  21. data/lib/project_types/script/config/extension_points.yml +15 -15
  22. data/lib/project_types/script/forms/ask_app.rb +0 -5
  23. data/lib/project_types/script/layers/domain/metadata.rb +3 -5
  24. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +5 -3
  25. data/lib/project_types/script/layers/infrastructure/script_service.rb +1 -1
  26. data/lib/project_types/script/messages/messages.rb +1 -1
  27. data/lib/project_types/theme/commands/push.rb +3 -1
  28. data/lib/project_types/theme/commands/serve.rb +1 -0
  29. data/lib/project_types/theme/messages/messages.rb +38 -1
  30. data/lib/shopify_cli/assets/post_auth_page/index.html.erb +34 -0
  31. data/lib/shopify_cli/assets/post_auth_page/style.css +58 -0
  32. data/lib/shopify_cli/git.rb +7 -2
  33. data/lib/shopify_cli/identity_auth/servlet.rb +4 -20
  34. data/lib/shopify_cli/messages/messages.rb +7 -9
  35. data/lib/shopify_cli/services/app/create/node_service.rb +1 -1
  36. data/lib/shopify_cli/services/app/create/php_service.rb +1 -1
  37. data/lib/shopify_cli/theme/dev_server/hot-reload-no-script.html +27 -0
  38. data/lib/shopify_cli/theme/dev_server/hot-reload.js +16 -4
  39. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +2 -0
  40. data/lib/shopify_cli/theme/dev_server.rb +3 -2
  41. data/lib/shopify_cli/theme/file.rb +5 -0
  42. data/lib/shopify_cli/theme/syncer/json_update_handler.rb +16 -6
  43. data/lib/shopify_cli/theme/syncer/operation.rb +7 -6
  44. data/lib/shopify_cli/theme/syncer/unsupported_script_warning.rb +90 -0
  45. data/lib/shopify_cli/theme/syncer.rb +73 -29
  46. data/lib/shopify_cli/theme/theme_admin_api.rb +16 -11
  47. data/lib/shopify_cli/theme/theme_admin_api_throttler/bulk.rb +102 -0
  48. data/lib/shopify_cli/theme/theme_admin_api_throttler/bulk_job.rb +75 -0
  49. data/lib/shopify_cli/theme/theme_admin_api_throttler/errors.rb +7 -0
  50. data/lib/shopify_cli/theme/theme_admin_api_throttler/put_request.rb +52 -0
  51. data/lib/shopify_cli/theme/theme_admin_api_throttler/request_parser.rb +39 -0
  52. data/lib/shopify_cli/theme/theme_admin_api_throttler/response_parser.rb +21 -0
  53. data/lib/shopify_cli/theme/theme_admin_api_throttler.rb +62 -0
  54. data/lib/shopify_cli/version.rb +1 -1
  55. data/shopify-cli.gemspec +1 -1
  56. metadata +20 -8
@@ -16,6 +16,8 @@ module Theme
16
16
  ensure_user_try_this: <<~ENSURE_USER,
17
17
  Check if your user is activated, has permission to edit themes at the store, and try to re-login.
18
18
  ENSURE_USER
19
+ stable_flag_suggestion: "If the current command isn't working as expected," \
20
+ " we suggest re-running the command with the {{command: --stable}} flag",
19
21
  init: {
20
22
  help: <<~HELP,
21
23
  {{command:%s theme init}}: Clones a Git repository to use as a starting point for building a new theme.
@@ -179,6 +181,41 @@ module Theme
179
181
  exit: "Exit",
180
182
  },
181
183
  },
184
+ warnings: {
185
+ unsupported_script: "unsupported script",
186
+ unsupported_script_text: <<~UNSUPPORTED_SCRIPT,
187
+
188
+ {{underline:Unsupported external checkout script}}
189
+
190
+ You have a code snippet on your storefront that violates
191
+ Shopify's Terms of Service. This script removes Shopify's
192
+ ability to protect your store against fraudulent orders,
193
+ could steal customer data and may cause customers to be
194
+ charged the wrong amount.
195
+
196
+ %s
197
+ By proceeding, you're acknowledging that you understand the
198
+ risks and will not hold Shopify liable for any problems that
199
+ occur due to the use of an external checkout, including:
200
+
201
+ - Discounts
202
+ - Shipping rules
203
+ - Multi-currency rules
204
+ - Variant selection
205
+ - Orders and fulfillment workflows
206
+ - Shopify Fraud Protection
207
+ - Payment settings
208
+ - Cart
209
+
210
+ You also acknowledge that you will not be able to reliably
211
+ get support for those features from Shopify because you are
212
+ violating Shopify's terms of service and that your account
213
+ may become suspended as a result.
214
+ UNSUPPORTED_SCRIPT
215
+ line_and_column: <<~LINE_AND_COLUMN,
216
+ - Line: %s Column: %s
217
+ LINE_AND_COLUMN
218
+ },
182
219
  },
183
220
  error: {
184
221
  address_binding_error: "Couldn't bind to localhost."\
@@ -309,7 +346,7 @@ module Theme
309
346
  {{command:-t, --theme=NAME_OR_ID}} Theme ID or name of your theme.
310
347
  {{command:-l, --live}} Open your live theme.
311
348
  {{command:-d, --development}} Open your development theme.
312
- {{command:-e, --editor}} Open the editor to the specified/selected theme.
349
+ {{command:-e, --editor}} Open the theme editor for the specified theme in the browser.
313
350
  HELP
314
351
  },
315
352
  list: {
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Shopify CLI</title>
8
+ <style><%= locals[:css] %></style>
9
+ <link rel="icon" href="data:image/svg+xml;charset=UTF-8,%3c?xml version='1.0' encoding='utf-8'?%3e%3c!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3e%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 109.5 124.5' style='enable-background:new 0 0 109.5 124.5;' xml:space='preserve'%3e%3cstyle type='text/css'%3e .st0%7bfill:%2395BF47;%7d .st1%7bfill:%235E8E3E;%7d .st2%7bfill:%23FFFFFF;%7d %3c/style%3e%3cg%3e%3cpath class='st0' d='M95.9,23.9c-0.1-0.6-0.6-1-1.1-1c-0.5,0-9.3-0.2-9.3-0.2s-7.4-7.2-8.1-7.9c-0.7-0.7-2.2-0.5-2.7-0.3 c0,0-1.4,0.4-3.7,1.1c-0.4-1.3-1-2.8-1.8-4.4c-2.6-5-6.5-7.7-11.1-7.7c0,0,0,0,0,0c-0.3,0-0.6,0-1,0.1c-0.1-0.2-0.3-0.3-0.4-0.5 c-2-2.2-4.6-3.2-7.7-3.1c-6,0.2-12,4.5-16.8,12.2c-3.4,5.4-6,12.2-6.8,17.5c-6.9,2.1-11.7,3.6-11.8,3.7c-3.5,1.1-3.6,1.2-4,4.5 c-0.3,2.5-9.5,73-9.5,73l76.4,13.2l33.1-8.2C109.5,115.8,96,24.5,95.9,23.9z M67.2,16.8c-1.8,0.5-3.8,1.2-5.9,1.8 c0-3-0.4-7.3-1.8-10.9C64,8.6,66.2,13.7,67.2,16.8z M57.2,19.9c-4,1.2-8.4,2.6-12.8,3.9c1.2-4.7,3.6-9.4,6.4-12.5 c1.1-1.1,2.6-2.4,4.3-3.2C56.9,11.6,57.3,16.5,57.2,19.9z M49.1,4c1.4,0,2.6,0.3,3.6,0.9C51.1,5.8,49.5,7,48,8.6 c-3.8,4.1-6.7,10.5-7.9,16.6c-3.6,1.1-7.2,2.2-10.5,3.2C31.7,18.8,39.8,4.3,49.1,4z'/%3e%3cg%3e%3cpath class='st1' d='M94.8,22.9c-0.5,0-9.3-0.2-9.3-0.2s-7.4-7.2-8.1-7.9c-0.3-0.3-0.6-0.4-1-0.5l0,109.7l33.1-8.2 c0,0-13.5-91.3-13.6-92C95.8,23.3,95.3,22.9,94.8,22.9z'/%3e%3cpath class='st2' d='M58,39.9l-3.8,14.4c0,0-4.3-2-9.4-1.6c-7.5,0.5-7.5,5.2-7.5,6.4c0.4,6.4,17.3,7.8,18.3,22.9 c0.7,11.9-6.3,20-16.4,20.6c-12.2,0.8-18.9-6.4-18.9-6.4l2.6-11c0,0,6.7,5.1,12.1,4.7c3.5-0.2,4.8-3.1,4.7-5.1 c-0.5-8.4-14.3-7.9-15.2-21.7c-0.7-11.6,6.9-23.4,23.7-24.4C54.7,38.2,58,39.9,58,39.9z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e" sizes="any" type="image/svg+xml">
10
+ </head>
11
+
12
+ <% if successful %>
13
+ <body class="body-success">
14
+ <div class="app-success">
15
+ <div class="container">
16
+ <h1><%= locals[:message] %></h1>
17
+ <p>You can close this tab and return to your terminal.</p>
18
+ </div>
19
+ </div>
20
+ </body>
21
+ <% else %>
22
+ <body class="body-error">
23
+ <div class="app-error">
24
+ <div class="container">
25
+ <h1>Something went wrong!</h1>
26
+ <p><%= locals[:message] %></p>
27
+ <br>
28
+ <br>
29
+ <p>Return to your terminal and try running the previous command again.</p>
30
+ </div>
31
+ </div>
32
+ </body>
33
+ <% end %>
34
+ </html>
@@ -0,0 +1,58 @@
1
+ html {
2
+ font-family: -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
3
+ text-size-adjust: 100%;
4
+ text-rendering: optimizeLegibility;
5
+ -webkit-font-smoothing: antialiased;
6
+ -moz-osx-font-smoothing: grayscale;
7
+ }
8
+
9
+ body {
10
+ font-size: 26px;
11
+ line-height: normal;
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ button, input, optgroup, select, textarea {
17
+ font-family: inherit;
18
+ }
19
+
20
+ h1 {
21
+ font-weight: 600;
22
+ font-size: 1em;
23
+ }
24
+
25
+ p {
26
+ font-weight: 400;
27
+ }
28
+
29
+ .body-success {
30
+ color: #F6F6F7;
31
+ }
32
+
33
+ .body-error {
34
+ color: #202223;
35
+ }
36
+
37
+ .app-success {
38
+ width: 100vw;
39
+ height: 100vh;
40
+ background-color: #054A49;
41
+ display: flex;
42
+ }
43
+
44
+ .app-error {
45
+ width: 100vw;
46
+ height: 100vh;
47
+ background-color: #F6F6F7;
48
+ display: flex;
49
+ }
50
+
51
+ .container {
52
+ display: flex;
53
+ flex-direction: column;
54
+ justify-content: center;
55
+ width: 100%;
56
+ height: 100%;
57
+ padding-left: 7.5em;
58
+ }
@@ -67,9 +67,14 @@ module ShopifyCLI
67
67
  if Dir.exist?(dest)
68
68
  ctx.abort(ctx.message("core.git.error.directory_exists"))
69
69
  else
70
+ repo, branch = repository.split("#")
70
71
  success_message = ctx.message("core.git.cloned", dest)
71
- CLI::UI::Frame.open(ctx.message("core.git.cloning", repository, dest), success_text: success_message) do
72
- clone_progress("clone", "--single-branch", repository, dest, ctx: ctx)
72
+ CLI::UI::Frame.open(ctx.message("core.git.cloning", repo, dest), success_text: success_message) do
73
+ if branch
74
+ clone_progress("clone", "--single-branch", "--branch", branch, repo, dest, ctx: ctx)
75
+ else
76
+ clone_progress("clone", "--single-branch", repo, dest, ctx: ctx)
77
+ end
73
78
  end
74
79
  end
75
80
  end
@@ -1,20 +1,8 @@
1
1
  module ShopifyCLI
2
2
  class IdentityAuth
3
3
  class Servlet < WEBrick::HTTPServlet::AbstractServlet
4
- TEMPLATE = %{<!DOCTYPE html>
5
- <html>
6
- <head>
7
- <title>%{title}</title>
8
- </head>
9
- <body>
10
- <h1 style="color: #%{color};">%{message}</h1>
11
- %{autoclose}
12
- </body>
13
- </html>
14
- }
15
- AUTOCLOSE_TEMPLATE = %{
16
- <script>window.close();</script>
17
- }
4
+ ERB_FILENAME = File.join(ROOT, "lib/shopify_cli/assets/post_auth_page/index.html.erb")
5
+ CSS_FILENAME = File.join(ROOT, "lib/shopify_cli/assets/post_auth_page/style.css")
18
6
 
19
7
  def initialize(server, identity_auth, token)
20
8
  super
@@ -46,14 +34,10 @@ module ShopifyCLI
46
34
  locals = {
47
35
  status: status,
48
36
  message: message,
49
- color: successful ? "black" : "red",
50
- title: Context.message(
51
- successful ? "core.identity_auth.servlet.authenticated" : "core.identity_auth.servlet.not_authenticated"
52
- ),
53
- autoclose: successful ? AUTOCLOSE_TEMPLATE : "",
37
+ css: File.read(CSS_FILENAME),
54
38
  }
55
39
  response.status = status
56
- response.body = format(TEMPLATE, locals)
40
+ response.body = ERB.new(File.read(ERB_FILENAME)).result(binding)
57
41
  end
58
42
  end
59
43
  end
@@ -285,7 +285,7 @@ module ShopifyCLI
285
285
  },
286
286
  extension: {
287
287
  push: {
288
- beacon_extension: {
288
+ web_pixel_extension: {
289
289
  error: {
290
290
  file_read_error: "There was a problem reading %s",
291
291
  missing_config_key_error: "Configuration is missing key: %s",
@@ -449,7 +449,7 @@ module ShopifyCLI
449
449
  login: {
450
450
  help: <<~HELP,
451
451
  Log in to the Shopify CLI by authenticating with a store or partner organization
452
- Usage: {{command:%s login [--store STORE]}}
452
+ Usage: {{command:%s login [--store/-s STORE]}}
453
453
  HELP
454
454
  invalid_shop: <<~MESSAGE,
455
455
  Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
@@ -476,7 +476,7 @@ module ShopifyCLI
476
476
  switch: {
477
477
  help: <<~HELP,
478
478
  Switch between development stores in your partner organization
479
- Usage: {{command:%s switch [--store STORE]}}
479
+ Usage: {{command:%s switch [--store/-s STORE]}}
480
480
  HELP
481
481
  disabled_as_shopify_org: "Can't switch development stores logged in as {{green:Shopify partners org}}",
482
482
  success: "Switched development store to {{green:%s}}",
@@ -498,11 +498,9 @@ module ShopifyCLI
498
498
  "{{i}} Authentication required. Login to the URL below with your %s credentials to continue.",
499
499
 
500
500
  servlet: {
501
- success_response: "Authenticated successfully. You may now close this page.",
501
+ success_response: "You've successfuly logged into the Shopify CLI!",
502
502
  invalid_request_response: "Invalid request: %s",
503
- invalid_state_response: "Anti-forgery state token does not match the initial request.",
504
- authenticated: "Authenticated successfully",
505
- not_authenticated: "Failed to authenticate",
503
+ invalid_state_response: "The anti-forgery state token does not match the initial request.",
506
504
  },
507
505
  login_prompt: "Please ensure you've logged in with {{command:%s login}} and try again",
508
506
  token_authentication: "%s environment variable. We'll authenticate using its value as a token.",
@@ -589,7 +587,7 @@ module ShopifyCLI
589
587
  HELP
590
588
 
591
589
  error: {
592
- no_shop: "No store found. Please run {{command:%s login --store STORE}} to login to a specific store",
590
+ no_shop: "No store found. Please run {{command:%s login --store/-s STORE}} to login to a specific store",
593
591
  },
594
592
 
595
593
  customer: {
@@ -820,7 +818,7 @@ module ShopifyCLI
820
818
  not_logged_in: <<~MESSAGE,
821
819
  It doesn't appear that you're logged in. You must log into a partner organization or a store staff account.
822
820
 
823
- If trying to log into a store staff account, please use {{command:%s login --store STORE}} to log in.
821
+ If trying to log into a store staff account, please use {{command:%s login --store/-s STORE}} to log in.
824
822
  MESSAGE
825
823
  logged_in_shop_only: <<~MESSAGE,
826
824
  Logged into store {{green:%s}} as staff (no partner organizations available for this login)
@@ -117,7 +117,7 @@ module ShopifyCLI
117
117
  end
118
118
 
119
119
  def build(name)
120
- ShopifyCLI::Git.clone("https://github.com/Shopify/shopify-app-node.git", name)
120
+ ShopifyCLI::Git.clone("https://github.com/Shopify/shopify-app-template-node.git#cli_two", name)
121
121
 
122
122
  context.root = File.join(context.root, name)
123
123
 
@@ -74,7 +74,7 @@ module ShopifyCLI
74
74
  end
75
75
 
76
76
  def build(form)
77
- ShopifyCLI::Git.clone("https://github.com/Shopify/shopify-app-php.git", form.name)
77
+ ShopifyCLI::Git.clone("https://github.com/Shopify/shopify-app-template-php.git#cli_two", form.name)
78
78
 
79
79
  context.root = File.join(context.root, form.name)
80
80
  context.chdir(context.root)
@@ -0,0 +1,27 @@
1
+ <noscript>
2
+ <style type="text/css">
3
+ .shopify-cli-no-script-message {
4
+ font-family: -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
5
+ text-size-adjust: 100%;
6
+ text-rendering: optimizeLegibility;
7
+ -webkit-font-smoothing: antialiased;
8
+ -moz-osx-font-smoothing: grayscale;
9
+ position: fixed;
10
+ z-index: 999;
11
+ font-weight: 500;
12
+ left: 0;
13
+ top: 0;
14
+ width: 100vw;
15
+ height: 100vh;
16
+ padding: 10rem 0;
17
+ color: #202223;
18
+ text-align: center;
19
+ background-color: #F6F6F7;
20
+ }
21
+ </style>
22
+ <div class="shopify-cli-no-script-message">
23
+ Shopify CLI requires JavaScript to work.
24
+ <br />
25
+ Activate JavaScript support or try a different browser.
26
+ </div>
27
+ </noscript>
@@ -1,3 +1,15 @@
1
+ (() => {
2
+ function verifySSE() {
3
+ if (typeof (EventSource) === "undefined") {
4
+ console.error("[HotReload] Error: SSE features are not supported. Try a different browser.");
5
+ }
6
+ }
7
+
8
+ console.log("[HotReload] Initializing...");
9
+
10
+ verifySSE();
11
+ })();
12
+
1
13
  (() => {
2
14
  function connect() {
3
15
  const eventSource = new EventSource('/hot-reload');
@@ -32,7 +44,7 @@
32
44
 
33
45
  function fetchDOMSections(name) {
34
46
  const domSections = sectionNamesByType(name).flatMap((n) => querySelectDOMSections(n));
35
-
47
+
36
48
  if (domSections.length > 0) {
37
49
  return domSections;
38
50
  }
@@ -40,11 +52,11 @@
40
52
  return querySelectDOMSections(name);
41
53
  }
42
54
 
43
- function isFullPageReloadMode(){
55
+ function isFullPageReloadMode() {
44
56
  return reloadMode() === 'full-page';
45
57
  }
46
58
 
47
- function isReloadModeActive(){
59
+ function isReloadModeActive() {
48
60
  return reloadMode() !== 'off';
49
61
  }
50
62
 
@@ -72,7 +84,7 @@
72
84
 
73
85
  // Hot reload cookie expires in 3 seconds
74
86
  date.setSeconds(date.getSeconds() + 3);
75
-
87
+
76
88
  var sections = files.join(',');
77
89
  var expires = date.toUTCString();
78
90
 
@@ -67,7 +67,9 @@ module ShopifyCLI
67
67
 
68
68
  def inject_hot_reload_javascript(body)
69
69
  hot_reload_js = ::File.read("#{__dir__}/hot-reload.js")
70
+ hot_reload_no_script = ::File.read("#{__dir__}/hot-reload-no-script.html")
70
71
  hot_reload_script = [
72
+ hot_reload_no_script,
71
73
  "<script>",
72
74
  params_js,
73
75
  hot_reload_js,
@@ -28,11 +28,12 @@ module ShopifyCLI
28
28
  attr_accessor :ctx
29
29
 
30
30
  def start(ctx, root, host: "127.0.0.1", theme: nil, port: 9292, poll: false, editor_sync: false,
31
- mode: ReloadMode.default)
31
+ mode: ReloadMode.default, stable: false)
32
32
  @ctx = ctx
33
33
  theme = find_theme(root, theme)
34
34
  ignore_filter = IgnoreFilter.from_path(root)
35
- @syncer = Syncer.new(ctx, theme: theme, ignore_filter: ignore_filter, overwrite_json: !editor_sync)
35
+ @syncer = Syncer.new(ctx, theme: theme, ignore_filter: ignore_filter, overwrite_json: !editor_sync,
36
+ stable: stable)
36
37
  watcher = Watcher.new(ctx, theme: theme, ignore_filter: ignore_filter, syncer: @syncer, poll: poll)
37
38
  remote_watcher = RemoteWatcher.to(theme: theme, syncer: @syncer)
38
39
 
@@ -5,6 +5,7 @@ module ShopifyCLI
5
5
  module Theme
6
6
  class File < Struct.new(:path)
7
7
  attr_accessor :remote_checksum
8
+ attr_writer :warnings
8
9
 
9
10
  def initialize(path, root)
10
11
  super(Pathname.new(path))
@@ -104,6 +105,10 @@ module ShopifyCLI
104
105
  @relative_path.to_s
105
106
  end
106
107
 
108
+ def warnings
109
+ @warnings || []
110
+ end
111
+
107
112
  private
108
113
 
109
114
  def normalize_json(content)
@@ -8,16 +8,11 @@ module ShopifyCLI
8
8
  class Syncer
9
9
  module JsonUpdateHandler
10
10
  def enqueue_json_updates(files)
11
- # Some files must be uploaded after the other ones
12
- delayed_files = [
13
- theme["config/settings_schema.json"],
14
- theme["config/settings_data.json"],
15
- ]
16
-
17
11
  # Update remote JSON files and delays `delayed_files` update
18
12
  files = files
19
13
  .select { |file| !ignore_file?(file) && file.exist? && checksums.file_has_changed?(file) }
20
14
  .sort_by { |file| delayed_files.include?(file) ? 1 : 0 }
15
+ .reject { |file| overwrite_json? && delayed_files.include?(file) }
21
16
 
22
17
  if overwrite_json?
23
18
  enqueue_updates(files)
@@ -27,6 +22,21 @@ module ShopifyCLI
27
22
  end
28
23
  end
29
24
 
25
+ def enqueue_delayed_files_updates
26
+ return unless overwrite_json?
27
+ # Update delayed files synchronously
28
+ delayed_files.each do |file|
29
+ update(file) if checksums.file_has_changed?(file)
30
+ end
31
+ end
32
+
33
+ def delayed_files
34
+ [
35
+ theme["config/settings_schema.json"],
36
+ theme["config/settings_data.json"],
37
+ ]
38
+ end
39
+
30
40
  private
31
41
 
32
42
  def handle_update_conflicts(files)
@@ -9,6 +9,7 @@ module ShopifyCLI
9
9
  COLOR_BY_STATUS = {
10
10
  error: :red,
11
11
  synced: :green,
12
+ warning: :yellow,
12
13
  fixed: :cyan,
13
14
  }
14
15
 
@@ -26,8 +27,8 @@ module ShopifyCLI
26
27
  as_message_with(status: :error)
27
28
  end
28
29
 
29
- def as_synced_message
30
- as_message_with(status: :synced)
30
+ def as_synced_message(color: :green)
31
+ as_message_with(status: :synced, color: color)
31
32
  end
32
33
 
33
34
  def as_fix_message
@@ -40,11 +41,11 @@ module ShopifyCLI
40
41
 
41
42
  private
42
43
 
43
- def as_message_with(status:)
44
- status_color = COLOR_BY_STATUS[status]
45
- status_text = @ctx.message("theme.serve.operation.status.#{status}").ljust(6)
44
+ def as_message_with(status:, color: nil)
45
+ color ||= COLOR_BY_STATUS[status]
46
+ text = @ctx.message("theme.serve.operation.status.#{status}").ljust(6)
46
47
 
47
- "#{timestamp} {{#{status_color}:#{status_text}}} {{>}} {{blue:#{self}}}"
48
+ "#{timestamp} {{#{color}:#{text}}} {{>}} {{blue:#{self}}}"
48
49
  end
49
50
 
50
51
  def timestamp
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyCLI
4
+ module Theme
5
+ class Syncer
6
+ class UnsupportedScriptWarning
7
+ attr_reader :ctx
8
+
9
+ def initialize(ctx, file)
10
+ @ctx = ctx
11
+ @file = file
12
+ end
13
+
14
+ def to_s
15
+ "\n\n#{occurrences} #{long_text}"
16
+ end
17
+
18
+ private
19
+
20
+ def occurrences
21
+ warnings.map { |w| occurrence(w) }.join("\n")
22
+ end
23
+
24
+ def occurrence(warning)
25
+ line_number = "{{blue: #{warning.line} |}}"
26
+ pointer = pointer_message(warning)
27
+
28
+ <<~OCCURRENCE
29
+ #{line_number} #{warning.line_content}
30
+ #{pointer}
31
+ OCCURRENCE
32
+ end
33
+
34
+ def long_text
35
+ lines_and_columns = warnings.map do |warning|
36
+ message("line_and_column", warning.line, warning.column)
37
+ end
38
+
39
+ message("unsupported_script_text", lines_and_columns.join)
40
+ .split("\n")
41
+ .reduce("") do |text, line|
42
+ # Add indentation in the long text to improve readability
43
+ line = " #{line}"
44
+
45
+ # Inline yellow (otherwise `CLI::UI::Frame` breaks multiline formatting)
46
+ line = "{{yellow:#{line}}}"
47
+
48
+ "#{text}#{line}\n"
49
+ end
50
+ end
51
+
52
+ def pointer_message(warning)
53
+ padding = warning.column + warning.line.to_s.size + 2
54
+ text = message("unsupported_script")
55
+
56
+ "{{yellow:#{" " * padding} ^ {{bold:#{text}}}}}"
57
+ end
58
+
59
+ def message(*args)
60
+ key = args.shift
61
+ @ctx.message("theme.serve.syncer.warnings.#{key}", *args)
62
+ end
63
+
64
+ def warnings
65
+ @warnings ||= @file.warnings.map { |w| Warning.new(@file, w) }
66
+ end
67
+
68
+ class Warning
69
+ attr_reader :line, :column
70
+
71
+ def initialize(file, warning_hash)
72
+ @file = file
73
+ @line = warning_hash["line"].to_i
74
+ @column = warning_hash["column"].to_i
75
+ end
76
+
77
+ def line_content
78
+ file_lines[line - 1]
79
+ end
80
+
81
+ private
82
+
83
+ def file_lines
84
+ @file_lines ||= @file.read.split("\n")
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end