shopify-cli 2.7.3 → 2.7.4
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 +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
|