shopify-cli 2.11.1 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
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