shopify-cli 2.11.0 → 2.12.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/Gemfile.lock +1 -1
  4. data/bin/shopify +13 -0
  5. data/docs/users/installation.md +1 -44
  6. data/lib/project_types/extension/commands/build.rb +0 -3
  7. data/lib/project_types/extension/commands/check.rb +0 -1
  8. data/lib/project_types/extension/commands/create.rb +0 -1
  9. data/lib/project_types/extension/commands/push.rb +13 -1
  10. data/lib/project_types/extension/commands/serve.rb +0 -1
  11. data/lib/project_types/extension/loaders/project.rb +28 -8
  12. data/lib/project_types/extension/messages/messages.rb +10 -2
  13. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  14. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +7 -1
  15. data/lib/project_types/script/cli.rb +2 -0
  16. data/lib/project_types/script/commands/create.rb +2 -2
  17. data/lib/project_types/script/commands/push.rb +4 -6
  18. data/lib/project_types/script/config/extension_points.yml +0 -4
  19. data/lib/project_types/script/forms/create.rb +1 -14
  20. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  21. data/lib/project_types/script/layers/application/create_script.rb +1 -1
  22. data/lib/project_types/script/layers/application/push_script.rb +1 -1
  23. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  24. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +2 -6
  25. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +30 -26
  26. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +35 -9
  27. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  28. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +3 -6
  29. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +31 -27
  30. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +3 -7
  31. data/lib/project_types/script/loaders/project.rb +8 -7
  32. data/lib/project_types/script/messages/messages.rb +14 -13
  33. data/lib/project_types/script/ui/error_handler.rb +13 -0
  34. data/lib/project_types/theme/commands/check.rb +0 -1
  35. data/lib/project_types/theme/commands/common/root_helper.rb +65 -0
  36. data/lib/project_types/theme/commands/delete.rb +0 -1
  37. data/lib/project_types/theme/commands/init.rb +2 -1
  38. data/lib/project_types/theme/commands/language_server.rb +0 -1
  39. data/lib/project_types/theme/commands/package.rb +0 -1
  40. data/lib/project_types/theme/commands/publish.rb +0 -1
  41. data/lib/project_types/theme/commands/pull.rb +18 -9
  42. data/lib/project_types/theme/commands/push.rb +16 -11
  43. data/lib/project_types/theme/commands/serve.rb +6 -3
  44. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  45. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  46. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  47. data/lib/project_types/theme/messages/messages.rb +5 -5
  48. data/lib/shopify_cli/command.rb +11 -3
  49. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  50. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  51. data/lib/shopify_cli/commands/app/create/rails.rb +2 -1
  52. data/lib/shopify_cli/commands/app/create.rb +0 -3
  53. data/lib/shopify_cli/commands/app/deploy.rb +1 -1
  54. data/lib/shopify_cli/commands/app/serve.rb +0 -1
  55. data/lib/shopify_cli/constants.rb +2 -2
  56. data/lib/shopify_cli/environment.rb +45 -45
  57. data/lib/shopify_cli/git.rb +9 -1
  58. data/lib/shopify_cli/messages/messages.rb +23 -2
  59. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  60. data/lib/shopify_cli/tasks.rb +1 -0
  61. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +63 -0
  62. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +22 -6
  63. data/lib/shopify_cli/theme/dev_server/proxy.rb +18 -7
  64. data/lib/shopify_cli/theme/dev_server.rb +1 -3
  65. data/lib/shopify_cli/theme/development_theme.rb +11 -0
  66. data/lib/shopify_cli/theme/file.rb +4 -0
  67. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  68. data/lib/shopify_cli/theme/syncer.rb +13 -4
  69. data/lib/shopify_cli/theme/theme.rb +0 -4
  70. data/lib/shopify_cli/version.rb +1 -1
  71. metadata +9 -2
@@ -4,17 +4,16 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class AssemblyScriptTaskRunner
7
+ class AssemblyScriptTaskRunner < TaskRunner
8
+ NODE_MIN_VERSION = "14.15.0"
9
+ NPM_MIN_VERSION = "5.2.0"
10
+
8
11
  BYTECODE_FILE = "build/script.wasm"
9
12
  METADATA_FILE = "build/metadata.json"
10
13
  SCRIPT_SDK_BUILD = "npm run build"
11
-
12
- attr_reader :ctx, :script_name
13
-
14
- def initialize(ctx, script_name)
15
- @ctx = ctx
16
- @script_name = script_name
17
- end
14
+ NPM_SET_REGISTRY_COMMAND = "npm --userconfig ./.npmrc config set @shopify:registry https://registry.npmjs.com"
15
+ NPM_SET_ENGINE_STRICT_COMMAND = "npm --userconfig ./.npmrc config set engine-strict true"
16
+ NPM_INSTALL_COMMAND = "npm install --no-audit --no-optional --legacy-peer-deps --loglevel error"
18
17
 
19
18
  def build
20
19
  compile
@@ -22,10 +21,10 @@ module Script
22
21
  end
23
22
 
24
23
  def install_dependencies
25
- check_node_version!
24
+ run_cmd_with_env_check(NPM_INSTALL_COMMAND)
26
25
 
27
- output, status = ctx.capture2e("npm install --no-audit --no-optional --legacy-peer-deps --loglevel error")
28
- raise Errors::DependencyInstallError, output unless status.success?
26
+ rescue Errors::SystemCallFailureError => e
27
+ raise Errors::DependencyInstallError, e.out
29
28
  end
30
29
 
31
30
  def dependencies_installed?
@@ -38,14 +37,32 @@ module Script
38
37
  end
39
38
 
40
39
  def library_version(library_name)
41
- output = JSON.parse(CommandRunner.new(ctx: ctx).call("npm -s list --json"))
40
+ output = JSON.parse(run_cmd_with_env_check("npm -s list --json"))
42
41
  library_version_from_npm_list(output, library_name)
43
42
  rescue Errors::SystemCallFailureError => error
44
43
  library_version_from_npm_list_error_output(error, library_name)
45
44
  end
46
45
 
46
+ def set_npm_config
47
+ run_cmd_with_env_check(NPM_SET_REGISTRY_COMMAND)
48
+ run_cmd_with_env_check(NPM_SET_ENGINE_STRICT_COMMAND)
49
+ end
50
+
47
51
  private
48
52
 
53
+ def ensure_environment
54
+ return if defined?(@environment_checked)
55
+ @environment_checked = true
56
+
57
+ ToolVersionChecker.check_node(minimum_version: NODE_MIN_VERSION)
58
+ ToolVersionChecker.check_npm(minimum_version: NPM_MIN_VERSION)
59
+ end
60
+
61
+ def run_cmd_with_env_check(cmd)
62
+ ensure_environment
63
+ CommandRunner.new(ctx: ctx).call(cmd)
64
+ end
65
+
49
66
  def library_version_from_npm_list_error_output(error, library_name)
50
67
  # npm list can return a failure status code, even when returning the correct data.
51
68
  # This causes the CommandRunner to throw a SystemCallFailure error that contains the data.
@@ -64,22 +81,9 @@ module Script
64
81
  end
65
82
  end
66
83
 
67
- def check_node_version!
68
- output, status = @ctx.capture2e("node", "--version")
69
- raise Errors::DependencyInstallError, output unless status.success?
70
-
71
- require "semantic/semantic"
72
- version = ::Semantic::Version.new(output[1..-1])
73
- unless version >= ::Semantic::Version.new(AssemblyScriptProjectCreator::MIN_NODE_VERSION)
74
- raise Errors::DependencyInstallError,
75
- "Node version must be >= v#{AssemblyScriptProjectCreator::MIN_NODE_VERSION}. "\
76
- "Current version: #{output.strip}."
77
- end
78
- end
79
-
80
84
  def compile
81
85
  check_compilation_dependencies!
82
- CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
86
+ run_cmd_with_env_check(SCRIPT_SDK_BUILD)
83
87
  end
84
88
 
85
89
  def check_compilation_dependencies!
@@ -5,15 +5,41 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
7
  class TaskRunner
8
- TASK_RUNNERS = {
9
- "assemblyscript" => AssemblyScriptTaskRunner,
10
- "typescript" => TypeScriptTaskRunner,
11
- "wasm" => WasmTaskRunner,
12
- }
13
-
14
- def self.for(ctx, language, script_name)
15
- raise Errors::TaskRunnerNotFoundError unless TASK_RUNNERS[language]
16
- TASK_RUNNERS[language].new(ctx, script_name)
8
+ attr_reader :ctx
9
+
10
+ def self.for(ctx, language)
11
+ task_runners = {
12
+ "assemblyscript" => AssemblyScriptTaskRunner,
13
+ "typescript" => TypeScriptTaskRunner,
14
+ "wasm" => WasmTaskRunner,
15
+ }
16
+
17
+ raise Errors::TaskRunnerNotFoundError unless task_runners[language]
18
+ task_runners[language].new(ctx)
19
+ end
20
+
21
+ def initialize(ctx)
22
+ @ctx = ctx
23
+ end
24
+
25
+ def build
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def dependencies_installed?
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def install_dependencies
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def metadata_file_location
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def library_version(_library_name)
42
+ raise NotImplementedError
17
43
  end
18
44
  end
19
45
  end
@@ -0,0 +1,26 @@
1
+ module Script
2
+ module Layers
3
+ module Infrastructure
4
+ module Languages
5
+ class ToolVersionChecker
6
+ class << self
7
+ def check_node(minimum_version:)
8
+ check_version("node", ShopifyCLI::Environment.node_version, minimum_version)
9
+ end
10
+
11
+ def check_npm(minimum_version:)
12
+ check_version("npm", ShopifyCLI::Environment.npm_version, minimum_version)
13
+ end
14
+
15
+ private
16
+
17
+ def check_version(tool, env_version, minimum_version)
18
+ return if env_version >= ::Semantic::Version.new(minimum_version)
19
+ raise Errors::InvalidEnvironmentError.new(tool, env_version, minimum_version)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -5,18 +5,15 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
7
  class TypeScriptProjectCreator < ProjectCreator
8
- MIN_NODE_VERSION = "14.15.0"
9
- NPM_SET_REGISTRY_COMMAND = "npm --userconfig ./.npmrc config set @shopify:registry https://registry.npmjs.com"
10
- NPM_SET_ENGINE_STRICT_COMMAND = "npm --userconfig ./.npmrc config set engine-strict true"
11
-
12
8
  def self.config_file
13
9
  "package.json"
14
10
  end
15
11
 
16
12
  def setup_dependencies
13
+ task_runner = Infrastructure::Languages::TypeScriptTaskRunner.new(ctx)
14
+ task_runner.set_npm_config
15
+
17
16
  super
18
- command_runner.call(NPM_SET_REGISTRY_COMMAND)
19
- command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
20
17
 
21
18
  if ctx.file_exist?("yarn.lock")
22
19
  ctx.rm("yarn.lock")
@@ -4,18 +4,17 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class TypeScriptTaskRunner
7
+ class TypeScriptTaskRunner < TaskRunner
8
+ NODE_MIN_VERSION = "14.15.0"
9
+ NPM_MIN_VERSION = "5.2.0"
10
+
8
11
  BYTECODE_FILE = "build/index.wasm"
9
12
  METADATA_FILE = "build/metadata.json"
10
13
  SCRIPT_SDK_BUILD = "npm run build"
11
14
  GEN_METADATA = "npm run gen-metadata"
12
-
13
- attr_reader :ctx, :script_name
14
-
15
- def initialize(ctx, script_name)
16
- @ctx = ctx
17
- @script_name = script_name
18
- end
15
+ NPM_SET_REGISTRY_COMMAND = "npm --userconfig ./.npmrc config set @shopify:registry https://registry.npmjs.com"
16
+ NPM_SET_ENGINE_STRICT_COMMAND = "npm --userconfig ./.npmrc config set engine-strict true"
17
+ NPM_INSTALL_COMMAND = "npm install --no-audit --no-optional --legacy-peer-deps --loglevel error"
19
18
 
20
19
  def build
21
20
  compile
@@ -23,10 +22,10 @@ module Script
23
22
  end
24
23
 
25
24
  def install_dependencies
26
- check_node_version!
25
+ run_cmd_with_env_check(NPM_INSTALL_COMMAND)
27
26
 
28
- output, status = ctx.capture2e("npm install --no-audit --no-optional --legacy-peer-deps --loglevel error")
29
- raise Errors::DependencyInstallError, output unless status.success?
27
+ rescue Errors::SystemCallFailureError => e
28
+ raise Errors::DependencyInstallError, e.out
30
29
  end
31
30
 
32
31
  def dependencies_installed?
@@ -39,14 +38,32 @@ module Script
39
38
  end
40
39
 
41
40
  def library_version(library_name)
42
- output = JSON.parse(CommandRunner.new(ctx: ctx).call("npm -s list --json"))
41
+ output = JSON.parse(run_cmd_with_env_check("npm -s list --json"))
43
42
  library_version_from_npm_list(output, library_name)
44
43
  rescue Errors::SystemCallFailureError => error
45
44
  library_version_from_npm_list_error_output(error, library_name)
46
45
  end
47
46
 
47
+ def set_npm_config
48
+ run_cmd_with_env_check(NPM_SET_REGISTRY_COMMAND)
49
+ run_cmd_with_env_check(NPM_SET_ENGINE_STRICT_COMMAND)
50
+ end
51
+
48
52
  private
49
53
 
54
+ def ensure_environment
55
+ return if defined?(@environment_checked)
56
+ @environment_checked = true
57
+
58
+ ToolVersionChecker.check_node(minimum_version: NODE_MIN_VERSION)
59
+ ToolVersionChecker.check_npm(minimum_version: NPM_MIN_VERSION)
60
+ end
61
+
62
+ def run_cmd_with_env_check(cmd)
63
+ ensure_environment
64
+ CommandRunner.new(ctx: ctx).call(cmd)
65
+ end
66
+
50
67
  def library_version_from_npm_list_error_output(error, library_name)
51
68
  # npm list can return a failure status code, even when returning the correct data.
52
69
  # This causes the CommandRunner to throw a SystemCallFailure error that contains the data.
@@ -65,23 +82,10 @@ module Script
65
82
  end
66
83
  end
67
84
 
68
- def check_node_version!
69
- output, status = @ctx.capture2e("node", "--version")
70
- raise Errors::DependencyInstallError, output unless status.success?
71
-
72
- require "semantic/semantic"
73
- version = ::Semantic::Version.new(output[1..-1])
74
- unless version >= ::Semantic::Version.new(TypeScriptProjectCreator::MIN_NODE_VERSION)
75
- raise Errors::DependencyInstallError,
76
- "Node version must be >= v#{TypeScriptProjectCreator::MIN_NODE_VERSION}. "\
77
- "Current version: #{output.strip}."
78
- end
79
- end
80
-
81
85
  def compile
82
86
  check_compilation_dependencies!
83
- CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
84
- CommandRunner.new(ctx: ctx).call(GEN_METADATA)
87
+ run_cmd_with_env_check(SCRIPT_SDK_BUILD)
88
+ run_cmd_with_env_check(GEN_METADATA)
85
89
  end
86
90
 
87
91
  def check_compilation_dependencies!
@@ -4,19 +4,15 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class WasmTaskRunner
7
+ class WasmTaskRunner < TaskRunner
8
8
  BYTECODE_FILE = "script.wasm"
9
- attr_reader :ctx, :script_name
10
-
11
- def initialize(ctx, script_name)
12
- @ctx = ctx
13
- @script_name = script_name
14
- end
15
9
 
16
10
  def dependencies_installed?
17
11
  true
18
12
  end
19
13
 
14
+ def install_dependencies; end
15
+
20
16
  def library_version(_library_name)
21
17
  nil
22
18
  end
@@ -20,21 +20,22 @@ module Script
20
20
  project.env = env
21
21
  project
22
22
  rescue SmartProperties::InitializationError, SmartProperties::InvalidValueError => error
23
- handle_error(error, context: context, env_file_present: env_file_present)
23
+ handle_error(error, context: context)
24
24
  end
25
25
 
26
- def self.handle_error(error, context:, env_file_present:)
27
- if env_file_present
26
+ def self.handle_error(error, context:)
27
+ if ShopifyCLI::Environment.interactive?
28
28
  properties_hash = { api_key: "SHOPIFY_API_KEY", secret: "SHOPIFY_API_SECRET" }
29
29
  missing_env_variables = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
30
- raise ShopifyCLI::Abort,
31
- context.message("script.error.missing_env_file_variables", missing_env_variables, ShopifyCLI::TOOL_NAME)
30
+ message = context.message("script.error.missing_env_file_variables", missing_env_variables)
31
+ message += context.message("script.error.missing_env_file_variables_solution", ShopifyCLI::TOOL_NAME)
32
32
  else
33
33
  properties_hash = { api_key: "--api-key", secret: "--api-secret" }
34
34
  missing_options = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
35
- raise ShopifyCLI::Abort, context.message("script.error.missing_push_options", missing_options,
36
- ShopifyCli::TOOL_NAME)
35
+ message = context.message("script.error.missing_push_options_ci", missing_options)
36
+ message += context.message("script.error.missing_push_options_ci_solution", ShopifyCLI::TOOL_NAME)
37
37
  end
38
+ raise ShopifyCLI::Abort, message
38
39
  end
39
40
 
40
41
  def self.env_file_exists?(directory)
@@ -113,6 +113,10 @@ module Script
113
113
  dependency_install_cause: "Something went wrong while installing the needed dependencies.",
114
114
  dependency_install_help: "Correct the errors.",
115
115
 
116
+ invalid_environment_cause: "Your environment %{tool} version, %{env_version}, "\
117
+ "is too low. It must be at least %{minimum_version}.",
118
+ invalid_environment_help: "Update %{tool}.",
119
+
116
120
  failed_api_request_cause: "Something went wrong while communicating with Shopify.",
117
121
  failed_api_request_help: "Try again.",
118
122
 
@@ -149,12 +153,14 @@ module Script
149
153
 
150
154
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
151
155
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
152
- no_scripts_found_in_app: "The selected apps have no scripts. Create them first on the partners' dashboard.",
153
- missing_env_file_variables: "The following variables are missing in the .env file: %s."\
154
- " This can occur when the script hasn't been connected to an app."\
155
- " To connect the script to an app, run {{command:%s script connect}}",
156
- missing_push_options: "The following options are missing from .env: %s."\
157
- " Run {{command:%s script connect}} to connect the script to an app and generate these options.",
156
+ no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
157
+ missing_push_options_ci: "The following are missing: %s. ",
158
+ missing_push_options_ci_solution: "To add them to a CI environment:\n\t1. Run a connect command " \
159
+ "({{command:%1$s script connect}})\n\t2. Navigate to the .env file at the root of your project\n\t" \
160
+ "3. Copy the missing values and pass them through as arguments in {{command:%1$s script push}}",
161
+ missing_env_file_variables: "The following are missing in the .env file: %s. ",
162
+ missing_env_file_variables_solution: "To add it, connect your script with " \
163
+ "{{command:%1$s script connect}} ",
158
164
  },
159
165
 
160
166
  create: {
@@ -164,7 +170,7 @@ module Script
164
170
  Options:
165
171
  {{command:--name=NAME}} Script project name.
166
172
  {{command:--api=TYPE}} Script API name. Supported values: %2$s.
167
- {{command:--language=LANGUAGE}} Programming language. Supported values: %3$s.
173
+ {{command:--language=LANGUAGE}} Programming language. Defaults to wasm. Supported values: %3$s.
168
174
  HELP
169
175
 
170
176
  error: {
@@ -191,9 +197,7 @@ module Script
191
197
  HELP
192
198
 
193
199
  error: {
194
- operation_failed_no_uuid: "UUID is required to push in a CI environment.",
195
- operation_failed_with_api_key: "Couldn't push script to app (API key: %{api_key}).",
196
- operation_failed_no_api_key: "Couldn't push script to app.",
200
+ operation_failed: "Couldn't push script to app.",
197
201
  },
198
202
 
199
203
  script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
@@ -206,8 +210,6 @@ module Script
206
210
  HELP
207
211
  error: {
208
212
  operation_failed: "Couldn't connect script to app.",
209
- missing_env_file_variables: "The following variables are missing in the .env file: %s."\
210
- " To connect the script to an app, enter the value into the .env file or delete the .env file, and then run {{command:%s script connect}}",
211
213
  },
212
214
  },
213
215
  javy: {
@@ -233,7 +235,6 @@ module Script
233
235
  forms: {
234
236
  create: {
235
237
  select_extension_point: "Which Script API do you want to use?",
236
- select_language: "Which language do you want to use?",
237
238
  script_name: "What do you want to name your script?",
238
239
  },
239
240
  },
@@ -237,6 +237,19 @@ module Script
237
237
  ),
238
238
  help_suggestion: ShopifyCLI::Context.message("script.error.graphql_error_help"),
239
239
  }
240
+ when Layers::Infrastructure::Errors::InvalidEnvironmentError
241
+ {
242
+ cause_of_error: ShopifyCLI::Context.message(
243
+ "script.error.invalid_environment_cause",
244
+ tool: e.tool,
245
+ env_version: e.env_version,
246
+ minimum_version: e.minimum_version,
247
+ ),
248
+ help_suggestion: ShopifyCLI::Context.message(
249
+ "script.error.invalid_environment_help",
250
+ tool: e.tool,
251
+ ),
252
+ }
240
253
  when Layers::Infrastructure::Errors::SystemCallFailureError
241
254
  {
242
255
  cause_of_error: ShopifyCLI::Context
@@ -4,7 +4,6 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class Check < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  class Options < ShopifyCLI::Options
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theme
4
+ class Command
5
+ module Common
6
+ module RootHelper
7
+ def root_value(options, name)
8
+ argv = default_argv(options)
9
+ command_index = argv.index(name.to_s)
10
+
11
+ return "." if command_index.nil?
12
+
13
+ next_index = command_index + 1
14
+ option_by_key = options_map(options)
15
+
16
+ while next_index < argv.size
17
+ element = argv[next_index]
18
+ option = option_by_key[element]
19
+
20
+ return element if option.nil?
21
+
22
+ # Skip the option argument
23
+ next_index += 1 unless option.arg.nil?
24
+
25
+ # PATTERN arguments take precedence over the `root`
26
+ if option.arg =~ /PATTERN/
27
+ next_index += 1 while option_argument?(argv, next_index, option_by_key)
28
+ next
29
+ end
30
+
31
+ next_index += 1
32
+ end
33
+
34
+ "."
35
+ end
36
+
37
+ private
38
+
39
+ def default_argv(options)
40
+ options.parser.default_argv
41
+ end
42
+
43
+ def options_map(options)
44
+ map = {}
45
+ options_list(options).each do |option|
46
+ map[option.short.first] = option
47
+ map[option.long.first] = option
48
+ end
49
+ map
50
+ end
51
+
52
+ def options_list(options)
53
+ options.parser.top.list
54
+ end
55
+
56
+ def option_argument?(argv, next_index, option_by_key)
57
+ return false unless next_index < argv.size
58
+
59
+ element = argv[next_index]
60
+ option_by_key[element].nil?
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -5,7 +5,6 @@ require "shopify_cli/theme/development_theme"
5
5
  module Theme
6
6
  class Command
7
7
  class Delete < ShopifyCLI::Command::SubCommand
8
- recommend_default_node_range
9
8
  recommend_default_ruby_range
10
9
 
11
10
  options do |parser, flags|
@@ -3,13 +3,14 @@
3
3
  module Theme
4
4
  class Command
5
5
  class Init < ShopifyCLI::Command::SubCommand
6
- recommend_default_node_range
7
6
  recommend_default_ruby_range
8
7
 
9
8
  options do |parser, flags|
10
9
  parser.on("-u", "--clone-url URL") { |url| flags[:clone_url] = url }
11
10
  end
12
11
 
12
+ prerequisite_task :ensure_git_dependency
13
+
13
14
  DEFAULT_CLONE_URL = "https://github.com/Shopify/dawn.git"
14
15
 
15
16
  def call(args, _name)
@@ -4,7 +4,6 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class LanguageServer < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  def call(*)
@@ -5,7 +5,6 @@ require "json"
5
5
  module Theme
6
6
  class Command
7
7
  class Package < ShopifyCLI::Command::SubCommand
8
- recommend_default_node_range
9
8
  recommend_default_ruby_range
10
9
 
11
10
  THEME_DIRECTORIES = %w[
@@ -4,7 +4,6 @@ require "shopify_cli/theme/theme"
4
4
  module Theme
5
5
  class Command
6
6
  class Publish < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  options do |parser, flags|
@@ -1,38 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/theme"
3
+ require "shopify_cli/theme/development_theme"
3
4
  require "shopify_cli/theme/ignore_filter"
4
5
  require "shopify_cli/theme/include_filter"
5
6
  require "shopify_cli/theme/syncer"
7
+ require "project_types/theme/commands/common/root_helper"
8
+ require "project_types/theme/conversions/include_glob"
9
+ require "project_types/theme/conversions/ignore_glob"
6
10
 
7
11
  module Theme
8
12
  class Command
9
13
  class Pull < ShopifyCLI::Command::SubCommand
10
- recommend_default_node_range
14
+ include Common::RootHelper
15
+
11
16
  recommend_default_ruby_range
12
17
 
13
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
14
22
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
15
23
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
16
24
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
17
25
  parser.on("-l", "--live") { flags[:live] = true }
18
26
  parser.on("-d", "--development") { flags[:development] = true }
19
- parser.on("-o", "--only=PATTERN") do |pattern|
27
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
20
28
  flags[:includes] ||= []
21
- flags[:includes] << pattern
29
+ flags[:includes] += pattern
22
30
  end
23
- parser.on("-x", "--ignore=PATTERN") do |pattern|
31
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
24
32
  flags[:ignores] ||= []
25
- flags[:ignores] << pattern
33
+ flags[:ignores] += pattern
26
34
  end
27
35
  end
28
36
 
29
- def call(args, _name)
30
- root = args.first || "."
37
+ def call(_args, name)
38
+ root = root_value(options, name)
31
39
  delete = !options.flags[:nodelete]
32
40
  theme = find_theme(root, **options.flags)
33
41
  return if theme.nil?
34
42
 
35
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
43
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
36
44
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
37
45
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
38
46
 
@@ -74,7 +82,8 @@ module Theme
74
82
  end
75
83
 
76
84
  if development
77
- return ShopifyCLI::Theme::Theme.development(@ctx, root: root)
85
+ dev_theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx, root: root)
86
+ return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", "development"))
78
87
  end
79
88
 
80
89
  select_theme(root)