shopify-cli 2.7.3 → 2.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +1 -1
- data/dev.yml +2 -2
- data/ext/javy/javy.rb +7 -8
- data/lib/graphql/get_extension_registrations.graphql +27 -0
- data/lib/project_types/extension/cli.rb +27 -2
- data/lib/project_types/extension/commands/build.rb +10 -10
- data/lib/project_types/extension/commands/create.rb +2 -3
- data/lib/project_types/extension/commands/push.rb +36 -8
- data/lib/project_types/extension/extension_project.rb +1 -1
- data/lib/project_types/extension/features/argo_serve.rb +6 -5
- data/lib/project_types/extension/forms/questions/ask_registration.rb +6 -2
- data/lib/project_types/extension/loaders/project.rb +29 -0
- data/lib/project_types/extension/loaders/specification_handler.rb +22 -0
- data/lib/project_types/extension/messages/messages.rb +4 -0
- data/lib/project_types/extension/models/app.rb +1 -1
- data/lib/project_types/extension/models/specification_handlers/default.rb +4 -0
- data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
- data/lib/project_types/extension/tasks/execute_commands/base.rb +13 -0
- data/lib/project_types/extension/tasks/execute_commands/build.rb +29 -0
- data/lib/project_types/extension/tasks/execute_commands/create.rb +33 -0
- data/lib/project_types/extension/tasks/execute_commands/serve.rb +35 -0
- data/lib/project_types/extension/tasks/merge_server_config.rb +33 -22
- data/lib/project_types/script/cli.rb +2 -0
- data/lib/project_types/script/commands/connect.rb +19 -0
- data/lib/project_types/script/layers/application/connect_app.rb +9 -3
- data/lib/project_types/script/layers/application/create_script.rb +16 -16
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +21 -0
- data/lib/project_types/script/messages/messages.rb +12 -1
- data/lib/project_types/theme/commands/push.rb +3 -1
- data/lib/project_types/theme/messages/messages.rb +1 -0
- data/lib/shopify_cli/command.rb +6 -0
- data/lib/shopify_cli/constants.rb +4 -0
- data/lib/shopify_cli/context.rb +1 -1
- data/lib/shopify_cli/form.rb +2 -0
- data/lib/shopify_cli/messages/messages.rb +7 -1
- data/lib/shopify_cli/partners_api/app_extensions/job.rb +36 -0
- data/lib/shopify_cli/partners_api/app_extensions.rb +46 -0
- data/lib/shopify_cli/partners_api/organizations.rb +2 -5
- data/lib/shopify_cli/partners_api.rb +1 -0
- data/lib/shopify_cli/project.rb +8 -7
- data/lib/shopify_cli/resources/env_file.rb +13 -5
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +15 -2
- data/lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb +84 -0
- data/lib/shopify_cli/theme/dev_server/proxy.rb +9 -15
- data/lib/shopify_cli/thread_pool/job.rb +27 -0
- data/lib/shopify_cli/thread_pool.rb +37 -0
- data/lib/shopify_cli/version.rb +1 -1
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
- metadata +15 -4
- data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
- data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
@@ -13,6 +13,7 @@ module Script
|
|
13
13
|
hidden_feature(feature_set: :script_project)
|
14
14
|
subcommand :Create, "create", Project.project_filepath("commands/create")
|
15
15
|
subcommand :Push, "push", Project.project_filepath("commands/push")
|
16
|
+
subcommand :Connect, "connect", Project.project_filepath("commands/connect")
|
16
17
|
subcommand :Javy, "javy", Project.project_filepath("commands/javy")
|
17
18
|
end
|
18
19
|
ShopifyCLI::Commands.register("Script::Command", "script")
|
@@ -24,6 +25,7 @@ module Script
|
|
24
25
|
autoload :AskScriptUuid, Project.project_filepath("forms/ask_script_uuid")
|
25
26
|
autoload :RunAgainstShopifyOrg, Project.project_filepath("forms/run_against_shopify_org")
|
26
27
|
autoload :Create, Project.project_filepath("forms/create")
|
28
|
+
autoload :Connect, Project.project_filepath("forms/connect")
|
27
29
|
autoload :ScriptForm, Project.project_filepath("forms/script_form")
|
28
30
|
end
|
29
31
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Script
|
3
|
+
class Command
|
4
|
+
class Connect < ShopifyCLI::Command::SubCommand
|
5
|
+
prerequisite_task :ensure_authenticated
|
6
|
+
prerequisite_task ensure_project_type: :script
|
7
|
+
|
8
|
+
def call(_args, _)
|
9
|
+
Layers::Application::ConnectApp.call(ctx: @ctx, force: true, strict: true)
|
10
|
+
rescue StandardError => e
|
11
|
+
UI::ErrorHandler.pretty_print_and_raise(e, failed_op: @ctx.message("script.connect.error.operation_failed"))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.help
|
15
|
+
ShopifyCLI::Context.new.message("connect.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -7,10 +7,11 @@ module Script
|
|
7
7
|
module Application
|
8
8
|
class ConnectApp
|
9
9
|
class << self
|
10
|
-
def call(ctx:)
|
10
|
+
def call(ctx:, force: false, strict: false)
|
11
11
|
script_project_repo = Layers::Infrastructure::ScriptProjectRepository.new(ctx: ctx)
|
12
12
|
script_project = script_project_repo.get
|
13
|
-
|
13
|
+
|
14
|
+
return false if script_project.env_valid? && !force
|
14
15
|
|
15
16
|
if ShopifyCLI::Shopifolk.check && Forms::RunAgainstShopifyOrg.ask(ctx, nil, nil).response
|
16
17
|
ShopifyCLI::Shopifolk.act_as_shopify_organization
|
@@ -37,13 +38,18 @@ module Script
|
|
37
38
|
extension_point_type = script_project.extension_point_type
|
38
39
|
scripts = script_service.get_app_scripts(extension_point_type: extension_point_type)
|
39
40
|
|
40
|
-
uuid = Forms::AskScriptUuid.ask(ctx, scripts, nil)
|
41
|
+
uuid = Forms::AskScriptUuid.ask(ctx, scripts, nil)&.uuid
|
42
|
+
|
43
|
+
if strict && uuid.nil?
|
44
|
+
ctx.abort(ctx.message("script.connect.missing_script"))
|
45
|
+
end
|
41
46
|
|
42
47
|
script_project_repo.create_env(
|
43
48
|
api_key: app["apiKey"],
|
44
49
|
secret: app["apiSecretKeys"].first["secret"],
|
45
50
|
uuid: uuid
|
46
51
|
)
|
52
|
+
ctx.done(ctx.message("script.connect.connected", app["title"]))
|
47
53
|
|
48
54
|
true
|
49
55
|
end
|
@@ -8,11 +8,14 @@ module Script
|
|
8
8
|
class CreateScript
|
9
9
|
class << self
|
10
10
|
def call(ctx:, language:, sparse_checkout_branch:, script_name:, extension_point_type:)
|
11
|
-
|
11
|
+
script_project_repo = Infrastructure::ScriptProjectRepository.new(
|
12
|
+
ctx: ctx,
|
13
|
+
directory: script_name,
|
14
|
+
initial_directory: ctx.root
|
15
|
+
)
|
12
16
|
|
13
|
-
in_new_directory_context(
|
17
|
+
in_new_directory_context(script_project_repo) do
|
14
18
|
extension_point = ExtensionPoints.get(type: extension_point_type)
|
15
|
-
script_project_repo = Infrastructure::ScriptProjectRepository.new(ctx: ctx)
|
16
19
|
project = script_project_repo.create(
|
17
20
|
script_name: script_name,
|
18
21
|
extension_point_type: extension_point_type,
|
@@ -62,19 +65,16 @@ module Script
|
|
62
65
|
ProjectDependencies.install(ctx: ctx, task_runner: task_runner)
|
63
66
|
end
|
64
67
|
|
65
|
-
def in_new_directory_context(
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
ensure
|
76
|
-
ctx.chdir(initial_directory)
|
77
|
-
end
|
68
|
+
def in_new_directory_context(script_project_repo)
|
69
|
+
script_project_repo.create_project_directory
|
70
|
+
yield
|
71
|
+
rescue Infrastructure::Errors::ScriptProjectAlreadyExistsError
|
72
|
+
raise
|
73
|
+
rescue
|
74
|
+
script_project_repo.delete_project_directory
|
75
|
+
raise
|
76
|
+
ensure
|
77
|
+
script_project_repo.change_to_initial_directory
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -6,9 +6,26 @@ module Script
|
|
6
6
|
class ScriptProjectRepository
|
7
7
|
include SmartProperties
|
8
8
|
property! :ctx, accepts: ShopifyCLI::Context
|
9
|
+
property :directory, accepts: String
|
10
|
+
property :initial_directory, accepts: String
|
9
11
|
|
10
12
|
MUTABLE_ENV_VALUES = %i(uuid)
|
11
13
|
|
14
|
+
def create_project_directory
|
15
|
+
raise Infrastructure::Errors::ScriptProjectAlreadyExistsError, directory if ctx.dir_exist?(directory)
|
16
|
+
ctx.mkdir_p(directory)
|
17
|
+
change_directory(directory: directory)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_project_directory
|
21
|
+
change_to_initial_directory
|
22
|
+
ctx.rm_r(directory)
|
23
|
+
end
|
24
|
+
|
25
|
+
def change_to_initial_directory
|
26
|
+
change_directory(directory: initial_directory)
|
27
|
+
end
|
28
|
+
|
12
29
|
def create(script_name:, extension_point_type:, language:)
|
13
30
|
validate_metadata!(extension_point_type, language)
|
14
31
|
|
@@ -95,6 +112,10 @@ module Script
|
|
95
112
|
|
96
113
|
private
|
97
114
|
|
115
|
+
def change_directory(directory:)
|
116
|
+
ctx.chdir(directory)
|
117
|
+
end
|
118
|
+
|
98
119
|
def capture_io(&block)
|
99
120
|
CLI::UI::StdoutRouter::Capture.new(&block).run
|
100
121
|
end
|
@@ -144,6 +144,7 @@ module Script
|
|
144
144
|
|
145
145
|
language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
|
146
146
|
language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
|
147
|
+
no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
|
147
148
|
},
|
148
149
|
|
149
150
|
create: {
|
@@ -179,7 +180,17 @@ module Script
|
|
179
180
|
|
180
181
|
script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
|
181
182
|
},
|
182
|
-
|
183
|
+
connect: {
|
184
|
+
connected: "Connected! Your project is now connected to {{green:%s}}",
|
185
|
+
missing_script: "No script has been selected.",
|
186
|
+
help: <<~HELP,
|
187
|
+
{{command:%s script connect}}: Connects an existing script to an app.
|
188
|
+
Usage: {{command:%s script connect}}
|
189
|
+
HELP
|
190
|
+
error: {
|
191
|
+
operation_failed: "Couldn't connect script to app.",
|
192
|
+
},
|
193
|
+
},
|
183
194
|
javy: {
|
184
195
|
help: <<~HELP,
|
185
196
|
Compile the JavaScript code into WebAssembly.
|
@@ -51,7 +51,9 @@ module Theme
|
|
51
51
|
end
|
52
52
|
|
53
53
|
if theme.live? && !options.flags[:allow_live]
|
54
|
-
|
54
|
+
question = @ctx.message("theme.push.live")
|
55
|
+
question += @ctx.message("theme.push.theme", theme.name, theme.id) if options.flags[:live]
|
56
|
+
return unless CLI::UI::Prompt.confirm(question)
|
55
57
|
end
|
56
58
|
|
57
59
|
ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
|
@@ -74,6 +74,7 @@ module Theme
|
|
74
74
|
push: "Pushing theme files to Shopify",
|
75
75
|
select: "Select theme to push to",
|
76
76
|
live: "Are you sure you want to push to your live theme?",
|
77
|
+
theme: "\n Theme: {{blue:%s #%s}} {{green:[live]}}",
|
77
78
|
theme_not_found: "Theme #%s doesn't exist",
|
78
79
|
done: <<~DONE,
|
79
80
|
{{green:Your theme was pushed successfully}}
|
data/lib/shopify_cli/command.rb
CHANGED
@@ -29,6 +29,12 @@ module ShopifyCLI
|
|
29
29
|
run_prerequisites
|
30
30
|
cmd.call(args, command_name)
|
31
31
|
end
|
32
|
+
rescue OptionParser::InvalidOption => error
|
33
|
+
arg = error.args.first
|
34
|
+
raise ShopifyCLI::Abort, @ctx.message("core.errors.option_parser.invalid_option", arg)
|
35
|
+
rescue OptionParser::MissingArgument => error
|
36
|
+
arg = error.args.first
|
37
|
+
raise ShopifyCLI::Abort, @ctx.message("core.errors.option_parser.missing_argument", arg)
|
32
38
|
end
|
33
39
|
|
34
40
|
def options(&block)
|
data/lib/shopify_cli/context.rb
CHANGED
data/lib/shopify_cli/form.rb
CHANGED
@@ -14,6 +14,12 @@ module ShopifyCLI
|
|
14
14
|
},
|
15
15
|
},
|
16
16
|
core: {
|
17
|
+
errors: {
|
18
|
+
option_parser: {
|
19
|
+
invalid_option: "The option {{command:%s}} is not supported.",
|
20
|
+
missing_argument: "The required argument {{command:%s}} is missing.",
|
21
|
+
},
|
22
|
+
},
|
17
23
|
app: {
|
18
24
|
help: <<~HELP,
|
19
25
|
Suite of commands for developing apps. See {{command:%1$s app <command> --help}} for usage of each command.
|
@@ -719,7 +725,7 @@ module ShopifyCLI
|
|
719
725
|
signup_suggestion: <<~MESSAGE,
|
720
726
|
{{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
|
721
727
|
account at {{underline:https://ngrok.com/signup}}. After you signup, install your
|
722
|
-
personalized authorization token using {{command:%s
|
728
|
+
personalized authorization token using {{command:%s app tunnel auth <token>}}.
|
723
729
|
MESSAGE
|
724
730
|
start: "{{v}} ngrok tunnel running at {{underline:%s}}",
|
725
731
|
start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/thread_pool/job"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
class PartnersAPI
|
7
|
+
class AppExtensions
|
8
|
+
class Job < ShopifyCLI::ThreadPool::Job
|
9
|
+
attr_reader :result
|
10
|
+
|
11
|
+
def initialize(ctx, app, type)
|
12
|
+
super()
|
13
|
+
@ctx = ctx
|
14
|
+
@app = app
|
15
|
+
@api_key = @app["apiKey"]
|
16
|
+
@type = type
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform!
|
20
|
+
resp = PartnersAPI.query(@ctx, "get_extension_registrations", **params)
|
21
|
+
@result = resp&.dig("data", "app") || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def patch_app_with_extensions!
|
25
|
+
@app.merge!(result)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def params
|
31
|
+
{ api_key: @api_key, type: @type }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shopify_cli/thread_pool"
|
4
|
+
|
5
|
+
require_relative "app_extensions/job"
|
6
|
+
|
7
|
+
module ShopifyCLI
|
8
|
+
class PartnersAPI
|
9
|
+
class AppExtensions
|
10
|
+
class << self
|
11
|
+
def fetch_apps_extensions(ctx, orgs, type)
|
12
|
+
jobs = apps(orgs).map { |app| AppExtensions::Job.new(ctx, app, type) }
|
13
|
+
|
14
|
+
consume_jobs!(jobs)
|
15
|
+
patch_apps_with_extensions!(jobs)
|
16
|
+
|
17
|
+
orgs
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def apps(orgs)
|
23
|
+
orgs.flat_map { |org| org["apps"] }
|
24
|
+
end
|
25
|
+
|
26
|
+
def consume_jobs!(jobs)
|
27
|
+
thread_pool = ShopifyCLI::ThreadPool.new
|
28
|
+
jobs.each do |job|
|
29
|
+
thread_pool.schedule(job)
|
30
|
+
end
|
31
|
+
thread_pool.shutdown
|
32
|
+
|
33
|
+
raise_if_any_error(jobs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def patch_apps_with_extensions!(jobs)
|
37
|
+
jobs.each(&:patch_app_with_extensions!)
|
38
|
+
end
|
39
|
+
|
40
|
+
def raise_if_any_error(jobs)
|
41
|
+
jobs.find(&:error?).tap { |job| raise job.error if job }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -28,11 +28,8 @@ module ShopifyCLI
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def fetch_with_extensions(ctx, type)
|
31
|
-
|
32
|
-
(
|
33
|
-
org["apps"] = (org.dig("apps", "nodes") || [])
|
34
|
-
org
|
35
|
-
end
|
31
|
+
orgs = fetch_with_app(ctx)
|
32
|
+
AppExtensions.fetch_apps_extensions(ctx, orgs, type)
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
data/lib/shopify_cli/project.rb
CHANGED
@@ -107,13 +107,6 @@ module ShopifyCLI
|
|
107
107
|
@dir = nil
|
108
108
|
end
|
109
109
|
|
110
|
-
private
|
111
|
-
|
112
|
-
def directory(dir)
|
113
|
-
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
114
|
-
@dir[dir]
|
115
|
-
end
|
116
|
-
|
117
110
|
def at(dir)
|
118
111
|
proj_dir = directory(dir)
|
119
112
|
unless proj_dir
|
@@ -123,6 +116,13 @@ module ShopifyCLI
|
|
123
116
|
@at[proj_dir]
|
124
117
|
end
|
125
118
|
|
119
|
+
private
|
120
|
+
|
121
|
+
def directory(dir)
|
122
|
+
@dir ||= Hash.new { |h, k| h[k] = __directory(k) }
|
123
|
+
@dir[dir]
|
124
|
+
end
|
125
|
+
|
126
126
|
def __directory(curr)
|
127
127
|
loop do
|
128
128
|
return nil if curr == "/" || /^[A-Z]:\/$/.match?(curr)
|
@@ -134,6 +134,7 @@ module ShopifyCLI
|
|
134
134
|
end
|
135
135
|
|
136
136
|
property :directory # :nodoc:
|
137
|
+
property :env # :nodoc:
|
137
138
|
|
138
139
|
##
|
139
140
|
# will read, parse and return the envfile for the project
|
@@ -14,11 +14,15 @@ module ShopifyCLI
|
|
14
14
|
}
|
15
15
|
|
16
16
|
class << self
|
17
|
-
def read(_directory = Dir.pwd)
|
18
|
-
input = parse_external_env
|
17
|
+
def read(_directory = Dir.pwd, overrides: {})
|
18
|
+
input = parse_external_env(overrides: overrides)
|
19
19
|
new(input)
|
20
20
|
end
|
21
21
|
|
22
|
+
def from_hash(hash)
|
23
|
+
new(env_input(hash))
|
24
|
+
end
|
25
|
+
|
22
26
|
def parse(directory)
|
23
27
|
File.read(File.join(directory, FILENAME))
|
24
28
|
.gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
|
@@ -37,10 +41,14 @@ module ShopifyCLI
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def parse_external_env(directory = Dir.pwd)
|
44
|
+
def parse_external_env(directory = Dir.pwd, overrides: {})
|
45
|
+
env_input(parse(directory), overrides: overrides)
|
46
|
+
end
|
47
|
+
|
48
|
+
def env_input(parsed_source, overrides: {})
|
41
49
|
env_details = {}
|
42
50
|
extra = {}
|
43
|
-
|
51
|
+
parsed_source.merge(overrides).each do |key, value|
|
44
52
|
if KEY_MAP[key]
|
45
53
|
env_details[KEY_MAP[key]] = value
|
46
54
|
else
|
@@ -53,7 +61,7 @@ module ShopifyCLI
|
|
53
61
|
end
|
54
62
|
|
55
63
|
property :api_key, required: true
|
56
|
-
property :secret
|
64
|
+
property :secret
|
57
65
|
property :shop
|
58
66
|
property :scopes
|
59
67
|
property :host
|
@@ -33,7 +33,20 @@
|
|
33
33
|
}
|
34
34
|
}
|
35
35
|
|
36
|
-
function
|
36
|
+
function setHotReloadCookie(files) {
|
37
|
+
var date = new Date();
|
38
|
+
|
39
|
+
// Hot reload cookie expires in 3 seconds
|
40
|
+
date.setSeconds(date.getSeconds() + 3);
|
41
|
+
|
42
|
+
var sections = files.join(',');
|
43
|
+
var expires = date.toUTCString();
|
44
|
+
|
45
|
+
document.cookie = `hot_reload_sections=${sections}; expires=${expires}; path=/`;
|
46
|
+
}
|
47
|
+
|
48
|
+
function refreshPage(files) {
|
49
|
+
setHotReloadCookie(files);
|
37
50
|
console.log('[HotReload] Refreshing entire page');
|
38
51
|
window.location.reload();
|
39
52
|
}
|
@@ -43,7 +56,7 @@
|
|
43
56
|
var modifiedFiles = data.modified;
|
44
57
|
|
45
58
|
if (isRefreshRequired(modifiedFiles)) {
|
46
|
-
refreshPage();
|
59
|
+
refreshPage(modifiedFiles);
|
47
60
|
} else {
|
48
61
|
modifiedFiles.forEach(refreshFile);
|
49
62
|
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
|
5
|
+
module ShopifyCLI
|
6
|
+
module Theme
|
7
|
+
module DevServer
|
8
|
+
class Proxy
|
9
|
+
class TemplateParamBuilder
|
10
|
+
def build
|
11
|
+
# Core doesn't support replace_templates
|
12
|
+
return {} if core?(current_path)
|
13
|
+
|
14
|
+
(syncer_templates + request_templates)
|
15
|
+
.select { |file| file.liquid? || file.json? }
|
16
|
+
.uniq(&:relative_path)
|
17
|
+
.map { |file| as_param(file) }
|
18
|
+
.to_h
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_core_endpoints(core_endpoints)
|
22
|
+
@core_endpoints = core_endpoints
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_syncer(syncer)
|
27
|
+
@syncer = syncer
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_rack_env(rack_env)
|
32
|
+
@rack_env = rack_env
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_theme(theme)
|
37
|
+
@theme = theme
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def as_param(file)
|
44
|
+
["replace_templates[#{file.relative_path}]", file.read]
|
45
|
+
end
|
46
|
+
|
47
|
+
def syncer_templates
|
48
|
+
@syncer&.pending_updates || []
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_templates
|
52
|
+
cookie_sections
|
53
|
+
.map { |section| @theme[section] unless @theme.nil? }
|
54
|
+
.compact
|
55
|
+
end
|
56
|
+
|
57
|
+
def cookie_sections
|
58
|
+
CGI::Cookie.parse(cookie)["hot_reload_sections"].join.split(",") || []
|
59
|
+
end
|
60
|
+
|
61
|
+
def core?(path)
|
62
|
+
core_endpoints.include?(path)
|
63
|
+
end
|
64
|
+
|
65
|
+
def current_path
|
66
|
+
rack_env["PATH_INFO"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def cookie
|
70
|
+
rack_env["HTTP_COOKIE"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def core_endpoints
|
74
|
+
@core_endpoints || []
|
75
|
+
end
|
76
|
+
|
77
|
+
def rack_env
|
78
|
+
@rack_env || {}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
require "net/http"
|
3
3
|
require "stringio"
|
4
4
|
require "time"
|
5
|
+
require "cgi"
|
6
|
+
|
7
|
+
require_relative "proxy/template_param_builder"
|
5
8
|
|
6
9
|
module ShopifyCLI
|
7
10
|
module Theme
|
@@ -112,21 +115,12 @@ module ShopifyCLI
|
|
112
115
|
end
|
113
116
|
|
114
117
|
def build_replace_templates_param(env)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
# Only replace Liquid or JSON files
|
122
|
-
file.liquid? || file.json?
|
123
|
-
end
|
124
|
-
|
125
|
-
pending_templates.each do |path|
|
126
|
-
params["replace_templates[#{path.relative_path}]"] = path.read
|
127
|
-
end
|
128
|
-
|
129
|
-
params
|
118
|
+
TemplateParamBuilder.new
|
119
|
+
.with_core_endpoints(@core_endpoints)
|
120
|
+
.with_syncer(@syncer)
|
121
|
+
.with_theme(@theme)
|
122
|
+
.with_rack_env(env)
|
123
|
+
.build
|
130
124
|
end
|
131
125
|
|
132
126
|
def add_session_cookie(cookie_header)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
class ThreadPool
|
5
|
+
class Job
|
6
|
+
attr_reader :error
|
7
|
+
|
8
|
+
def perform!
|
9
|
+
raise "`#{self.class.name}#perform!` must be defined"
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
perform!
|
14
|
+
rescue StandardError => error
|
15
|
+
@error = error
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
!@error
|
20
|
+
end
|
21
|
+
|
22
|
+
def error?
|
23
|
+
!!@error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
class ThreadPool
|
5
|
+
attr_reader :errors
|
6
|
+
|
7
|
+
def initialize(pool_size: 10)
|
8
|
+
@jobs = Queue.new
|
9
|
+
@pool = Array.new(pool_size) { spawn_thread }
|
10
|
+
end
|
11
|
+
|
12
|
+
def schedule(job)
|
13
|
+
@jobs << job
|
14
|
+
end
|
15
|
+
|
16
|
+
def shutdown
|
17
|
+
@pool.size.times do
|
18
|
+
schedule(-> { throw(:stop_thread) })
|
19
|
+
end
|
20
|
+
@pool.map(&:join)
|
21
|
+
ensure
|
22
|
+
@jobs.close
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def spawn_thread
|
28
|
+
Thread.new do
|
29
|
+
catch(:stop_thread) do
|
30
|
+
loop do
|
31
|
+
@jobs.pop.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|