shopify-cli 2.11.2 → 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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/Gemfile.lock +1 -1
  4. data/bin/shopify +8 -4
  5. data/docs/users/installation.md +1 -44
  6. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  7. data/lib/project_types/script/cli.rb +2 -0
  8. data/lib/project_types/script/commands/create.rb +2 -2
  9. data/lib/project_types/script/commands/push.rb +4 -6
  10. data/lib/project_types/script/config/extension_points.yml +0 -4
  11. data/lib/project_types/script/forms/create.rb +1 -14
  12. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  13. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  14. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +2 -6
  15. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +29 -18
  16. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  17. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +3 -6
  18. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +30 -19
  19. data/lib/project_types/script/loaders/project.rb +8 -7
  20. data/lib/project_types/script/messages/messages.rb +13 -12
  21. data/lib/project_types/script/ui/error_handler.rb +13 -0
  22. data/lib/project_types/theme/commands/common/root_helper.rb +65 -0
  23. data/lib/project_types/theme/commands/init.rb +2 -0
  24. data/lib/project_types/theme/commands/pull.rb +16 -8
  25. data/lib/project_types/theme/commands/push.rb +15 -7
  26. data/lib/project_types/theme/commands/serve.rb +6 -2
  27. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  28. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  29. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  30. data/lib/project_types/theme/messages/messages.rb +5 -5
  31. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  32. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  33. data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
  34. data/lib/shopify_cli/commands/app/deploy.rb +1 -0
  35. data/lib/shopify_cli/environment.rb +6 -0
  36. data/lib/shopify_cli/git.rb +9 -1
  37. data/lib/shopify_cli/messages/messages.rb +21 -1
  38. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  39. data/lib/shopify_cli/tasks.rb +1 -0
  40. data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
  41. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  42. data/lib/shopify_cli/theme/syncer.rb +13 -4
  43. data/lib/shopify_cli/version.rb +1 -1
  44. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a98682c26b1f02a7a24648c23bb987ceab5a70939e031957e80785c1068d849
4
- data.tar.gz: bfddf7b2f92004d654a66fa8746619d724789c65da7b236e413857fd8705c65f
3
+ metadata.gz: ee0786bedc79f0e7fb3646adc5161faeb0fb2f4f15c6147394782db33236f425
4
+ data.tar.gz: 13d805804c7764151bfdd80747e879bd480ffcf85ba91fa280b776b93f784855
5
5
  SHA512:
6
- metadata.gz: 33830ff754743290463c4744cd6e76c37fcf72f6119bcc8d43c9d5a752c3acaab7e9f0e9365f0b0083bae8be2b930d1c1a35affc508210e72998abc11b483d3b
7
- data.tar.gz: e89c4a64ac97093c8973ccd482ca76b11f3852e29eb124904301c72f930cdce1774fabd1c1d1a3bdb775e065a7e27364a6bb7bbbdcf631c0587ee5e473f793b1
6
+ metadata.gz: 8347252541e765517a6bc485cc7bc1b811771a55610543549d676d5321944185e9a446bed4c8a3909f21f336fd56ae76b91ede2e3731f30ea66c0916d1a3e591
7
+ data.tar.gz: 930367d81adf351b66aa7365daaf9f4406067db80a4959409e68ddd5ab7041ed10db4f983f81d1af3d8f2457b8d79543cbf33c927e69bfbde027a31da4b166a8
data/CHANGELOG.md CHANGED
@@ -2,11 +2,27 @@ From version 2.6.0, the sections in this file adhere to the [keep a changelog](h
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## Version 2.12.0
6
+ ### Added
7
+ * [#1866](https://github.com/Shopify/shopify-cli/pull/1866): Enforce git dependency
8
+ * [#2009](https://github.com/Shopify/shopify-cli/pull/2009): Add localization support for Checkout Extensions
9
+ * [#2060](https://github.com/Shopify/shopify-cli/pull/2060): Improve CLI error messages for token-based auth (scripts)
10
+ * [#2076](https://github.com/Shopify/shopify-cli/pull/2076): Release Wasm Script Projects
11
+ * [#2051](https://github.com/Shopify/shopify-cli/pull/2051): Update `theme serve` to accept `root` argument
12
+ * [#2025](https://github.com/Shopify/shopify-cli/pull/2025): Improve `theme pull`/`push` help messages to indicate multiple `--only`/`--ignore` flags are allowed.
13
+
14
+ ### Fixed
15
+ * [#2030](https://github.com/Shopify/shopify-cli/pull/2030): Fix Theme::Syncer handling of file deletions in `download_file!`
16
+ * [#2071](https://github.com/Shopify/shopify-cli/pull/2071): Fix `theme pull` error message when dev theme doesn't exist
17
+ * [#2066](https://github.com/Shopify/shopify-cli/pull/2066): Improve `--only`/`--ignore` parameters on Theme `pull`/`push` commands to work without quotes
18
+ * [#2078](https://github.com/Shopify/shopify-cli/pull/2078): Fix errors on section rendering caused by CORS issues
19
+
5
20
  ## Version 2.11.2
6
21
  ### Fixed
7
22
  * [#2047](https://github.com/Shopify/shopify-cli/pull/2047): Fix the Homebrew installation
8
23
  * [#2019](https://github.com/Shopify/shopify-cli/pull/2019): Provide helpful link when nokogiri fails to load
9
24
  * [#2055](https://github.com/Shopify/shopify-cli/pull/2055): Remove unneeded Node requirements
25
+ * [#2020](https://github.com/Shopify/shopify-cli/pull/2020): Fix `theme pull` so that correct dev theme is used with `-d` option
10
26
 
11
27
  ## Version 2.11.1
12
28
  ### Fixed
@@ -18,7 +34,6 @@ From version 2.6.0, the sections in this file adhere to the [keep a changelog](h
18
34
  ## Version 2.11.0
19
35
  ### Fixed
20
36
  * [#2005](https://github.com/Shopify/shopify-cli/pull/2005): Fix PHP app serve on Windows environments
21
- * [#2020](https://github.com/Shopify/shopify-cli/pull/2020): Fix `theme pull` so that correct dev theme is used with `-d` option
22
37
 
23
38
  ### Added
24
39
  * [#1998](https://github.com/Shopify/shopify-cli/pull/1998): Add support for Rails 7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-cli (2.11.2)
4
+ shopify-cli (2.12.0)
5
5
  bugsnag (~> 6.22)
6
6
  listen (~> 3.7.0)
7
7
  theme-check (~> 1.9.0)
data/bin/shopify CHANGED
@@ -16,10 +16,14 @@ module Kernel
16
16
  raise if name == "#{RUBY_VERSION.split(".")[0, 2].join(".")}/ffi_c"
17
17
  # Special case for nokogiri, which might install the wrong architecture
18
18
  if name == "nokogiri/nokogiri"
19
- STDERR.puts "[Note] Nokogiri is failing to load, which most likely means " \
20
- "it is installed with the wrong architecture for your system. This link " \
21
- "has instructions for how to install the correct version for your system: " \
22
- "https://nokogiri.org/tutorials/installing_nokogiri.html"
19
+ STDERR.puts(<<~MESSAGE)
20
+ The Nokogiri gem is failing to load, due to an installation or architecture issue.
21
+
22
+ To fix this, reinstall Nokogiri.
23
+
24
+ • Installation guide: https://nokogiri.org/tutorials/installing_nokogiri.html
25
+
26
+ MESSAGE
23
27
  STDERR.puts e.full_message
24
28
  exit(1)
25
29
  end
@@ -1,46 +1,3 @@
1
1
  # Installation
2
2
 
3
- ## Installation through RubyGems
4
-
5
- The easiest method to install the Shopify CLI is through RubyGems:
6
-
7
- ```shell
8
- $ gem install shopify-cli
9
- ```
10
-
11
- ## Installation for macOS Users
12
-
13
- - Make sure you have [Homebrew](https://brew.sh/) installed
14
- - Open your terminal app
15
- - Run `brew tap shopify/shopify`
16
- - Run `brew install shopify-cli`
17
- - After the installation is completed, run `shopify version`, if this outputs a version number you've successfully installed the CLI.
18
-
19
- ### To upgrade Shopify CLI
20
-
21
- #### Homebrew (Mac OS)
22
-
23
- ```shell
24
- $ brew update
25
- $ brew upgrade shopify-cli
26
- ```
27
-
28
- ## Installation for Debian and Ubuntu users through `apt`
29
-
30
- 1.- Download the latest `.deb` binary for Shopify CLI from the releases page.
31
-
32
- 2.- Install the downloaded file and make sure to replace /path/to/download/shopify-cli-x.y.z.deb with the path to your file's location:
33
-
34
- ```shell
35
- $ sudo apt install /path/to/downloaded/shopify-cli-x.y.z.deb
36
- ```
37
-
38
- ## Installation for CentOS 8+, Fedora, Red Hat, and SUSE users through `yum`
39
-
40
- 1.- Download the latest .rpm file for Shopify App CLI from the releases page.
41
-
42
- 2.- Install the downloaded file and make sure to replace /path/to/downloaded/shopify-cli-x.y.x.rpm with the path to your file's location:
43
-
44
- ```shell
45
- $ sudo yum install /path/to/downloaded/shopify-cli-x.y.x.rpm
46
- ```
3
+ The installation guide can be found here: https://shopify.dev/apps/tools/cli/installation
@@ -4,12 +4,24 @@ module Extension
4
4
  module Models
5
5
  module SpecificationHandlers
6
6
  class CheckoutUiExtension < Default
7
+ L10N_ERROR_PREFIX = "core.extension.push.checkout_ui_extension.localization.error"
8
+ L10N_FILE_SIZE_LIMIT = 16 * 1024 # 16kb
9
+ L10N_BUNDLE_SIZE_LIMIT = 256 * 1024 # 256kb
10
+ LOCALE_CODE_FORMAT = %r{
11
+ \A
12
+ (?<language>[a-zA-Z]{2,3}) # Language tag
13
+ (?:
14
+ -
15
+ (?<region>[a-zA-Z]{2}) # Optional region subtag
16
+ )?
17
+ \z}x
7
18
  PERMITTED_CONFIG_KEYS = [:extension_points, :metafields, :name]
8
19
 
9
20
  def config(context)
10
21
  {
11
22
  **Features::ArgoConfig.parse_yaml(context, PERMITTED_CONFIG_KEYS),
12
23
  **argo.config(context, include_renderer_version: false),
24
+ **localization(context),
13
25
  }
14
26
  end
15
27
 
@@ -22,6 +34,108 @@ module Extension
22
34
  return unless product
23
35
  format("/cart/%<variant_id>d:%<quantity>d", variant_id: product.variant_id, quantity: 1)
24
36
  end
37
+
38
+ private
39
+
40
+ def localization(context)
41
+ Dir.chdir(context.root) do
42
+ locale_filenames = Dir["locales/*"].select { |filename| valid_l10n_file?(filename) }
43
+ # Localization is optional
44
+ return {} if locale_filenames.empty?
45
+
46
+ validate_total_size(locale_filenames)
47
+ default_locale = single_default_locale(locale_filenames)
48
+
49
+ locale_filenames.map do |filename|
50
+ locale = basename_for_locale_filename(filename)
51
+ [locale.to_sym, Base64.strict_encode64(File.read(filename, mode: "rt", encoding: "UTF-8").strip)]
52
+ end
53
+ .yield_self do |encoded_files_by_locale|
54
+ {
55
+ localization: {
56
+ default_locale: default_locale,
57
+ translations: encoded_files_by_locale.to_h,
58
+ },
59
+ }
60
+ end
61
+ end
62
+ end
63
+
64
+ def validate_total_size(locale_filenames)
65
+ total_size = locale_filenames.sum { |filename| File.size(filename) }
66
+ if total_size > L10N_BUNDLE_SIZE_LIMIT
67
+ raise(
68
+ ShopifyCLI::Abort,
69
+ ShopifyCLI::Context.message(
70
+ "#{L10N_ERROR_PREFIX}.bundle_too_large",
71
+ CLI::Kit::Util.to_filesize(L10N_BUNDLE_SIZE_LIMIT)
72
+ )
73
+ )
74
+ end
75
+ end
76
+
77
+ def single_default_locale(locale_filenames)
78
+ default_locale_matches = locale_filenames.grep(/default/)
79
+ if default_locale_matches.size != 1
80
+ raise(ShopifyCLI::Abort, ShopifyCLI::Context.message("#{L10N_ERROR_PREFIX}.single_default_locale"))
81
+ end
82
+ basename_for_locale_filename(default_locale_matches.first)
83
+ end
84
+
85
+ def valid_l10n_file?(filename)
86
+ return false unless File.file?(filename)
87
+ return false unless File.dirname(filename) == "locales"
88
+
89
+ validate_file_extension(filename)
90
+ validate_file_locale_code(filename)
91
+ validate_file_size(filename)
92
+ validate_file_not_empty(filename)
93
+
94
+ true
95
+ end
96
+
97
+ def validate_file_extension(filename)
98
+ if File.extname(filename) != ".json"
99
+ raise(
100
+ ShopifyCLI::Abort, ShopifyCLI::Context.message("#{L10N_ERROR_PREFIX}.invalid_file_extension", filename)
101
+ )
102
+ end
103
+ end
104
+
105
+ def validate_file_locale_code(filename)
106
+ unless valid_locale_code?(basename_for_locale_filename(filename))
107
+ raise(
108
+ ShopifyCLI::Abort, ShopifyCLI::Context.message("#{L10N_ERROR_PREFIX}.invalid_locale_code", filename)
109
+ )
110
+ end
111
+ end
112
+
113
+ def validate_file_size(filename)
114
+ if File.size(filename) > L10N_FILE_SIZE_LIMIT
115
+ raise(
116
+ ShopifyCLI::Abort,
117
+ ShopifyCLI::Context.message(
118
+ "#{L10N_ERROR_PREFIX}.file_too_large",
119
+ filename,
120
+ CLI::Kit::Util.to_filesize(L10N_FILE_SIZE_LIMIT)
121
+ )
122
+ )
123
+ end
124
+ end
125
+
126
+ def validate_file_not_empty(filename)
127
+ if File.zero?(filename)
128
+ raise(ShopifyCLI::Abort, ShopifyCLI::Context.message("#{L10N_ERROR_PREFIX}.file_empty", filename))
129
+ end
130
+ end
131
+
132
+ def valid_locale_code?(locale_code)
133
+ LOCALE_CODE_FORMAT.match?(locale_code)
134
+ end
135
+
136
+ def basename_for_locale_filename(filename)
137
+ File.basename(File.basename(filename, ".json"), ".default")
138
+ end
25
139
  end
26
140
  end
27
141
  end
@@ -78,6 +78,8 @@ module Script
78
78
  Project.project_filepath("layers/infrastructure/languages/wasm_project_creator.rb")
79
79
  autoload :WasmTaskRunner,
80
80
  Project.project_filepath("layers/infrastructure/languages/wasm_task_runner.rb")
81
+ autoload :ToolVersionChecker,
82
+ Project.project_filepath("layers/infrastructure/languages/tool_version_checker.rb")
81
83
  end
82
84
 
83
85
  module ApiClients
@@ -18,13 +18,13 @@ module Script
18
18
  form = Forms::Create.ask(@ctx, args, options.flags)
19
19
  return @ctx.puts(self.class.help) if form.nil?
20
20
 
21
- unless !form.name.empty? && form.extension_point && form.language
21
+ unless !form.name.empty? && form.extension_point
22
22
  return @ctx.puts(self.class.help)
23
23
  end
24
24
 
25
25
  project = Layers::Application::CreateScript.call(
26
26
  ctx: @ctx,
27
- language: form.language,
27
+ language: options.flags[:language]&.downcase || "wasm",
28
28
  sparse_checkout_branch: options.flags[:branch] || "master",
29
29
  script_name: form.name,
30
30
  extension_point_type: form.extension_point,
@@ -5,10 +5,6 @@ module Script
5
5
  class Push < ShopifyCLI::Command::SubCommand
6
6
  prerequisite_task ensure_project_type: :script
7
7
 
8
- recommend_node(
9
- from: ::Script::Layers::Infrastructure::Languages::TypeScriptProjectCreator::MIN_NODE_VERSION,
10
- to: ShopifyCLI::Constants::SupportedVersions::Node::TO
11
- )
12
8
  recommend_default_ruby_range
13
9
 
14
10
  options do |parser, flags|
@@ -26,7 +22,7 @@ module Script
26
22
  push(project: project)
27
23
  rescue StandardError => e
28
24
  UI::ErrorHandler.pretty_print_and_raise(e,
29
- failed_op: @ctx.message("script.push.error.operation_failed_no_api_key"))
25
+ failed_op: @ctx.message("script.push.error.operation_failed"))
30
26
  end
31
27
 
32
28
  def push(project:)
@@ -38,7 +34,9 @@ module Script
38
34
  Layers::Application::PushScript.call(ctx: @ctx, force: force, project: project)
39
35
  @ctx.puts(@ctx.message("script.push.script_pushed", api_key: api_key))
40
36
  else
41
- raise ShopifyCLI::Abort, @ctx.message("script.push.error.operation_failed_no_uuid")
37
+ message = @ctx.message("script.error.missing_push_options_ci", "--uuid")
38
+ message += @ctx.message("script.error.missing_push_options_ci_solution", ShopifyCLI::TOOL_NAME)
39
+ raise ShopifyCLI::Abort, message
42
40
  end
43
41
  end
44
42
 
@@ -9,7 +9,6 @@ payment_methods:
9
9
  package: "@shopify/scripts-checkout-apis"
10
10
  repo: "https://github.com/Shopify/scripts-apis-examples"
11
11
  wasm:
12
- beta: true
13
12
  repo: "https://github.com/Shopify/scripts-apis-examples"
14
13
  shipping_methods:
15
14
  domain: 'checkout'
@@ -22,7 +21,6 @@ shipping_methods:
22
21
  package: "@shopify/scripts-checkout-apis"
23
22
  repo: "https://github.com/Shopify/scripts-apis-examples"
24
23
  wasm:
25
- beta: true
26
24
  repo: "https://github.com/Shopify/scripts-apis-examples"
27
25
  merchandise_discount_types:
28
26
  beta: true
@@ -33,7 +31,6 @@ merchandise_discount_types:
33
31
  package: "@shopify/scripts-discounts-apis"
34
32
  repo: "https://github.com/Shopify/scripts-apis-examples"
35
33
  wasm:
36
- beta: true
37
34
  repo: "https://github.com/Shopify/scripts-apis-examples"
38
35
  delivery_discount_types:
39
36
  beta: true
@@ -44,5 +41,4 @@ delivery_discount_types:
44
41
  package: "@shopify/scripts-discounts-apis"
45
42
  repo: "https://github.com/Shopify/scripts-apis-examples"
46
43
  wasm:
47
- beta: true
48
44
  repo: "https://github.com/Shopify/scripts-apis-examples"
@@ -3,12 +3,11 @@
3
3
  module Script
4
4
  module Forms
5
5
  class Create < ShopifyCLI::Form
6
- flag_arguments :extension_point, :name, :language
6
+ flag_arguments :extension_point, :name
7
7
 
8
8
  def ask
9
9
  self.name = valid_name
10
10
  self.extension_point ||= ask_extension_point
11
- self.language = ask_language
12
11
  end
13
12
 
14
13
  private
@@ -29,18 +28,6 @@ module Script
29
28
  return n if n.match?(/^[0-9A-Za-z_-]*$/)
30
29
  raise Errors::InvalidScriptNameError
31
30
  end
32
-
33
- def ask_language
34
- return language.downcase if language
35
-
36
- all_languages = Layers::Application::ExtensionPoints.languages(type: extension_point)
37
- return all_languages.first if all_languages.count == 1
38
-
39
- CLI::UI::Prompt.ask(
40
- ctx.message("script.forms.create.select_language"),
41
- options: all_languages
42
- )
43
- end
44
31
  end
45
32
  end
46
33
  end
@@ -55,9 +55,10 @@ module Script
55
55
  def handle_error(error, context:)
56
56
  properties_hash = { api_key: "SHOPIFY_API_KEY", secret: "SHOPIFY_API_SECRET" }
57
57
  missing_env_variables = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
58
+ message = context.message("script.error.missing_env_file_variables", missing_env_variables)
59
+ message += context.message("script.error.missing_env_file_variables_solution", ShopifyCLI::TOOL_NAME)
58
60
  raise ShopifyCLI::Abort,
59
- context.message("script.connect.error.missing_env_file_variables", missing_env_variables,
60
- ShopifyCLI::TOOL_NAME)
61
+ message
61
62
  end
62
63
 
63
64
  private
@@ -105,6 +105,17 @@ module Script
105
105
 
106
106
  class DependencyInstallError < ScriptProjectError; end
107
107
  class EmptyResponseError < ScriptProjectError; end
108
+
109
+ class InvalidEnvironmentError < ScriptProjectError
110
+ attr_reader :tool, :env_version, :minimum_version
111
+ def initialize(tool, env_version, minimum_version)
112
+ super()
113
+ @tool = tool
114
+ @env_version = env_version
115
+ @minimum_version = minimum_version
116
+ end
117
+ end
118
+
108
119
  class InvalidResponseError < ScriptProjectError; end
109
120
  class ForbiddenError < ScriptProjectError; end
110
121
  class InvalidContextError < ScriptProjectError; end
@@ -5,18 +5,14 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
7
  class AssemblyScriptProjectCreator < ProjectCreator
8
- MIN_NODE_VERSION = "14.5.0" # kept because task_runner uses this
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::AssemblyScriptTaskRunner.new(ctx)
14
+ task_runner.set_npm_config
17
15
  super
18
- command_runner.call(NPM_SET_REGISTRY_COMMAND)
19
- command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
20
16
  end
21
17
  end
22
18
  end
@@ -5,9 +5,15 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
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"
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"
11
17
 
12
18
  def build
13
19
  compile
@@ -15,10 +21,10 @@ module Script
15
21
  end
16
22
 
17
23
  def install_dependencies
18
- check_node_version!
24
+ run_cmd_with_env_check(NPM_INSTALL_COMMAND)
19
25
 
20
- output, status = ctx.capture2e("npm install --no-audit --no-optional --legacy-peer-deps --loglevel error")
21
- raise Errors::DependencyInstallError, output unless status.success?
26
+ rescue Errors::SystemCallFailureError => e
27
+ raise Errors::DependencyInstallError, e.out
22
28
  end
23
29
 
24
30
  def dependencies_installed?
@@ -31,14 +37,32 @@ module Script
31
37
  end
32
38
 
33
39
  def library_version(library_name)
34
- 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"))
35
41
  library_version_from_npm_list(output, library_name)
36
42
  rescue Errors::SystemCallFailureError => error
37
43
  library_version_from_npm_list_error_output(error, library_name)
38
44
  end
39
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
+
40
51
  private
41
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
+
42
66
  def library_version_from_npm_list_error_output(error, library_name)
43
67
  # npm list can return a failure status code, even when returning the correct data.
44
68
  # This causes the CommandRunner to throw a SystemCallFailure error that contains the data.
@@ -57,22 +81,9 @@ module Script
57
81
  end
58
82
  end
59
83
 
60
- def check_node_version!
61
- output, status = @ctx.capture2e("node", "--version")
62
- raise Errors::DependencyInstallError, output unless status.success?
63
-
64
- require "semantic/semantic"
65
- version = ::Semantic::Version.new(output[1..-1])
66
- unless version >= ::Semantic::Version.new(AssemblyScriptProjectCreator::MIN_NODE_VERSION)
67
- raise Errors::DependencyInstallError,
68
- "Node version must be >= v#{AssemblyScriptProjectCreator::MIN_NODE_VERSION}. "\
69
- "Current version: #{output.strip}."
70
- end
71
- end
72
-
73
84
  def compile
74
85
  check_compilation_dependencies!
75
- CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
86
+ run_cmd_with_env_check(SCRIPT_SDK_BUILD)
76
87
  end
77
88
 
78
89
  def check_compilation_dependencies!
@@ -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")
@@ -5,10 +5,16 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
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"
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"
12
18
 
13
19
  def build
14
20
  compile
@@ -16,10 +22,10 @@ module Script
16
22
  end
17
23
 
18
24
  def install_dependencies
19
- check_node_version!
25
+ run_cmd_with_env_check(NPM_INSTALL_COMMAND)
20
26
 
21
- output, status = ctx.capture2e("npm install --no-audit --no-optional --legacy-peer-deps --loglevel error")
22
- raise Errors::DependencyInstallError, output unless status.success?
27
+ rescue Errors::SystemCallFailureError => e
28
+ raise Errors::DependencyInstallError, e.out
23
29
  end
24
30
 
25
31
  def dependencies_installed?
@@ -32,14 +38,32 @@ module Script
32
38
  end
33
39
 
34
40
  def library_version(library_name)
35
- 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"))
36
42
  library_version_from_npm_list(output, library_name)
37
43
  rescue Errors::SystemCallFailureError => error
38
44
  library_version_from_npm_list_error_output(error, library_name)
39
45
  end
40
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
+
41
52
  private
42
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
+
43
67
  def library_version_from_npm_list_error_output(error, library_name)
44
68
  # npm list can return a failure status code, even when returning the correct data.
45
69
  # This causes the CommandRunner to throw a SystemCallFailure error that contains the data.
@@ -58,23 +82,10 @@ module Script
58
82
  end
59
83
  end
60
84
 
61
- def check_node_version!
62
- output, status = @ctx.capture2e("node", "--version")
63
- raise Errors::DependencyInstallError, output unless status.success?
64
-
65
- require "semantic/semantic"
66
- version = ::Semantic::Version.new(output[1..-1])
67
- unless version >= ::Semantic::Version.new(TypeScriptProjectCreator::MIN_NODE_VERSION)
68
- raise Errors::DependencyInstallError,
69
- "Node version must be >= v#{TypeScriptProjectCreator::MIN_NODE_VERSION}. "\
70
- "Current version: #{output.strip}."
71
- end
72
- end
73
-
74
85
  def compile
75
86
  check_compilation_dependencies!
76
- CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
77
- CommandRunner.new(ctx: ctx).call(GEN_METADATA)
87
+ run_cmd_with_env_check(SCRIPT_SDK_BUILD)
88
+ run_cmd_with_env_check(GEN_METADATA)
78
89
  end
79
90
 
80
91
  def check_compilation_dependencies!