shopify-cli 2.7.4 → 2.10.1
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/CHANGELOG.md +33 -0
- data/Gemfile.lock +1 -1
- data/RELEASING.md +4 -3
- data/ext/javy/javy.rb +1 -1
- data/lib/project_types/extension/commands/push.rb +2 -2
- data/lib/project_types/extension/messages/messages.rb +1 -1
- data/lib/project_types/extension/models/development_server.rb +2 -4
- data/lib/project_types/rails/gem.rb +1 -2
- data/lib/project_types/script/cli.rb +5 -0
- data/lib/project_types/script/commands/connect.rb +1 -1
- data/lib/project_types/script/commands/create.rb +8 -2
- data/lib/project_types/script/commands/push.rb +35 -12
- data/lib/project_types/script/graphql/app_script_set.graphql +2 -0
- data/lib/project_types/script/layers/application/build_script.rb +0 -1
- data/lib/project_types/script/layers/application/connect_app.rb +11 -5
- data/lib/project_types/script/layers/application/extension_points.rb +50 -26
- data/lib/project_types/script/layers/application/push_script.rb +6 -3
- data/lib/project_types/script/layers/domain/errors.rb +3 -2
- data/lib/project_types/script/layers/domain/extension_point.rb +14 -0
- data/lib/project_types/script/layers/domain/push_package.rb +0 -3
- data/lib/project_types/script/layers/domain/script_config.rb +6 -4
- data/lib/project_types/script/layers/domain/script_project.rb +1 -0
- data/lib/project_types/script/layers/infrastructure/errors.rb +38 -23
- data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -4
- data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +0 -4
- data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +6 -7
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -54
- data/lib/project_types/script/layers/infrastructure/script_service.rb +25 -6
- data/lib/project_types/script/loaders/project.rb +44 -0
- data/lib/project_types/script/loaders/specification_handler.rb +22 -0
- data/lib/project_types/script/messages/messages.rb +28 -16
- data/lib/project_types/script/ui/error_handler.rb +46 -29
- data/lib/project_types/theme/commands/pull.rb +45 -17
- data/lib/project_types/theme/commands/push.rb +62 -27
- data/lib/project_types/theme/commands/serve.rb +5 -0
- data/lib/project_types/theme/messages/messages.rb +33 -18
- data/lib/shopify_cli/commands/login.rb +1 -1
- data/lib/shopify_cli/commands/switch.rb +1 -1
- data/lib/shopify_cli/constants.rb +7 -2
- data/lib/shopify_cli/context.rb +66 -12
- data/lib/shopify_cli/core/executor.rb +4 -4
- data/lib/shopify_cli/environment.rb +50 -20
- data/lib/shopify_cli/identity_auth.rb +4 -3
- data/lib/shopify_cli/messages/messages.rb +2 -0
- data/lib/shopify_cli/method_object.rb +21 -9
- data/lib/shopify_cli/resources/env_file.rb +5 -1
- data/lib/shopify_cli/result.rb +61 -59
- data/lib/shopify_cli/task.rb +5 -3
- data/lib/shopify_cli/theme/dev_server/hot-reload.js +19 -1
- data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
- data/lib/shopify_cli/theme/dev_server/proxy.rb +1 -0
- data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
- data/lib/shopify_cli/theme/dev_server.rb +6 -21
- data/lib/shopify_cli/theme/file.rb +2 -2
- data/lib/shopify_cli/theme/filter/path_matcher.rb +38 -0
- data/lib/shopify_cli/theme/ignore_filter.rb +14 -18
- data/lib/shopify_cli/theme/include_filter.rb +43 -0
- data/lib/shopify_cli/theme/syncer.rb +17 -2
- data/lib/shopify_cli/theme/theme.rb +26 -4
- data/lib/shopify_cli/version.rb +1 -1
- data/lib/shopify_cli.rb +6 -1
- data/vendor/deps/ruby2_keywords/LICENSE +22 -0
- data/vendor/deps/ruby2_keywords/README.md +67 -0
- data/vendor/deps/ruby2_keywords/Rakefile +54 -0
- data/vendor/deps/ruby2_keywords/lib/ruby2_keywords.rb +57 -0
- data/vendor/deps/ruby2_keywords/ruby2_keywords.gemspec +18 -0
- data/vendor/deps/ruby2_keywords/test/test_keyword.rb +41 -0
- metadata +13 -2
@@ -4,6 +4,21 @@ module ShopifyCLI
|
|
4
4
|
module Environment
|
5
5
|
TRUTHY_ENV_VARIABLE_VALUES = ["1", "true", "TRUE", "yes", "YES"]
|
6
6
|
|
7
|
+
def self.interactive=(interactive)
|
8
|
+
@interactive = interactive
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.interactive?(env_variables: ENV)
|
12
|
+
if env_variables.key?(Constants::EnvironmentVariables::TTY)
|
13
|
+
env_variable_truthy?(
|
14
|
+
Constants::EnvironmentVariables::TTY,
|
15
|
+
env_variables: env_variables
|
16
|
+
)
|
17
|
+
else
|
18
|
+
@interactive ||= STDIN.tty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
7
22
|
def self.development?(env_variables: ENV)
|
8
23
|
env_variable_truthy?(
|
9
24
|
Constants::EnvironmentVariables::DEVELOPMENT,
|
@@ -11,10 +26,6 @@ module ShopifyCLI
|
|
11
26
|
)
|
12
27
|
end
|
13
28
|
|
14
|
-
def self.interactive?
|
15
|
-
ShopifyCLI::Context.new.tty?
|
16
|
-
end
|
17
|
-
|
18
29
|
def self.use_local_partners_instance?(env_variables: ENV)
|
19
30
|
env_variable_truthy?(
|
20
31
|
Constants::EnvironmentVariables::LOCAL_PARTNERS,
|
@@ -50,17 +61,10 @@ module ShopifyCLI
|
|
50
61
|
)
|
51
62
|
end
|
52
63
|
|
53
|
-
def self.use_spin_partners_instance?(env_variables: ENV)
|
54
|
-
env_variable_truthy?(
|
55
|
-
Constants::EnvironmentVariables::SPIN_PARTNERS,
|
56
|
-
env_variables: env_variables
|
57
|
-
)
|
58
|
-
end
|
59
|
-
|
60
64
|
def self.partners_domain(env_variables: ENV)
|
61
65
|
if use_local_partners_instance?(env_variables: env_variables)
|
62
66
|
"partners.myshopify.io"
|
63
|
-
elsif
|
67
|
+
elsif use_spin?(env_variables: env_variables)
|
64
68
|
"partners.#{spin_url(env_variables: env_variables)}"
|
65
69
|
else
|
66
70
|
"partners.shopify.com"
|
@@ -68,15 +72,31 @@ module ShopifyCLI
|
|
68
72
|
end
|
69
73
|
|
70
74
|
def self.use_spin?(env_variables: ENV)
|
71
|
-
|
72
|
-
|
75
|
+
env_variable_truthy?(
|
76
|
+
Constants::EnvironmentVariables::SPIN,
|
77
|
+
env_variables: env_variables
|
78
|
+
) || env_variable_truthy?(
|
79
|
+
Constants::EnvironmentVariables::SPIN_PARTNERS,
|
80
|
+
env_variables: env_variables
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.infer_spin?(env_variables: ENV)
|
85
|
+
env_variable_truthy?(
|
86
|
+
Constants::EnvironmentVariables::INFER_SPIN,
|
87
|
+
env_variables: env_variables
|
88
|
+
)
|
73
89
|
end
|
74
90
|
|
75
91
|
def self.spin_url(env_variables: ENV)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
92
|
+
if infer_spin?(env_variables: env_variables)
|
93
|
+
%x(spin info fqdn 2> /dev/null).strip
|
94
|
+
else
|
95
|
+
spin_workspace = spin_workspace(env_variables: env_variables)
|
96
|
+
spin_namespace = spin_namespace(env_variables: env_variables)
|
97
|
+
spin_host = spin_host(env_variables: env_variables)
|
98
|
+
"#{spin_workspace}.#{spin_namespace}.#{spin_host}"
|
99
|
+
end
|
80
100
|
end
|
81
101
|
|
82
102
|
def self.send_monorail_events?(env_variables: ENV)
|
@@ -95,11 +115,21 @@ module ShopifyCLI
|
|
95
115
|
end
|
96
116
|
|
97
117
|
def self.spin_workspace(env_variables: ENV)
|
98
|
-
env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
|
118
|
+
env_value = env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
|
119
|
+
return env_value unless env_value.nil?
|
120
|
+
|
121
|
+
if env_value.nil?
|
122
|
+
raise "No value set for #{Constants::EnvironmentVariables::SPIN_WORKSPACE}"
|
123
|
+
end
|
99
124
|
end
|
100
125
|
|
101
126
|
def self.spin_namespace(env_variables: ENV)
|
102
|
-
env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
|
127
|
+
env_value = env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
|
128
|
+
return env_value unless env_value.nil?
|
129
|
+
|
130
|
+
if env_value.nil?
|
131
|
+
raise "No value set for #{Constants::EnvironmentVariables::SPIN_NAMESPACE}"
|
132
|
+
end
|
103
133
|
end
|
104
134
|
|
105
135
|
def self.spin_host(env_variables: ENV)
|
@@ -229,6 +229,7 @@ module ShopifyCLI
|
|
229
229
|
uri = URI.parse("#{auth_url}#{endpoint}")
|
230
230
|
https = Net::HTTP.new(uri.host, uri.port)
|
231
231
|
https.use_ssl = true
|
232
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE if ENV["SSL_VERIFY_NONE"]
|
232
233
|
request = Net::HTTP::Post.new(uri.path)
|
233
234
|
request["User-Agent"] = "Shopify CLI #{::ShopifyCLI::VERSION}"
|
234
235
|
request.body = URI.encode_www_form(params)
|
@@ -255,7 +256,7 @@ module ShopifyCLI
|
|
255
256
|
def auth_url
|
256
257
|
if Environment.use_local_partners_instance?
|
257
258
|
"https://identity.myshopify.io/oauth"
|
258
|
-
elsif Environment.
|
259
|
+
elsif Environment.use_spin?
|
259
260
|
"https://identity.#{Environment.spin_url}/oauth"
|
260
261
|
else
|
261
262
|
"https://accounts.shopify.com/oauth"
|
@@ -263,7 +264,7 @@ module ShopifyCLI
|
|
263
264
|
end
|
264
265
|
|
265
266
|
def client_id_for_application(application_name)
|
266
|
-
client_ids = if Environment.use_local_partners_instance? || Environment.
|
267
|
+
client_ids = if Environment.use_local_partners_instance? || Environment.use_spin?
|
267
268
|
DEV_APPLICATION_CLIENT_IDS
|
268
269
|
else
|
269
270
|
APPLICATION_CLIENT_IDS
|
@@ -279,7 +280,7 @@ module ShopifyCLI
|
|
279
280
|
end
|
280
281
|
|
281
282
|
def client_id
|
282
|
-
if Environment.use_local_partners_instance? || Environment.
|
283
|
+
if Environment.use_local_partners_instance? || Environment.use_spin?
|
283
284
|
Constants::Identity::CLIENT_ID_DEV
|
284
285
|
else
|
285
286
|
# In the future we might want to use Identity's dynamic
|
@@ -790,6 +790,8 @@ module ShopifyCLI
|
|
790
790
|
logged_in_partner_only: "Logged into partner organization {{green:%s}}",
|
791
791
|
logged_in_partner_and_shop: "Logged into store {{green:%s}} in partner organization {{green:%s}}",
|
792
792
|
},
|
793
|
+
error: "Error",
|
794
|
+
try_this: "Try this",
|
793
795
|
},
|
794
796
|
}.freeze
|
795
797
|
end
|
@@ -66,15 +66,27 @@ module ShopifyCLI
|
|
66
66
|
# initializer or to `call`. If the keyword argument matches the name of
|
67
67
|
# property, it is forwarded to the initializer, otherwise to call.
|
68
68
|
#
|
69
|
-
def call(*args,
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
69
|
+
ruby2_keywords def call(*args, &block)
|
70
|
+
# This is an extremely complicated case of delegation. The method wants
|
71
|
+
# to delegate arguments, but to have control over which keyword
|
72
|
+
# arguments are delegated. I'm not sure the forward and backward
|
73
|
+
# compatibility of this unusual form of delegation has really been
|
74
|
+
# explored or there's any good way to support it. So I have done
|
75
|
+
# done something hacky here and I'm looking at the last argument and
|
76
|
+
# modifying the package of arguments to be delegated in-place.
|
77
|
+
if args.last.is_a?(Hash)
|
78
|
+
kwargs = args.last
|
79
|
+
|
80
|
+
initializer_kwargs = kwargs.slice(*properties.keys)
|
81
|
+
instance = new(**initializer_kwargs)
|
82
|
+
|
83
|
+
kwargs.reject! { |key| initializer_kwargs.key?(key) }
|
84
|
+
args.pop if kwargs.empty?
|
85
|
+
instance.call(*args, &block)
|
86
|
+
else
|
87
|
+
# Since the former is so complicated - let's have a fast path that
|
88
|
+
# is much simpler.
|
89
|
+
new.call(*args, &block)
|
78
90
|
end
|
79
91
|
end
|
80
92
|
|
@@ -14,6 +14,10 @@ module ShopifyCLI
|
|
14
14
|
}
|
15
15
|
|
16
16
|
class << self
|
17
|
+
def path(directory)
|
18
|
+
File.join(directory, FILENAME)
|
19
|
+
end
|
20
|
+
|
17
21
|
def read(_directory = Dir.pwd, overrides: {})
|
18
22
|
input = parse_external_env(overrides: overrides)
|
19
23
|
new(input)
|
@@ -24,7 +28,7 @@ module ShopifyCLI
|
|
24
28
|
end
|
25
29
|
|
26
30
|
def parse(directory)
|
27
|
-
File.read(
|
31
|
+
File.read(path(directory))
|
28
32
|
.gsub("\r\n", "\n").split("\n").each_with_object({}) do |line, output|
|
29
33
|
match = /\A([A-Za-z_0-9]+)\s*=\s*(.*)\z/.match(line)
|
30
34
|
if match
|
data/lib/shopify_cli/result.rb
CHANGED
@@ -363,70 +363,72 @@ module ShopifyCLI
|
|
363
363
|
Result::Failure.new(error)
|
364
364
|
end
|
365
365
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
366
|
+
class << self
|
367
|
+
##
|
368
|
+
# takes either a value or a block and chooses the appropriate result
|
369
|
+
# container based on the type of the value or the type of the block's return
|
370
|
+
# value. If the type is an exception, it is wrapped in a
|
371
|
+
# `ShopifyCli::Result::Failure` and otherwise in a
|
372
|
+
# `ShopifyCli::Result::Success`. If a block was provided instead of value, a
|
373
|
+
# `Proc` is returned and the result wrapping doesn't occur until the block
|
374
|
+
# is invoked.
|
375
|
+
#
|
376
|
+
# #### Parameters
|
377
|
+
#
|
378
|
+
# * `*args` should be an `Array` with zero or one element
|
379
|
+
# * `&block` should be a `Proc` that takes zero or one argument
|
380
|
+
#
|
381
|
+
# #### Returns
|
382
|
+
#
|
383
|
+
# Returns either a `Result::Success`, `Result::Failure` or a `Proc` that
|
384
|
+
# produces one of the former when invoked.
|
385
|
+
#
|
386
|
+
# #### Examples
|
387
|
+
#
|
388
|
+
# Result.wrap(1) # => ShopifyCli::Result::Success
|
389
|
+
# Result.wrap(RuntimeError.new) # => ShopifyCli::Result::Failure
|
390
|
+
#
|
391
|
+
# Result.wrap { 1 } # => Proc
|
392
|
+
# Result.wrap { 1 }.call # => ShopifyCli::Result::Success
|
393
|
+
# Result.wrap { raise }.call # => ShopifyCli::Result::Failure
|
394
|
+
#
|
395
|
+
# Result.wrap { |s| s.upcase }.call("hello").tap do |result|
|
396
|
+
# result # => Result::Success
|
397
|
+
# result.value # => "HELLO"
|
398
|
+
# end
|
399
|
+
#
|
400
|
+
ruby2_keywords def wrap(*values, &block)
|
401
|
+
raise ArgumentError, "expected either a value or a block" unless (values.length == 1) ^ block
|
401
402
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
413
|
-
else
|
414
|
-
->(*args) do
|
415
|
-
begin
|
416
|
-
wrap(block.call(*args))
|
417
|
-
rescue Exception => error # rubocop:disable Lint/RescueException
|
418
|
-
wrap(error)
|
403
|
+
if values.length == 1
|
404
|
+
values.pop.yield_self do |value|
|
405
|
+
case value
|
406
|
+
when Result::Success, Result::Failure
|
407
|
+
value
|
408
|
+
when NilClass, Exception
|
409
|
+
Result.failure(value)
|
410
|
+
else
|
411
|
+
Result.success(value)
|
412
|
+
end
|
419
413
|
end
|
414
|
+
else
|
415
|
+
->(*args) do
|
416
|
+
begin
|
417
|
+
wrap(block.call(*args))
|
418
|
+
rescue Exception => error # rubocop:disable Lint/RescueException
|
419
|
+
wrap(error)
|
420
|
+
end
|
421
|
+
end.ruby2_keywords
|
420
422
|
end
|
421
423
|
end
|
422
|
-
end
|
423
424
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
425
|
+
##
|
426
|
+
# Wraps the given block and invokes it with the passed arguments.
|
427
|
+
#
|
428
|
+
ruby2_keywords def call(*args, &block)
|
429
|
+
raise ArgumentError, "expected a block" unless block
|
430
|
+
wrap(&block).call(*args)
|
431
|
+
end
|
430
432
|
end
|
431
433
|
end
|
432
434
|
end
|
data/lib/shopify_cli/task.rb
CHANGED
@@ -15,9 +15,23 @@
|
|
15
15
|
eventSource.onerror = () => eventSource.close();
|
16
16
|
}
|
17
17
|
|
18
|
-
|
18
|
+
function reloadMode() {
|
19
|
+
var namespace = window.__SHOPIFY_CLI_ENV__;
|
20
|
+
return namespace.mode;
|
21
|
+
}
|
22
|
+
|
23
|
+
function isFullPageReloadMode(){
|
24
|
+
return reloadMode() === "full-page";
|
25
|
+
}
|
26
|
+
|
27
|
+
function isReloadModeActive(){
|
28
|
+
return reloadMode() !== "off";
|
29
|
+
}
|
19
30
|
|
20
31
|
function isRefreshRequired(files) {
|
32
|
+
if (isFullPageReloadMode()) {
|
33
|
+
return true;
|
34
|
+
}
|
21
35
|
return files.some((file) => !isCssFile(file) && !isSectionFile(file));
|
22
36
|
}
|
23
37
|
|
@@ -119,4 +133,8 @@
|
|
119
133
|
}
|
120
134
|
}
|
121
135
|
}
|
136
|
+
|
137
|
+
if (isReloadModeActive()) {
|
138
|
+
connect();
|
139
|
+
}
|
122
140
|
})();
|
@@ -4,10 +4,11 @@ module ShopifyCLI
|
|
4
4
|
module Theme
|
5
5
|
module DevServer
|
6
6
|
class HotReload
|
7
|
-
def initialize(ctx, app, theme:, watcher:, ignore_filter: nil)
|
7
|
+
def initialize(ctx, app, theme:, watcher:, mode:, ignore_filter: nil)
|
8
8
|
@ctx = ctx
|
9
9
|
@app = app
|
10
10
|
@theme = theme
|
11
|
+
@mode = mode
|
11
12
|
@streams = SSE::Streams.new
|
12
13
|
@watcher = watcher
|
13
14
|
@watcher.add_observer(self, :notify_streams_of_file_change)
|
@@ -48,12 +49,27 @@ module ShopifyCLI
|
|
48
49
|
|
49
50
|
def inject_hot_reload_javascript(body)
|
50
51
|
hot_reload_js = ::File.read("#{__dir__}/hot-reload.js")
|
51
|
-
hot_reload_script =
|
52
|
+
hot_reload_script = [
|
53
|
+
"<script>",
|
54
|
+
params_js,
|
55
|
+
hot_reload_js,
|
56
|
+
"</script>",
|
57
|
+
].join("\n")
|
58
|
+
|
52
59
|
body = body.join.gsub("</body>", "#{hot_reload_script}\n</body>")
|
53
60
|
|
54
61
|
[body]
|
55
62
|
end
|
56
63
|
|
64
|
+
def params_js
|
65
|
+
env = { mode: @mode }
|
66
|
+
<<~JS
|
67
|
+
(() => {
|
68
|
+
window.__SHOPIFY_CLI_ENV__ = #{env.to_json};
|
69
|
+
})();
|
70
|
+
JS
|
71
|
+
end
|
72
|
+
|
57
73
|
def create_stream
|
58
74
|
stream = @streams.new
|
59
75
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
class ReloadMode
|
7
|
+
MODES = [:"hot-reload", :"full-page", :off]
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def default
|
11
|
+
:"hot-reload"
|
12
|
+
end
|
13
|
+
|
14
|
+
def get!(mode)
|
15
|
+
MODES.find { |m| m == mode.to_sym } || raise_error(mode)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def raise_error(invalid_mode)
|
21
|
+
error_message = ShopifyCLI::Context.message("theme.serve.reload_mode_is_not_valid", invalid_mode)
|
22
|
+
help_message = ShopifyCLI::Context.message("theme.serve.try_a_valid_reload_mode", valid_modes)
|
23
|
+
|
24
|
+
ShopifyCLI::Context.abort(error_message, help_message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid_modes
|
28
|
+
MODES.map { |v| "`#{v}`" }.join(", ")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -6,6 +6,7 @@ require_relative "syncer"
|
|
6
6
|
require_relative "dev_server/cdn_fonts"
|
7
7
|
require_relative "dev_server/hot_reload"
|
8
8
|
require_relative "dev_server/header_hash"
|
9
|
+
require_relative "dev_server/reload_mode"
|
9
10
|
require_relative "dev_server/local_assets"
|
10
11
|
require_relative "dev_server/proxy"
|
11
12
|
require_relative "dev_server/sse"
|
@@ -25,7 +26,7 @@ module ShopifyCLI
|
|
25
26
|
class << self
|
26
27
|
attr_accessor :ctx
|
27
28
|
|
28
|
-
def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false)
|
29
|
+
def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false, mode: ReloadMode.default)
|
29
30
|
@ctx = ctx
|
30
31
|
theme = DevelopmentTheme.new(ctx, root: root)
|
31
32
|
ignore_filter = IgnoreFilter.from_path(root)
|
@@ -36,7 +37,7 @@ module ShopifyCLI
|
|
36
37
|
@app = Proxy.new(ctx, theme: theme, syncer: @syncer)
|
37
38
|
@app = CdnFonts.new(@app, theme: theme)
|
38
39
|
@app = LocalAssets.new(ctx, @app, theme: theme)
|
39
|
-
@app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, ignore_filter: ignore_filter)
|
40
|
+
@app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, mode: mode, ignore_filter: ignore_filter)
|
40
41
|
stopped = false
|
41
42
|
address = "http://#{host}:#{port}"
|
42
43
|
|
@@ -83,7 +84,9 @@ module ShopifyCLI
|
|
83
84
|
ShopifyCLI::API::APIRequestUnauthorizedError
|
84
85
|
raise ShopifyCLI::Abort, @ctx.message("theme.serve.ensure_user", theme.shop)
|
85
86
|
rescue Errno::EADDRINUSE
|
86
|
-
|
87
|
+
error_message = @ctx.message("theme.serve.address_already_in_use", address)
|
88
|
+
help_message = @ctx.message("theme.serve.try_port_option")
|
89
|
+
@ctx.abort(error_message, help_message)
|
87
90
|
rescue Errno::EADDRNOTAVAIL
|
88
91
|
raise AddressBindingError, "Error binding to the address #{host}."
|
89
92
|
end
|
@@ -94,24 +97,6 @@ module ShopifyCLI
|
|
94
97
|
@syncer.shutdown
|
95
98
|
WebServer.shutdown
|
96
99
|
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def abort_address_already_in_use(address)
|
101
|
-
open_frame(@ctx.message("theme.serve.already_in_use_error"), color: :red) do
|
102
|
-
@ctx.puts(@ctx.message("theme.serve.address_already_in_use", address))
|
103
|
-
end
|
104
|
-
|
105
|
-
open_frame(@ctx.message("theme.serve.try_this"), color: :green) do
|
106
|
-
@ctx.puts(@ctx.message("theme.serve.try_port_option"))
|
107
|
-
end
|
108
|
-
|
109
|
-
raise ShopifyCLI::AbortSilent
|
110
|
-
end
|
111
|
-
|
112
|
-
def open_frame(title, color:, &block)
|
113
|
-
CLI::UI::Frame.open(title, color: CLI::UI.resolve_color(color), timing: false, &block)
|
114
|
-
end
|
115
100
|
end
|
116
101
|
end
|
117
102
|
end
|
@@ -18,7 +18,7 @@ module ShopifyCLI
|
|
18
18
|
|
19
19
|
def read
|
20
20
|
if text?
|
21
|
-
path.read
|
21
|
+
path.read(universal_newline: true)
|
22
22
|
else
|
23
23
|
path.read(mode: "rb")
|
24
24
|
end
|
@@ -27,7 +27,7 @@ module ShopifyCLI
|
|
27
27
|
def write(content)
|
28
28
|
path.parent.mkpath unless path.parent.directory?
|
29
29
|
if text?
|
30
|
-
path.write(content)
|
30
|
+
path.write(content, universal_newline: true)
|
31
31
|
else
|
32
32
|
path.write(content, 0, mode: "wb")
|
33
33
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module Filter
|
6
|
+
module PathMatcher
|
7
|
+
def regex_match?(regex, path)
|
8
|
+
regex.match?(path)
|
9
|
+
rescue StandardError
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
def glob_match?(glob, path)
|
14
|
+
!!::File.fnmatch?(glob, path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def regex?(pattern)
|
18
|
+
pattern.start_with?("/") && pattern.end_with?("/")
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_regex(pattern)
|
22
|
+
Regexp.new(pattern.gsub(%r{^\/|\/$}, ""))
|
23
|
+
end
|
24
|
+
|
25
|
+
def as_glob(pattern)
|
26
|
+
# if specifying a directory, match everything below it
|
27
|
+
pattern += "*" if pattern.end_with?("/")
|
28
|
+
|
29
|
+
# The pattern will be scoped to root directory, so it should match anything
|
30
|
+
# within that space
|
31
|
+
pattern.prepend("*") unless pattern.start_with?("*")
|
32
|
+
|
33
|
+
pattern
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "filter/path_matcher"
|
4
|
+
|
3
5
|
module ShopifyCLI
|
4
6
|
module Theme
|
5
7
|
class IgnoreFilter
|
8
|
+
include Filter::PathMatcher
|
9
|
+
|
6
10
|
FILE = ".shopifyignore"
|
7
11
|
|
8
12
|
DEFAULT_REGEXES = [
|
@@ -72,11 +76,11 @@ module ShopifyCLI
|
|
72
76
|
return true if path.empty?
|
73
77
|
|
74
78
|
regexes.each do |regex|
|
75
|
-
return true if regex
|
79
|
+
return true if regex_match?(regex, path)
|
76
80
|
end
|
77
81
|
|
78
82
|
globs.each do |glob|
|
79
|
-
return true if
|
83
|
+
return true if glob_match?(glob, path)
|
80
84
|
end
|
81
85
|
|
82
86
|
false
|
@@ -91,24 +95,16 @@ module ShopifyCLI
|
|
91
95
|
new_regexes = DEFAULT_REGEXES.dup
|
92
96
|
new_globs = DEFAULT_GLOBS.dup
|
93
97
|
|
94
|
-
patterns
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
patterns
|
99
|
+
.map(&:strip)
|
100
|
+
.each do |pattern|
|
101
|
+
if regex?(pattern)
|
102
|
+
new_regexes << as_regex(pattern)
|
103
|
+
else
|
104
|
+
new_globs << as_glob(pattern)
|
105
|
+
end
|
100
106
|
end
|
101
107
|
|
102
|
-
# if specifying a directory, match everything below it
|
103
|
-
pattern += "*" if pattern.end_with?("/")
|
104
|
-
|
105
|
-
# The pattern will be scoped to root directory, so it should match anything
|
106
|
-
# within that space
|
107
|
-
pattern.prepend("*") unless pattern.start_with?("*")
|
108
|
-
|
109
|
-
new_globs << pattern
|
110
|
-
end
|
111
|
-
|
112
108
|
[new_regexes, new_globs]
|
113
109
|
end
|
114
110
|
end
|