shopify-cli 2.5.0 → 2.6.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/Dockerfile +0 -2
- data/Gemfile.lock +22 -16
- data/Rakefile +7 -16
- data/bin/console +11 -0
- data/bin/shopify +15 -3
- data/dev.yml +3 -0
- data/ext/shopify-cli/extconf.rb +2 -0
- data/lib/project_types/extension/cli.rb +2 -0
- data/lib/project_types/extension/commands/build.rb +2 -1
- data/lib/project_types/extension/features/argo.rb +1 -1
- data/lib/project_types/extension/features/argo_serve.rb +1 -0
- data/lib/project_types/extension/models/development_server.rb +4 -0
- data/lib/project_types/extension/models/development_server_requirements.rb +1 -2
- data/lib/project_types/extension/models/specification_handlers/default.rb +4 -0
- data/lib/project_types/extension/tasks/converters/server_config_converter.rb +31 -0
- data/lib/project_types/extension/tasks/find_npm_packages.rb +2 -2
- data/lib/project_types/extension/tasks/load_server_config.rb +23 -0
- data/lib/project_types/extension/tasks/run_extension_command.rb +26 -10
- data/lib/project_types/node/commands/serve.rb +9 -1
- data/lib/project_types/node/messages/messages.rb +3 -0
- data/lib/project_types/script/cli.rb +4 -3
- data/lib/project_types/script/commands/create.rb +2 -0
- data/lib/project_types/script/config/extension_points.yml +30 -29
- data/lib/project_types/script/layers/application/create_script.rb +32 -12
- data/lib/project_types/script/layers/application/extension_points.rb +3 -3
- data/lib/project_types/script/layers/domain/extension_point.rb +13 -45
- data/lib/project_types/script/layers/infrastructure/api_clients/partners_proxy_api_client.rb +4 -2
- data/lib/project_types/script/layers/infrastructure/api_clients/script_service_api_client.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/errors.rb +5 -0
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +10 -90
- data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +76 -11
- data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +1 -1
- data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +33 -0
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +105 -0
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +1 -1
- data/lib/project_types/script/messages/messages.rb +4 -0
- data/lib/project_types/script/ui/error_handler.rb +8 -0
- data/lib/shopify_cli/command/app_sub_command.rb +16 -0
- data/lib/shopify_cli/constants.rb +33 -5
- data/lib/shopify_cli/core/executor.rb +5 -1
- data/lib/shopify_cli/environment.rb +35 -4
- data/lib/shopify_cli/exception_reporter/permission_controller.rb +54 -0
- data/lib/shopify_cli/exception_reporter.rb +55 -0
- data/lib/shopify_cli/git.rb +30 -0
- data/lib/shopify_cli/messages/messages.rb +27 -1
- data/lib/shopify_cli/method_object.rb +11 -4
- data/lib/shopify_cli/migrator/migration.rb +27 -0
- data/lib/shopify_cli/migrator/migrations/1631709766_noop.rb +13 -0
- data/lib/shopify_cli/migrator.rb +48 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/lib/shopify_cli.rb +11 -3
- data/shopify-cli.gemspec +9 -1
- data/utilities/docker.rb +47 -0
- data/utilities/utilities.rb +5 -0
- metadata +31 -6
- data/lib/project_types/script/layers/infrastructure/languages/rust_project_creator.rb +0 -73
- data/lib/project_types/script/layers/infrastructure/languages/rust_task_runner.rb +0 -60
| @@ -7,7 +7,7 @@ module Script | |
| 7 7 | 
             
                module Application
         | 
| 8 8 | 
             
                  class CreateScript
         | 
| 9 9 | 
             
                    class << self
         | 
| 10 | 
            -
                      def call(ctx:, language:, script_name:, extension_point_type:, no_config_ui:)
         | 
| 10 | 
            +
                      def call(ctx:, language:, sparse_checkout_branch:, script_name:, extension_point_type:, no_config_ui:)
         | 
| 11 11 | 
             
                        raise Infrastructure::Errors::ScriptProjectAlreadyExistsError, script_name if ctx.dir_exist?(script_name)
         | 
| 12 12 |  | 
| 13 13 | 
             
                        in_new_directory_context(ctx, script_name) do
         | 
| @@ -18,10 +18,24 @@ module Script | |
| 18 18 | 
             
                            extension_point_type: extension_point_type,
         | 
| 19 19 | 
             
                            language: language
         | 
| 20 20 | 
             
                          )
         | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 21 | 
            +
             | 
| 22 | 
            +
                          # remove the need to pass the whole extension-point object to the infra layer
         | 
| 23 | 
            +
                          sparse_checkout_repo = extension_point.libraries.for(language).repo
         | 
| 24 | 
            +
                          type = extension_point.dasherize_type
         | 
| 25 | 
            +
                          domain = extension_point.domain
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                          project_creator = Infrastructure::Languages::ProjectCreator.for(
         | 
| 28 | 
            +
                            ctx: ctx,
         | 
| 29 | 
            +
                            language: language,
         | 
| 30 | 
            +
                            type: type,
         | 
| 31 | 
            +
                            project_name: script_name,
         | 
| 32 | 
            +
                            path_to_project: project.id,
         | 
| 33 | 
            +
                            sparse_checkout_repo: sparse_checkout_repo,
         | 
| 34 | 
            +
                            sparse_checkout_branch: sparse_checkout_branch,
         | 
| 35 | 
            +
                            sparse_checkout_set_path: "#{domain}/#{language}/#{type}/default"
         | 
| 36 | 
            +
                          )
         | 
| 37 | 
            +
             | 
| 23 38 | 
             
                          install_dependencies(ctx, language, script_name, project_creator)
         | 
| 24 | 
            -
                          bootstrap(ctx, project_creator)
         | 
| 25 39 | 
             
                          script_project_repo.update_or_create_script_json(title: script_name, configuration_ui: !no_config_ui)
         | 
| 26 40 | 
             
                          project
         | 
| 27 41 | 
             
                        end
         | 
| @@ -31,15 +45,21 @@ module Script | |
| 31 45 |  | 
| 32 46 | 
             
                      def install_dependencies(ctx, language, script_name, project_creator)
         | 
| 33 47 | 
             
                        task_runner = Infrastructure::Languages::TaskRunner.for(ctx, language, script_name)
         | 
| 34 | 
            -
                         | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 48 | 
            +
                        CLI::UI::Frame.open(ctx.message(
         | 
| 49 | 
            +
                          "core.git.pulling_from_to",
         | 
| 50 | 
            +
                          project_creator.sparse_checkout_repo,
         | 
| 51 | 
            +
                          script_name,
         | 
| 52 | 
            +
                        )) do
         | 
| 53 | 
            +
                          UI::StrictSpinner.spin(ctx.message(
         | 
| 54 | 
            +
                            "core.git.pulling",
         | 
| 55 | 
            +
                            project_creator.sparse_checkout_repo,
         | 
| 56 | 
            +
                            script_name,
         | 
| 57 | 
            +
                          )) do |spinner|
         | 
| 58 | 
            +
                            project_creator.setup_dependencies
         | 
| 59 | 
            +
                            spinner.update_title(ctx.message("core.git.pulled", script_name))
         | 
| 60 | 
            +
                          end
         | 
| 42 61 | 
             
                        end
         | 
| 62 | 
            +
                        ProjectDependencies.install(ctx: ctx, task_runner: task_runner)
         | 
| 43 63 | 
             
                      end
         | 
| 44 64 |  | 
| 45 65 | 
             
                      def in_new_directory_context(ctx, directory)
         | 
| @@ -27,9 +27,9 @@ module Script | |
| 27 27 | 
             
                    end
         | 
| 28 28 |  | 
| 29 29 | 
             
                    def self.languages(type:)
         | 
| 30 | 
            -
                      get(type: type). | 
| 31 | 
            -
                        next nil if  | 
| 32 | 
            -
                         | 
| 30 | 
            +
                      get(type: type).libraries.all.map do |library|
         | 
| 31 | 
            +
                        next nil if library.beta? && !ShopifyCLI::Feature.enabled?(:scripts_beta_languages)
         | 
| 32 | 
            +
                        library.language
         | 
| 33 33 | 
             
                      end.compact
         | 
| 34 34 | 
             
                    end
         | 
| 35 35 |  | 
| @@ -4,14 +4,14 @@ module Script | |
| 4 4 | 
             
              module Layers
         | 
| 5 5 | 
             
                module Domain
         | 
| 6 6 | 
             
                  class ExtensionPoint
         | 
| 7 | 
            -
                    attr_reader :type, :beta, :deprecated, : | 
| 7 | 
            +
                    attr_reader :type, :beta, :deprecated, :libraries, :domain
         | 
| 8 8 |  | 
| 9 9 | 
             
                    def initialize(type, config)
         | 
| 10 10 | 
             
                      @type = type
         | 
| 11 11 | 
             
                      @beta = config["beta"] || false
         | 
| 12 12 | 
             
                      @deprecated = config["deprecated"] || false
         | 
| 13 13 | 
             
                      @domain = config["domain"] || nil
         | 
| 14 | 
            -
                      @ | 
| 14 | 
            +
                      @libraries = ExtensionPointLibraries.new(config["libraries"])
         | 
| 15 15 | 
             
                    end
         | 
| 16 16 |  | 
| 17 17 | 
             
                    def beta?
         | 
| @@ -26,39 +26,31 @@ module Script | |
| 26 26 | 
             
                      @type.gsub("_", "-")
         | 
| 27 27 | 
             
                    end
         | 
| 28 28 |  | 
| 29 | 
            -
                    class  | 
| 29 | 
            +
                    class ExtensionPointLibraries
         | 
| 30 30 | 
             
                      def initialize(config)
         | 
| 31 31 | 
             
                        @config = config
         | 
| 32 32 | 
             
                      end
         | 
| 33 33 |  | 
| 34 34 | 
             
                      def all
         | 
| 35 | 
            -
                         | 
| 35 | 
            +
                        @all ||= @config.map do |language, libray_config|
         | 
| 36 | 
            +
                          ExtensionPointLibrary.new(language, libray_config)
         | 
| 37 | 
            +
                        end
         | 
| 36 38 | 
             
                      end
         | 
| 37 39 |  | 
| 38 | 
            -
                      def  | 
| 39 | 
            -
                         | 
| 40 | 
            -
                      end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                      def rust
         | 
| 43 | 
            -
                        @rust ||= new_sdk(ExtensionPointRustSDK)
         | 
| 44 | 
            -
                      end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                      private
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                      def new_sdk(klass)
         | 
| 49 | 
            -
                        config = @config[klass.language]
         | 
| 50 | 
            -
                        return nil if config.nil?
         | 
| 51 | 
            -
                        klass.new(config)
         | 
| 40 | 
            +
                      def for(language)
         | 
| 41 | 
            +
                        all.find { |ep| ep.language == language }
         | 
| 52 42 | 
             
                      end
         | 
| 53 43 | 
             
                    end
         | 
| 54 44 |  | 
| 55 | 
            -
                    class  | 
| 56 | 
            -
                      attr_reader :version, :beta, :package
         | 
| 45 | 
            +
                    class ExtensionPointLibrary
         | 
| 46 | 
            +
                      attr_reader :language, :version, :beta, :package, :repo
         | 
| 57 47 |  | 
| 58 | 
            -
                      def initialize(config)
         | 
| 48 | 
            +
                      def initialize(language, config)
         | 
| 49 | 
            +
                        @language = language
         | 
| 59 50 | 
             
                        @beta = config["beta"] || false
         | 
| 60 51 | 
             
                        @package = config["package"]
         | 
| 61 52 | 
             
                        @version = config["package-version"]
         | 
| 53 | 
            +
                        @repo = config["repo"]
         | 
| 62 54 | 
             
                      end
         | 
| 63 55 |  | 
| 64 56 | 
             
                      def beta?
         | 
| @@ -68,30 +60,6 @@ module Script | |
| 68 60 | 
             
                      def versioned?
         | 
| 69 61 | 
             
                        @version
         | 
| 70 62 | 
             
                      end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                      def self.language
         | 
| 73 | 
            -
                        raise NotImplementedError
         | 
| 74 | 
            -
                      end
         | 
| 75 | 
            -
                    end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                    class ExtensionPointAssemblyScriptSDK < ExtensionPointSDK
         | 
| 78 | 
            -
                      attr_reader :sdk_version, :toolchain_version
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                      def initialize(config)
         | 
| 81 | 
            -
                        super
         | 
| 82 | 
            -
                        @sdk_version = config["sdk-version"]
         | 
| 83 | 
            -
                        @toolchain_version = config["toolchain-version"]
         | 
| 84 | 
            -
                      end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                      def self.language
         | 
| 87 | 
            -
                        "assemblyscript"
         | 
| 88 | 
            -
                      end
         | 
| 89 | 
            -
                    end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    class ExtensionPointRustSDK < ExtensionPointSDK
         | 
| 92 | 
            -
                      def self.language
         | 
| 93 | 
            -
                        "rust"
         | 
| 94 | 
            -
                      end
         | 
| 95 63 | 
             
                    end
         | 
| 96 64 | 
             
                  end
         | 
| 97 65 | 
             
                end
         | 
    
        data/lib/project_types/script/layers/infrastructure/api_clients/partners_proxy_api_client.rb
    CHANGED
    
    | @@ -12,8 +12,10 @@ module Script | |
| 12 12 | 
             
                        proxy_response = ShimAPI.query(@ctx, query_name, api_key: @api_key, variables: variables.to_json)
         | 
| 13 13 | 
             
                        raise_if_graphql_failed(proxy_response)
         | 
| 14 14 |  | 
| 15 | 
            -
                        response = proxy_response | 
| 16 | 
            -
                         | 
| 15 | 
            +
                        response = proxy_response.dig("data", "scriptServiceProxy")
         | 
| 16 | 
            +
                        raise Errors::InvalidResponseError unless response
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                        JSON.parse(response)
         | 
| 17 19 | 
             
                      end
         | 
| 18 20 |  | 
| 19 21 | 
             
                      private
         | 
| @@ -43,6 +43,7 @@ module Script | |
| 43 43 | 
             
                    class DependencyInstallError < ScriptProjectError; end
         | 
| 44 44 | 
             
                    class DeprecatedEPError < ScriptProjectError; end
         | 
| 45 45 | 
             
                    class EmptyResponseError < ScriptProjectError; end
         | 
| 46 | 
            +
                    class InvalidResponseError < ScriptProjectError; end
         | 
| 46 47 | 
             
                    class ForbiddenError < ScriptProjectError; end
         | 
| 47 48 | 
             
                    class InvalidContextError < ScriptProjectError; end
         | 
| 48 49 |  | 
| @@ -94,7 +95,11 @@ module Script | |
| 94 95 | 
             
                      end
         | 
| 95 96 | 
             
                    end
         | 
| 96 97 |  | 
| 98 | 
            +
                    class InvalidProjectError < ScriptProjectError; end
         | 
| 99 | 
            +
             | 
| 97 100 | 
             
                    class ScriptUploadError < ScriptProjectError; end
         | 
| 101 | 
            +
                    class ProjectConfigNotFoundError < ScriptProjectError; end
         | 
| 102 | 
            +
                    class InvalidProjectConfigError < ScriptProjectError; end
         | 
| 98 103 | 
             
                  end
         | 
| 99 104 | 
             
                end
         | 
| 100 105 | 
             
              end
         | 
    
        data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb
    CHANGED
    
    | @@ -4,99 +4,19 @@ module Script | |
| 4 4 | 
             
              module Layers
         | 
| 5 5 | 
             
                module Infrastructure
         | 
| 6 6 | 
             
                  module Languages
         | 
| 7 | 
            -
                    class AssemblyScriptProjectCreator
         | 
| 8 | 
            -
                       | 
| 9 | 
            -
                       | 
| 10 | 
            -
                       | 
| 11 | 
            -
                      property! :script_name, accepts: String
         | 
| 12 | 
            -
                      property! :path_to_project, accepts: String
         | 
| 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"
         | 
| 13 11 |  | 
| 14 | 
            -
                       | 
| 15 | 
            -
             | 
| 16 | 
            -
                      "--binary build/script.wasm --metadata build/metadata.json"
         | 
| 17 | 
            -
                      MIN_NODE_VERSION = "14.5.0"
         | 
| 18 | 
            -
                      ASC_ARGS = "-- --lib node_modules --optimize --use Date="
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                      def setup_dependencies
         | 
| 21 | 
            -
                        write_npmrc
         | 
| 22 | 
            -
                        write_package_json
         | 
| 23 | 
            -
                      end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                      def bootstrap
         | 
| 26 | 
            -
                        command_runner.call(bootstap_command)
         | 
| 27 | 
            -
                      end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                      private
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                      def command_runner
         | 
| 32 | 
            -
                        @command_runner ||= CommandRunner.new(ctx: ctx)
         | 
| 33 | 
            -
                      end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                      def write_npmrc
         | 
| 36 | 
            -
                        command_runner.call("npm --userconfig ./.npmrc config set @shopify:registry https://registry.npmjs.com")
         | 
| 37 | 
            -
                        command_runner.call("npm --userconfig ./.npmrc config set engine-strict true")
         | 
| 38 | 
            -
                      end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                      def extension_point_version
         | 
| 41 | 
            -
                        return extension_point.sdks.assemblyscript.version if extension_point.sdks.assemblyscript.versioned?
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                        out = command_runner.call("npm -s show #{extension_point.sdks.assemblyscript.package} version --json")
         | 
| 44 | 
            -
                        "^#{JSON.parse(out)}"
         | 
| 12 | 
            +
                      def self.config_file
         | 
| 13 | 
            +
                        "package.json"
         | 
| 45 14 | 
             
                      end
         | 
| 46 15 |  | 
| 47 | 
            -
                      def  | 
| 48 | 
            -
                         | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                          devDependencies: dev_dependencies,
         | 
| 52 | 
            -
                          scripts: {
         | 
| 53 | 
            -
                            test: "asp --summary --verbose",
         | 
| 54 | 
            -
                            build: build_command,
         | 
| 55 | 
            -
                          },
         | 
| 56 | 
            -
                          engines: {
         | 
| 57 | 
            -
                            node: ">=#{MIN_NODE_VERSION}",
         | 
| 58 | 
            -
                          },
         | 
| 59 | 
            -
                        }
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                        ctx.write("package.json", JSON.pretty_generate(package_json))
         | 
| 62 | 
            -
                      end
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                      def bootstap_command
         | 
| 65 | 
            -
                        type = extension_point.dasherize_type
         | 
| 66 | 
            -
                        base_command = format(BOOTSTRAP, extension_point: type, base: path_to_project)
         | 
| 67 | 
            -
                        domain = extension_point.domain
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                        if domain.nil?
         | 
| 70 | 
            -
                          base_command
         | 
| 71 | 
            -
                        else
         | 
| 72 | 
            -
                          "#{base_command} --domain #{domain}"
         | 
| 73 | 
            -
                        end
         | 
| 74 | 
            -
                      end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                      def build_command
         | 
| 77 | 
            -
                        type = extension_point.dasherize_type
         | 
| 78 | 
            -
                        domain = extension_point.domain
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                        if domain.nil?
         | 
| 81 | 
            -
                          "#{BUILD} #{ASC_ARGS}"
         | 
| 82 | 
            -
                        else
         | 
| 83 | 
            -
                          "#{BUILD} --domain #{domain} --ep #{type} #{ASC_ARGS}"
         | 
| 84 | 
            -
                        end
         | 
| 85 | 
            -
                      end
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                      def dev_dependencies
         | 
| 88 | 
            -
                        dependencies = {
         | 
| 89 | 
            -
                          "@as-pect/cli": "^6.0.0",
         | 
| 90 | 
            -
                          "assemblyscript": "^0.18.13",
         | 
| 91 | 
            -
                          "@shopify/scripts-toolchain-as": extension_point.sdks.assemblyscript.toolchain_version,
         | 
| 92 | 
            -
                          "#{extension_point.sdks.assemblyscript.package}": extension_point_version,
         | 
| 93 | 
            -
                        }
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                        if extension_point.sdks.assemblyscript.sdk_version
         | 
| 96 | 
            -
                          dependencies["@shopify/scripts-sdk-as"] = extension_point.sdks.assemblyscript.sdk_version
         | 
| 97 | 
            -
                        end
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                        dependencies
         | 
| 16 | 
            +
                      def setup_dependencies
         | 
| 17 | 
            +
                        super
         | 
| 18 | 
            +
                        command_runner.call(NPM_SET_REGISTRY_COMMAND)
         | 
| 19 | 
            +
                        command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
         | 
| 100 20 | 
             
                      end
         | 
| 101 21 | 
             
                    end
         | 
| 102 22 | 
             
                  end
         | 
| @@ -5,20 +5,85 @@ module Script | |
| 5 5 | 
             
                module Infrastructure
         | 
| 6 6 | 
             
                  module Languages
         | 
| 7 7 | 
             
                    class ProjectCreator
         | 
| 8 | 
            -
                       | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
                       | 
| 12 | 
            -
             | 
| 13 | 
            -
                       | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 8 | 
            +
                      include SmartProperties
         | 
| 9 | 
            +
                      property! :ctx, accepts: ShopifyCLI::Context
         | 
| 10 | 
            +
                      property! :type, accepts: String
         | 
| 11 | 
            +
                      property! :project_name, accepts: String
         | 
| 12 | 
            +
                      property! :path_to_project, accepts: String
         | 
| 13 | 
            +
                      property! :sparse_checkout_repo, accepts: String
         | 
| 14 | 
            +
                      property! :sparse_checkout_branch, accepts: String
         | 
| 15 | 
            +
                      property! :sparse_checkout_set_path, accepts: String
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                      def self.for(
         | 
| 18 | 
            +
                        ctx:,
         | 
| 19 | 
            +
                        language:,
         | 
| 20 | 
            +
                        type:,
         | 
| 21 | 
            +
                        project_name:,
         | 
| 22 | 
            +
                        path_to_project:,
         | 
| 23 | 
            +
                        sparse_checkout_repo:,
         | 
| 24 | 
            +
                        sparse_checkout_branch:,
         | 
| 25 | 
            +
                        sparse_checkout_set_path:
         | 
| 26 | 
            +
                      )
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                        project_creators = {
         | 
| 29 | 
            +
                          "assemblyscript" => AssemblyScriptProjectCreator,
         | 
| 30 | 
            +
                          "typescript" => TypeScriptProjectCreator,
         | 
| 31 | 
            +
                        }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                        raise Errors::ProjectCreatorNotFoundError unless project_creators[language]
         | 
| 34 | 
            +
                        project_creators[language].new(
         | 
| 16 35 | 
             
                          ctx: ctx,
         | 
| 17 | 
            -
                           | 
| 18 | 
            -
                           | 
| 19 | 
            -
                          path_to_project: path_to_project
         | 
| 36 | 
            +
                          type: type,
         | 
| 37 | 
            +
                          project_name: project_name,
         | 
| 38 | 
            +
                          path_to_project: path_to_project,
         | 
| 39 | 
            +
                          sparse_checkout_repo: sparse_checkout_repo,
         | 
| 40 | 
            +
                          sparse_checkout_branch: sparse_checkout_branch,
         | 
| 41 | 
            +
                          sparse_checkout_set_path: sparse_checkout_set_path
         | 
| 42 | 
            +
                        )
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      def self.config_file
         | 
| 46 | 
            +
                        raise NotImplementedError
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                      # the sparse checkout process is common to all script types
         | 
| 50 | 
            +
                      def setup_dependencies
         | 
| 51 | 
            +
                        setup_sparse_checkout
         | 
| 52 | 
            +
                        clean
         | 
| 53 | 
            +
                        update_project_name(File.join(path_to_project, self.class.config_file))
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      private
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      def setup_sparse_checkout
         | 
| 59 | 
            +
                        ShopifyCLI::Git.sparse_checkout(
         | 
| 60 | 
            +
                          sparse_checkout_repo,
         | 
| 61 | 
            +
                          sparse_checkout_set_path,
         | 
| 62 | 
            +
                          sparse_checkout_branch,
         | 
| 63 | 
            +
                          ctx
         | 
| 20 64 | 
             
                        )
         | 
| 21 65 | 
             
                      end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      def clean
         | 
| 68 | 
            +
                        source = File.join(path_to_project, sparse_checkout_set_path, ".")
         | 
| 69 | 
            +
                        FileUtils.cp_r(source, path_to_project)
         | 
| 70 | 
            +
                        ctx.rm_rf(sparse_checkout_set_path.split("/")[0])
         | 
| 71 | 
            +
                        ctx.rm_rf(".git")
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      def update_project_name(config_file)
         | 
| 75 | 
            +
                        raise Errors::ProjectConfigNotFoundError unless File.exist?(config_file)
         | 
| 76 | 
            +
                        upstream_name = "#{type.gsub("_", "-")}-default"
         | 
| 77 | 
            +
                        contents = File.read(config_file)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                        raise Errors::InvalidProjectConfigError unless contents.include?(upstream_name)
         | 
| 80 | 
            +
                        new_contents = contents.gsub(upstream_name, project_name)
         | 
| 81 | 
            +
                        File.write(config_file, new_contents)
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      def command_runner
         | 
| 85 | 
            +
                        @command_runner ||= CommandRunner.new(ctx: ctx)
         | 
| 86 | 
            +
                      end
         | 
| 22 87 | 
             
                    end
         | 
| 23 88 | 
             
                  end
         | 
| 24 89 | 
             
                end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Script
         | 
| 4 | 
            +
              module Layers
         | 
| 5 | 
            +
                module Infrastructure
         | 
| 6 | 
            +
                  module Languages
         | 
| 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 | 
            +
                      def setup_dependencies
         | 
| 17 | 
            +
                        super
         | 
| 18 | 
            +
                        command_runner.call(NPM_SET_REGISTRY_COMMAND)
         | 
| 19 | 
            +
                        command_runner.call(NPM_SET_ENGINE_STRICT_COMMAND)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                        if ctx.file_exist?("yarn.lock")
         | 
| 22 | 
            +
                          ctx.rm("yarn.lock")
         | 
| 23 | 
            +
                        end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                        if ctx.file_exist?("package-lock.json")
         | 
| 26 | 
            +
                          ctx.rm("package-lock.json")
         | 
| 27 | 
            +
                        end
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,105 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Script
         | 
| 4 | 
            +
              module Layers
         | 
| 5 | 
            +
                module Infrastructure
         | 
| 6 | 
            +
                  module Languages
         | 
| 7 | 
            +
                    class TypeScriptTaskRunner
         | 
| 8 | 
            +
                      BYTECODE_FILE = "build/%{name}.wasm"
         | 
| 9 | 
            +
                      METADATA_FILE = "build/metadata.json"
         | 
| 10 | 
            +
                      SCRIPT_SDK_BUILD = "npm run build"
         | 
| 11 | 
            +
                      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
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def build
         | 
| 21 | 
            +
                        compile
         | 
| 22 | 
            +
                        bytecode
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      def compiled_type
         | 
| 26 | 
            +
                        "wasm"
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                      def install_dependencies
         | 
| 30 | 
            +
                        check_node_version!
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                        output, status = ctx.capture2e("npm install --no-audit --no-optional --legacy-peer-deps --loglevel error")
         | 
| 33 | 
            +
                        raise Errors::DependencyInstallError, output unless status.success?
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      def dependencies_installed?
         | 
| 37 | 
            +
                        # Assuming if node_modules folder exist at root of script folder, all deps are installed
         | 
| 38 | 
            +
                        ctx.dir_exist?("node_modules")
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      def metadata
         | 
| 42 | 
            +
                        unless @ctx.file_exist?(METADATA_FILE)
         | 
| 43 | 
            +
                          msg = @ctx.message("script.error.metadata_not_found_cause", METADATA_FILE)
         | 
| 44 | 
            +
                          raise Domain::Errors::MetadataNotFoundError, msg
         | 
| 45 | 
            +
                        end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                        raw_contents = File.read(METADATA_FILE)
         | 
| 48 | 
            +
                        Domain::Metadata.create_from_json(@ctx, raw_contents)
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      private
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                      def check_node_version!
         | 
| 54 | 
            +
                        output, status = @ctx.capture2e("node", "--version")
         | 
| 55 | 
            +
                        raise Errors::DependencyInstallError, output unless status.success?
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                        require "semantic/semantic"
         | 
| 58 | 
            +
                        version = ::Semantic::Version.new(output[1..-1])
         | 
| 59 | 
            +
                        unless version >= ::Semantic::Version.new(TypeScriptProjectCreator::MIN_NODE_VERSION)
         | 
| 60 | 
            +
                          raise Errors::DependencyInstallError,
         | 
| 61 | 
            +
                            "Node version must be >= v#{TypeScriptProjectCreator::MIN_NODE_VERSION}. "\
         | 
| 62 | 
            +
                        "Current version: #{output.strip}."
         | 
| 63 | 
            +
                        end
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      def compile
         | 
| 67 | 
            +
                        check_compilation_dependencies!
         | 
| 68 | 
            +
                        CommandRunner.new(ctx: ctx).call(SCRIPT_SDK_BUILD)
         | 
| 69 | 
            +
                        CommandRunner.new(ctx: ctx).call(GEN_METADATA)
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      def check_compilation_dependencies!
         | 
| 73 | 
            +
                        pkg = JSON.parse(File.read("package.json"))
         | 
| 74 | 
            +
                        build_script = pkg.dig("scripts", "build")
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                        raise Errors::BuildScriptNotFoundError,
         | 
| 77 | 
            +
                          "Build script not found" if build_script.nil?
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                        unless build_script.start_with?("javy")
         | 
| 80 | 
            +
                          raise Errors::InvalidBuildScriptError, "Invalid build script"
         | 
| 81 | 
            +
                        end
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      def bytecode
         | 
| 85 | 
            +
                        legacy_filename = format(BYTECODE_FILE, name: script_name)
         | 
| 86 | 
            +
                        filename = format(BYTECODE_FILE, name: "index")
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                        bytecode_file = if ctx.file_exist?(filename)
         | 
| 89 | 
            +
                          filename
         | 
| 90 | 
            +
                        elsif ctx.file_exist?(legacy_filename)
         | 
| 91 | 
            +
                          legacy_filename
         | 
| 92 | 
            +
                        else
         | 
| 93 | 
            +
                          raise Errors::WebAssemblyBinaryNotFoundError
         | 
| 94 | 
            +
                        end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                        contents = ctx.binread(bytecode_file)
         | 
| 97 | 
            +
                        ctx.rm(bytecode_file)
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                        contents
         | 
| 100 | 
            +
                      end
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
            end
         | 
| @@ -142,6 +142,10 @@ module Script | |
| 142 142 | 
             
                        "Check that your build npm script outputs the generated binary to the root of the directory." \
         | 
| 143 143 | 
             
                        "Generated binary should match the script name: <script_name>.wasm",
         | 
| 144 144 |  | 
| 145 | 
            +
                      project_config_not_found: "Internal error - Script can't be created because the project's config file is missing from the repository.",
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                      invalid_project_config: "Internal error - Script can't be created because the project's config file is invalid in the repository.",
         | 
| 148 | 
            +
             | 
| 145 149 | 
             
                      script_upload_cause: "Fail to upload script.",
         | 
| 146 150 | 
             
                      script_upload_help: "Try again.",
         | 
| 147 151 | 
             
                    },
         | 
| @@ -237,6 +237,14 @@ module Script | |
| 237 237 | 
             
                        cause_of_error: ShopifyCLI::Context.message("script.error.web_assembly_binary_not_found"),
         | 
| 238 238 | 
             
                        help_suggestion: ShopifyCLI::Context.message("script.error.web_assembly_binary_not_found_suggestion"),
         | 
| 239 239 | 
             
                      }
         | 
| 240 | 
            +
                    when Layers::Infrastructure::Errors::ProjectConfigNotFoundError
         | 
| 241 | 
            +
                      {
         | 
| 242 | 
            +
                        cause_of_error: ShopifyCLI::Context.message("script.error.project_config_not_found"),
         | 
| 243 | 
            +
                      }
         | 
| 244 | 
            +
                    when Layers::Infrastructure::Errors::InvalidProjectConfigError
         | 
| 245 | 
            +
                      {
         | 
| 246 | 
            +
                        cause_of_error: ShopifyCLI::Context.message("script.error.invalid_project_config"),
         | 
| 247 | 
            +
                      }
         | 
| 240 248 | 
             
                    when Layers::Infrastructure::Errors::ScriptUploadError
         | 
| 241 249 | 
             
                      {
         | 
| 242 250 | 
             
                        cause_of_error: ShopifyCLI::Context.message("script.error.script_upload_cause"),
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module ShopifyCLI
         | 
| 2 | 
            +
              class Command
         | 
| 3 | 
            +
                class AppSubCommand < SubCommand
         | 
| 4 | 
            +
                  class << self
         | 
| 5 | 
            +
                    def call_help(*)
         | 
| 6 | 
            +
                      output = help
         | 
| 7 | 
            +
                      if respond_to?(:extended_help)
         | 
| 8 | 
            +
                        output += "\n"
         | 
| 9 | 
            +
                        output += extended_help
         | 
| 10 | 
            +
                      end
         | 
| 11 | 
            +
                      @ctx.puts(output)
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         |