shopify-cli 2.11.1 → 2.13.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/shopify.yml +2 -1
  3. data/.rubocop.yml +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +32 -0
  6. data/Gemfile.lock +14 -14
  7. data/bin/shopify +17 -4
  8. data/dev.yml +1 -1
  9. data/docs/users/installation.md +1 -44
  10. data/ext/javy/hashes/javy-arm-macos-v0.2.0.gz.sha256 +1 -0
  11. data/ext/javy/hashes/javy-x86_64-linux-v0.2.0.gz.sha256 +1 -0
  12. data/ext/javy/hashes/javy-x86_64-macos-v0.2.0.gz.sha256 +1 -0
  13. data/ext/javy/hashes/javy-x86_64-windows-v0.2.0.gz.sha256 +1 -0
  14. data/ext/javy/version +1 -1
  15. data/lib/project_types/extension/commands/build.rb +0 -3
  16. data/lib/project_types/extension/commands/check.rb +0 -1
  17. data/lib/project_types/extension/commands/create.rb +0 -1
  18. data/lib/project_types/extension/commands/push.rb +0 -1
  19. data/lib/project_types/extension/commands/serve.rb +0 -1
  20. data/lib/project_types/extension/features/argo_setup_steps.rb +4 -6
  21. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  22. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +7 -1
  23. data/lib/project_types/extension/tasks/configure_features.rb +15 -2
  24. data/lib/project_types/extension/tasks/convert_server_config.rb +2 -1
  25. data/lib/project_types/script/cli.rb +2 -0
  26. data/lib/project_types/script/commands/create.rb +5 -5
  27. data/lib/project_types/script/commands/push.rb +4 -6
  28. data/lib/project_types/script/config/extension_points.yml +0 -4
  29. data/lib/project_types/script/errors.rb +1 -1
  30. data/lib/project_types/script/forms/create.rb +7 -20
  31. data/lib/project_types/script/layers/application/build_script.rb +22 -24
  32. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  33. data/lib/project_types/script/layers/application/create_script.rb +9 -10
  34. data/lib/project_types/script/layers/application/project_dependencies.rb +12 -14
  35. data/lib/project_types/script/layers/application/push_script.rb +2 -0
  36. data/lib/project_types/script/layers/domain/errors.rb +3 -3
  37. data/lib/project_types/script/layers/domain/push_package.rb +6 -0
  38. data/lib/project_types/script/layers/domain/script_config.rb +2 -4
  39. data/lib/project_types/script/layers/domain/script_project.rb +3 -2
  40. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  41. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +20 -9
  42. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +29 -18
  43. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -15
  44. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  45. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +22 -10
  46. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +30 -19
  47. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -3
  48. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +4 -0
  49. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +13 -25
  50. data/lib/project_types/script/layers/infrastructure/script_service.rb +4 -2
  51. data/lib/project_types/script/loaders/project.rb +8 -7
  52. data/lib/project_types/script/messages/messages.rb +20 -19
  53. data/lib/project_types/script/ui/error_handler.rb +17 -4
  54. data/lib/project_types/script/ui/strict_spinner.rb +4 -6
  55. data/lib/project_types/theme/cli.rb +2 -0
  56. data/lib/project_types/theme/commands/common/root_helper.rb +71 -0
  57. data/lib/project_types/theme/commands/init.rb +2 -0
  58. data/lib/project_types/theme/commands/list.rb +34 -0
  59. data/lib/project_types/theme/commands/open.rb +65 -0
  60. data/lib/project_types/theme/commands/pull.rb +20 -10
  61. data/lib/project_types/theme/commands/push.rb +18 -12
  62. data/lib/project_types/theme/commands/serve.rb +6 -2
  63. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  64. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  65. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  66. data/lib/project_types/theme/forms/select.rb +11 -39
  67. data/lib/project_types/theme/messages/messages.rb +36 -7
  68. data/lib/project_types/theme/presenters/theme_presenter.rb +48 -0
  69. data/lib/project_types/theme/presenters/themes_presenter.rb +32 -0
  70. data/lib/shopify_cli/api.rb +1 -1
  71. data/lib/shopify_cli/command.rb +3 -1
  72. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  73. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  74. data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
  75. data/lib/shopify_cli/commands/app/deploy.rb +1 -0
  76. data/lib/shopify_cli/constants.rb +3 -3
  77. data/lib/shopify_cli/context.rb +11 -13
  78. data/lib/shopify_cli/environment.rb +6 -0
  79. data/lib/shopify_cli/git.rb +9 -1
  80. data/lib/shopify_cli/messages/messages.rb +23 -3
  81. data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
  82. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  83. data/lib/shopify_cli/tasks.rb +1 -0
  84. data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
  85. data/lib/shopify_cli/theme/dev_server.rb +1 -3
  86. data/lib/shopify_cli/theme/development_theme.rb +11 -0
  87. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  88. data/lib/shopify_cli/theme/syncer.rb +20 -11
  89. data/lib/shopify_cli/theme/theme.rb +0 -4
  90. data/lib/shopify_cli/version.rb +1 -1
  91. data/shopify-dev +9 -11
  92. metadata +16 -2
@@ -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, :title
7
7
 
8
8
  def ask
9
- self.name = valid_name
9
+ self.title = valid_name
10
10
  self.extension_point ||= ask_extension_point
11
- self.language = ask_language
12
11
  end
13
12
 
14
13
  private
@@ -20,26 +19,14 @@ module Script
20
19
  )
21
20
  end
22
21
 
23
- def ask_name
24
- CLI::UI::Prompt.ask(@ctx.message("script.forms.create.script_name"))
22
+ def ask_title
23
+ CLI::UI::Prompt.ask(@ctx.message("script.forms.create.script_title"))
25
24
  end
26
25
 
27
26
  def valid_name
28
- n = (name || ask_name).downcase.gsub(" ", "_")
29
- return n if n.match?(/^[0-9A-Za-z_-]*$/)
30
- raise Errors::InvalidScriptNameError
31
- 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
- )
27
+ normalized_title = (title || ask_title).downcase.gsub(" ", "_")
28
+ return normalized_title if normalized_title.match?(/^[0-9A-Za-z_-]*$/)
29
+ raise Errors::InvalidScriptTitleError
43
30
  end
44
31
  end
45
32
  end
@@ -7,32 +7,30 @@ module Script
7
7
  class << self
8
8
  def call(ctx:, task_runner:, script_project:, library:)
9
9
  CLI::UI::Frame.open(ctx.message("script.application.building")) do
10
- begin
11
- UI::StrictSpinner.spin(ctx.message("script.application.building_script")) do |spinner|
12
- script_content = task_runner.build
13
- metadata_file_location = task_runner.metadata_file_location
14
- metadata = Infrastructure::MetadataRepository.new(ctx: ctx).get_metadata(metadata_file_location)
10
+ UI::StrictSpinner.spin(ctx.message("script.application.building_script")) do |spinner|
11
+ script_content = task_runner.build
12
+ metadata_file_location = task_runner.metadata_file_location
13
+ metadata = Infrastructure::MetadataRepository.new(ctx: ctx).get_metadata(metadata_file_location)
15
14
 
16
- Infrastructure::PushPackageRepository.new(ctx: ctx).create_push_package(
17
- script_project: script_project,
18
- script_content: script_content,
19
- metadata: metadata,
20
- library: library,
21
- )
22
- spinner.update_title(ctx.message("script.application.built"))
23
- end
24
- rescue StandardError => e
25
- CLI::UI::Frame.with_frame_color_override(:red) do
26
- ctx.puts("\n{{red:#{e.message}}}")
27
- end
28
- errors = [
29
- Infrastructure::Errors::BuildScriptNotFoundError,
30
- Infrastructure::Errors::WebAssemblyBinaryNotFoundError,
31
- ]
32
-
33
- raise Infrastructure::Errors::BuildError unless errors.any? { |err| e.is_a?(err) }
34
- raise
15
+ Infrastructure::PushPackageRepository.new(ctx: ctx).create_push_package(
16
+ script_project: script_project,
17
+ script_content: script_content,
18
+ metadata: metadata,
19
+ library: library,
20
+ )
21
+ spinner.update_title(ctx.message("script.application.built"))
22
+ end
23
+ rescue StandardError => e
24
+ CLI::UI::Frame.with_frame_color_override(:red) do
25
+ ctx.puts("\n{{red:#{e.message}}}")
35
26
  end
27
+ errors = [
28
+ Infrastructure::Errors::BuildScriptNotFoundError,
29
+ Infrastructure::Errors::WebAssemblyBinaryNotFoundError,
30
+ ]
31
+
32
+ raise Infrastructure::Errors::BuildError unless errors.any? { |err| e.is_a?(err) }
33
+ raise
36
34
  end
37
35
  end
38
36
  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
@@ -7,17 +7,17 @@ module Script
7
7
  module Application
8
8
  class CreateScript
9
9
  class << self
10
- def call(ctx:, language:, sparse_checkout_branch:, script_name:, extension_point_type:)
10
+ def call(ctx:, language:, sparse_checkout_branch:, title:, extension_point_type:)
11
11
  script_project_repo = Infrastructure::ScriptProjectRepository.new(
12
12
  ctx: ctx,
13
- directory: script_name,
13
+ directory: title,
14
14
  initial_directory: ctx.root
15
15
  )
16
16
 
17
17
  in_new_directory_context(script_project_repo) do
18
18
  extension_point = ExtensionPoints.get(type: extension_point_type)
19
19
  project = script_project_repo.create(
20
- script_name: script_name,
20
+ title: title,
21
21
  extension_point_type: extension_point_type,
22
22
  language: language
23
23
  )
@@ -31,35 +31,34 @@ module Script
31
31
  ctx: ctx,
32
32
  language: language,
33
33
  type: type,
34
- project_name: script_name,
34
+ project_name: title,
35
35
  path_to_project: project.id,
36
36
  sparse_checkout_repo: sparse_checkout_repo,
37
37
  sparse_checkout_branch: sparse_checkout_branch,
38
38
  sparse_checkout_set_path: "#{domain}/#{language}/#{type}/default"
39
39
  )
40
40
 
41
- install_dependencies(ctx, language, script_name, project_creator)
42
- script_project_repo.update_script_config(title: script_name)
41
+ install_dependencies(ctx, language, title, project_creator)
43
42
  project
44
43
  end
45
44
  end
46
45
 
47
46
  private
48
47
 
49
- def install_dependencies(ctx, language, script_name, project_creator)
48
+ def install_dependencies(ctx, language, title, project_creator)
50
49
  task_runner = Infrastructure::Languages::TaskRunner.for(ctx, language)
51
50
  CLI::UI::Frame.open(ctx.message(
52
51
  "core.git.pulling_from_to",
53
52
  project_creator.sparse_checkout_repo,
54
- script_name,
53
+ title,
55
54
  )) do
56
55
  UI::StrictSpinner.spin(ctx.message(
57
56
  "core.git.pulling",
58
57
  project_creator.sparse_checkout_repo,
59
- script_name,
58
+ title,
60
59
  )) do |spinner|
61
60
  project_creator.setup_dependencies
62
- spinner.update_title(ctx.message("core.git.pulled", script_name))
61
+ spinner.update_title(ctx.message("core.git.pulled", title))
63
62
  end
64
63
  end
65
64
  ProjectDependencies.install(ctx: ctx, task_runner: task_runner)
@@ -4,22 +4,20 @@ module Script
4
4
  class ProjectDependencies
5
5
  def self.install(ctx:, task_runner:)
6
6
  CLI::UI::Frame.open(ctx.message("script.project_deps.checking")) do
7
- begin
8
- if task_runner.dependencies_installed?
9
- ctx.puts(ctx.message("script.project_deps.none_required"))
10
- else
11
- UI::StrictSpinner.spin(ctx.message("script.project_deps.installing")) do |spinner|
12
- task_runner.install_dependencies
13
- spinner.update_title(ctx.message("script.project_deps.installed"))
14
- end
7
+ if task_runner.dependencies_installed?
8
+ ctx.puts(ctx.message("script.project_deps.none_required"))
9
+ else
10
+ UI::StrictSpinner.spin(ctx.message("script.project_deps.installing")) do |spinner|
11
+ task_runner.install_dependencies
12
+ spinner.update_title(ctx.message("script.project_deps.installed"))
15
13
  end
16
- true
17
- rescue Infrastructure::Errors::DependencyInstallError => e
18
- CLI::UI::Frame.with_frame_color_override(:red) do
19
- ctx.puts("\n#{e.message}")
20
- end
21
- raise e
22
14
  end
15
+ true
16
+ rescue Infrastructure::Errors::DependencyInstallError => e
17
+ CLI::UI::Frame.with_frame_color_override(:red) do
18
+ ctx.puts("\n#{e.message}")
19
+ end
20
+ raise e
23
21
  end
24
22
  end
25
23
  end
@@ -48,6 +48,8 @@ module Script
48
48
  uuid = script_service.set_app_script(
49
49
  uuid: package.uuid,
50
50
  extension_point_type: package.extension_point_type,
51
+ title: package.title,
52
+ description: package.description,
51
53
  force: force,
52
54
  metadata: package.metadata,
53
55
  script_config: package.script_config,
@@ -24,10 +24,10 @@ module Script
24
24
  end
25
25
 
26
26
  class ScriptNotFoundError < ScriptProjectError
27
- attr_reader :script_name, :extension_point_type
28
- def initialize(extension_point_type, script_name)
27
+ attr_reader :title, :extension_point_type
28
+ def initialize(extension_point_type, title)
29
29
  super()
30
- @script_name = script_name
30
+ @title = title
31
31
  @extension_point_type = extension_point_type
32
32
  end
33
33
  end
@@ -7,6 +7,8 @@ module Script
7
7
  attr_reader :id,
8
8
  :uuid,
9
9
  :extension_point_type,
10
+ :title,
11
+ :description,
10
12
  :script_config,
11
13
  :script_content,
12
14
  :metadata,
@@ -16,6 +18,8 @@ module Script
16
18
  id:,
17
19
  uuid:,
18
20
  extension_point_type:,
21
+ title:,
22
+ description:,
19
23
  script_content:,
20
24
  metadata:,
21
25
  script_config:,
@@ -24,6 +28,8 @@ module Script
24
28
  @id = id
25
29
  @uuid = uuid
26
30
  @extension_point_type = extension_point_type
31
+ @title = title
32
+ @description = description
27
33
  @script_content = script_content
28
34
  @metadata = metadata
29
35
  @script_config = script_config
@@ -4,17 +4,15 @@ module Script
4
4
  module Layers
5
5
  module Domain
6
6
  class ScriptConfig
7
- attr_reader :content, :version, :title, :description, :configuration_ui, :configuration, :filename
7
+ attr_reader :content, :version, :configuration_ui, :configuration, :filename
8
8
 
9
- REQUIRED_FIELDS = %w(version title)
9
+ REQUIRED_FIELDS = %w(version)
10
10
 
11
11
  def initialize(content:, filename:)
12
12
  @filename = filename
13
13
  validate_content!(content)
14
14
  @content = content
15
15
  @version = @content["version"].to_s
16
- @title = @content["title"]
17
- @description = @content["description"]
18
16
  @configuration_ui = @content.fetch("configurationUi", true)
19
17
  @configuration = @content["configuration"]
20
18
  end
@@ -12,7 +12,8 @@ module Script
12
12
  property :env, accepts: ShopifyCLI::Resources::EnvFile
13
13
 
14
14
  property! :extension_point_type, accepts: String
15
- property! :script_name, accepts: String
15
+ property! :title, accepts: String
16
+ property :description, accepts: String
16
17
  property! :language, accepts: String
17
18
 
18
19
  property :script_config, accepts: ScriptConfig
@@ -22,7 +23,7 @@ module Script
22
23
  super
23
24
 
24
25
  ShopifyCLI::Core::Monorail.metadata = {
25
- "script_name" => script_name,
26
+ "script_name" => title,
26
27
  "extension_point_type" => extension_point_type,
27
28
  "language" => language,
28
29
  }
@@ -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,29 @@ 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"
8
+ def setup_dependencies
9
+ task_runner = Infrastructure::Languages::AssemblyScriptTaskRunner.new(ctx)
10
+ task_runner.set_npm_config
11
+ super
11
12
 
12
- def self.config_file
13
- "package.json"
13
+ update_package_json_name
14
14
  end
15
15
 
16
- def setup_dependencies
17
- super
18
- command_runner.call(NPM_SET_REGISTRY_COMMAND)
19
- command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
16
+ private
17
+
18
+ def update_package_json_name
19
+ file_content = ctx.read("package.json")
20
+ hash = file_content_to_hash(file_content)
21
+ hash["name"] = project_name
22
+ ctx.write("package.json", hash_to_file_content(hash))
23
+ end
24
+
25
+ def file_content_to_hash(content)
26
+ JSON.parse(content)
27
+ end
28
+
29
+ def hash_to_file_content(hash)
30
+ JSON.pretty_generate(hash)
20
31
  end
21
32
  end
22
33
  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!
@@ -43,15 +43,10 @@ module Script
43
43
  )
44
44
  end
45
45
 
46
- def self.config_file
47
- raise NotImplementedError
48
- end
49
-
50
46
  # the sparse checkout process is common to all script types
51
47
  def setup_dependencies
52
48
  setup_sparse_checkout
53
49
  clean
54
- update_project_name(File.join(path_to_project, self.class.config_file))
55
50
  end
56
51
 
57
52
  private
@@ -72,16 +67,6 @@ module Script
72
67
  ctx.rm_rf(".git")
73
68
  end
74
69
 
75
- def update_project_name(config_file)
76
- raise Errors::ProjectConfigNotFoundError unless File.exist?(config_file)
77
- upstream_name = "#{type.gsub("_", "-")}-default"
78
- contents = File.read(config_file)
79
-
80
- raise Errors::InvalidProjectConfigError unless contents.include?(upstream_name)
81
- new_contents = contents.gsub(upstream_name, project_name)
82
- File.write(config_file, new_contents)
83
- end
84
-
85
70
  def command_runner
86
71
  @command_runner ||= CommandRunner.new(ctx: ctx)
87
72
  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,11 @@ 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
- def self.config_file
13
- "package.json"
14
- end
15
-
16
8
  def setup_dependencies
9
+ task_runner = Infrastructure::Languages::TypeScriptTaskRunner.new(ctx)
10
+ task_runner.set_npm_config
11
+
17
12
  super
18
- command_runner.call(NPM_SET_REGISTRY_COMMAND)
19
- command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
20
13
 
21
14
  if ctx.file_exist?("yarn.lock")
22
15
  ctx.rm("yarn.lock")
@@ -25,6 +18,25 @@ module Script
25
18
  if ctx.file_exist?("package-lock.json")
26
19
  ctx.rm("package-lock.json")
27
20
  end
21
+
22
+ update_package_json_name
23
+ end
24
+
25
+ private
26
+
27
+ def update_package_json_name
28
+ file_content = ctx.read("package.json")
29
+ hash = file_content_to_hash(file_content)
30
+ hash["name"] = project_name
31
+ ctx.write("package.json", hash_to_file_content(hash))
32
+ end
33
+
34
+ def file_content_to_hash(content)
35
+ JSON.parse(content)
36
+ end
37
+
38
+ def hash_to_file_content(hash)
39
+ JSON.pretty_generate(hash)
28
40
  end
29
41
  end
30
42
  end
@@ -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!
@@ -5,9 +5,6 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
7
  class WasmProjectCreator < ProjectCreator
8
- def self.config_file
9
- "script.config.yml"
10
- end
11
8
  end
12
9
  end
13
10
  end