shopify-cli 2.9.0 → 2.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -2
- data/Gemfile.lock +1 -1
- data/lib/project_types/script/layers/domain/errors.rb +3 -2
- data/lib/project_types/script/layers/domain/script_config.rb +6 -4
- data/lib/project_types/script/layers/infrastructure/errors.rb +37 -24
- data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +28 -28
- data/lib/project_types/script/layers/infrastructure/script_service.rb +22 -5
- data/lib/project_types/script/messages/messages.rb +15 -17
- data/lib/project_types/script/ui/error_handler.rb +41 -29
- data/lib/project_types/theme/commands/pull.rb +6 -1
- data/lib/project_types/theme/commands/push.rb +6 -1
- data/lib/project_types/theme/messages/messages.rb +4 -0
- data/lib/shopify_cli/commands/login.rb +4 -10
- data/lib/shopify_cli/constants.rb +6 -2
- data/lib/shopify_cli/core/executor.rb +4 -4
- data/lib/shopify_cli/environment.rb +35 -16
- data/lib/shopify_cli/identity_auth.rb +3 -3
- data/lib/shopify_cli/messages/messages.rb +1 -1
- data/lib/shopify_cli/method_object.rb +21 -9
- data/lib/shopify_cli/result.rb +61 -59
- data/lib/shopify_cli/task.rb +5 -3
- data/lib/shopify_cli/theme/dev_server/cdn/cdn_helper.rb +49 -0
- data/lib/shopify_cli/theme/dev_server/cdn_assets.rb +69 -0
- data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +8 -28
- data/lib/shopify_cli/theme/dev_server/local_assets.rb +4 -0
- data/lib/shopify_cli/theme/dev_server.rb +2 -0
- 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/version.rb +1 -1
- data/lib/shopify_cli.rb +2 -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 +12 -2
@@ -15,6 +15,8 @@ module ShopifyCLI
|
|
15
15
|
|
16
16
|
def call(*)
|
17
17
|
shop = (options.flags[:shop] || @ctx.getenv("SHOPIFY_SHOP" || nil))
|
18
|
+
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop, context: @ctx)) unless shop.nil?
|
19
|
+
|
18
20
|
if shop.nil? && Shopifolk.check
|
19
21
|
Shopifolk.reset
|
20
22
|
@ctx.puts(@ctx.message("core.tasks.select_org_and_shop.identified_as_shopify"))
|
@@ -31,25 +33,17 @@ module ShopifyCLI
|
|
31
33
|
IdentityAuth.new(ctx: @ctx).authenticate
|
32
34
|
org = select_organization
|
33
35
|
ShopifyCLI::DB.set(organization_id: org["id"].to_i) unless org.nil?
|
34
|
-
|
36
|
+
Whoami.call([], "whoami")
|
35
37
|
end
|
36
|
-
# validate that shop belongs to organization
|
37
|
-
ShopifyCLI::DB.set(shop: self.class.validate_shop(shop: shop, org: org, context: @ctx)) unless shop.nil?
|
38
|
-
Whoami.call([], "whoami")
|
39
38
|
end
|
40
39
|
|
41
40
|
def self.help
|
42
41
|
ShopifyCLI::Context.message("core.login.help", ShopifyCLI::TOOL_NAME)
|
43
42
|
end
|
44
43
|
|
45
|
-
def self.validate_shop(shop
|
44
|
+
def self.validate_shop(shop, context:)
|
46
45
|
permanent_domain = shop_to_permanent_domain(shop)
|
47
46
|
context.abort(context.message("core.login.invalid_shop", shop)) unless permanent_domain
|
48
|
-
if org
|
49
|
-
stores_owned = org["stores"]
|
50
|
-
is_verified = stores_owned.any? { |store| store["shopDomain"] == permanent_domain }
|
51
|
-
context.abort(context.message("core.login.invalid_shop", shop)) unless is_verified
|
52
|
-
end
|
53
47
|
permanent_domain
|
54
48
|
end
|
55
49
|
|
@@ -36,12 +36,16 @@ module ShopifyCLI
|
|
36
36
|
# the partners dashboard and identity.
|
37
37
|
LOCAL_PARTNERS = "SHOPIFY_APP_CLI_LOCAL_PARTNERS"
|
38
38
|
|
39
|
-
# When true the CLI points to
|
40
|
-
|
39
|
+
# When true the CLI points to spin instances of services
|
40
|
+
SPIN = "SPIN"
|
41
|
+
INFER_SPIN = "INFER_SPIN"
|
41
42
|
SPIN_WORKSPACE = "SPIN_WORKSPACE"
|
42
43
|
SPIN_NAMESPACE = "SPIN_NAMESPACE"
|
43
44
|
SPIN_HOST = "SPIN_HOST"
|
44
45
|
|
46
|
+
# Deprecated, equivalent to using SPIN=1
|
47
|
+
SPIN_PARTNERS = "SHOPIFY_APP_CLI_SPIN_PARTNERS"
|
48
|
+
|
45
49
|
# Environments
|
46
50
|
TEST = "SHOPIFY_CLI_TEST"
|
47
51
|
ACCEPTANCE_TEST = "SHOPIFY_CLI_ACCEPTANCE_TEST"
|
@@ -3,10 +3,10 @@ require "shopify_cli"
|
|
3
3
|
module ShopifyCLI
|
4
4
|
module Core
|
5
5
|
class Executor < CLI::Kit::Executor
|
6
|
-
def initialize(ctx, task_registry, *args
|
7
|
-
@ctx = ctx ||
|
8
|
-
@task_registry = task_registry ||
|
9
|
-
super(*args
|
6
|
+
ruby2_keywords def initialize(ctx, task_registry, *args)
|
7
|
+
@ctx = ctx || ShopifyCli::Context.new
|
8
|
+
@task_registry = task_registry || ShopifyCli::Tasks::TaskRegistry.new
|
9
|
+
super(*args)
|
10
10
|
end
|
11
11
|
|
12
12
|
def call(command, command_name, args)
|
@@ -61,17 +61,10 @@ module ShopifyCLI
|
|
61
61
|
)
|
62
62
|
end
|
63
63
|
|
64
|
-
def self.use_spin_partners_instance?(env_variables: ENV)
|
65
|
-
env_variable_truthy?(
|
66
|
-
Constants::EnvironmentVariables::SPIN_PARTNERS,
|
67
|
-
env_variables: env_variables
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
64
|
def self.partners_domain(env_variables: ENV)
|
72
65
|
if use_local_partners_instance?(env_variables: env_variables)
|
73
66
|
"partners.myshopify.io"
|
74
|
-
elsif
|
67
|
+
elsif use_spin?(env_variables: env_variables)
|
75
68
|
"partners.#{spin_url(env_variables: env_variables)}"
|
76
69
|
else
|
77
70
|
"partners.shopify.com"
|
@@ -79,15 +72,31 @@ module ShopifyCLI
|
|
79
72
|
end
|
80
73
|
|
81
74
|
def self.use_spin?(env_variables: ENV)
|
82
|
-
|
83
|
-
|
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
|
+
)
|
84
89
|
end
|
85
90
|
|
86
91
|
def self.spin_url(env_variables: ENV)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
91
100
|
end
|
92
101
|
|
93
102
|
def self.send_monorail_events?(env_variables: ENV)
|
@@ -106,11 +115,21 @@ module ShopifyCLI
|
|
106
115
|
end
|
107
116
|
|
108
117
|
def self.spin_workspace(env_variables: ENV)
|
109
|
-
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
|
110
124
|
end
|
111
125
|
|
112
126
|
def self.spin_namespace(env_variables: ENV)
|
113
|
-
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
|
114
133
|
end
|
115
134
|
|
116
135
|
def self.spin_host(env_variables: ENV)
|
@@ -256,7 +256,7 @@ module ShopifyCLI
|
|
256
256
|
def auth_url
|
257
257
|
if Environment.use_local_partners_instance?
|
258
258
|
"https://identity.myshopify.io/oauth"
|
259
|
-
elsif Environment.
|
259
|
+
elsif Environment.use_spin?
|
260
260
|
"https://identity.#{Environment.spin_url}/oauth"
|
261
261
|
else
|
262
262
|
"https://accounts.shopify.com/oauth"
|
@@ -264,7 +264,7 @@ module ShopifyCLI
|
|
264
264
|
end
|
265
265
|
|
266
266
|
def client_id_for_application(application_name)
|
267
|
-
client_ids = if Environment.use_local_partners_instance? || Environment.
|
267
|
+
client_ids = if Environment.use_local_partners_instance? || Environment.use_spin?
|
268
268
|
DEV_APPLICATION_CLIENT_IDS
|
269
269
|
else
|
270
270
|
APPLICATION_CLIENT_IDS
|
@@ -280,7 +280,7 @@ module ShopifyCLI
|
|
280
280
|
end
|
281
281
|
|
282
282
|
def client_id
|
283
|
-
if Environment.use_local_partners_instance? || Environment.
|
283
|
+
if Environment.use_local_partners_instance? || Environment.use_spin?
|
284
284
|
Constants::Identity::CLIENT_ID_DEV
|
285
285
|
else
|
286
286
|
# In the future we might want to use Identity's dynamic
|
@@ -415,7 +415,7 @@ module ShopifyCLI
|
|
415
415
|
Usage: {{command:%s login [--store=STORE]}}
|
416
416
|
HELP
|
417
417
|
invalid_shop: <<~MESSAGE,
|
418
|
-
Invalid store provided (%s). Please
|
418
|
+
Invalid store provided (%s). Please provide the store in the following format: my-store.myshopify.com
|
419
419
|
MESSAGE
|
420
420
|
shop_prompt: <<~PROMPT,
|
421
421
|
What store are you connecting to? (e.g. my-store.myshopify.com; do {{bold:NOT}} include protocol part, e.g., https://)
|
@@ -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
|
|
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
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ShopifyCLI
|
4
|
+
module Theme
|
5
|
+
module DevServer
|
6
|
+
module Cdn
|
7
|
+
module CdnHelper
|
8
|
+
def proxy_request(env, uri, theme)
|
9
|
+
response = Net::HTTP.start(uri.host, 443, use_ssl: true) do |http|
|
10
|
+
req_class = Net::HTTP.const_get(method(env))
|
11
|
+
|
12
|
+
req = req_class.new(uri)
|
13
|
+
req.initialize_http_header(req_headers(theme))
|
14
|
+
req.body_stream = req_body(env)
|
15
|
+
|
16
|
+
http.request(req)
|
17
|
+
end
|
18
|
+
|
19
|
+
[
|
20
|
+
response.code.to_s,
|
21
|
+
{
|
22
|
+
"Content-Type" => response.content_type,
|
23
|
+
"Content-Length" => response.content_length.to_s,
|
24
|
+
},
|
25
|
+
[response.body],
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def method(env)
|
32
|
+
env["REQUEST_METHOD"].capitalize
|
33
|
+
end
|
34
|
+
|
35
|
+
def req_body(env)
|
36
|
+
env["rack.input"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def req_headers(theme)
|
40
|
+
{
|
41
|
+
"Referer" => "https://#{theme.shop}",
|
42
|
+
"Transfer-Encoding" => "chunked",
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "cdn/cdn_helper"
|
4
|
+
require_relative "local_assets"
|
5
|
+
|
6
|
+
module ShopifyCLI
|
7
|
+
module Theme
|
8
|
+
module DevServer
|
9
|
+
class CdnAssets
|
10
|
+
include Cdn::CdnHelper
|
11
|
+
|
12
|
+
ASSETS_PROXY_PATH = "/cdn_asset"
|
13
|
+
ASSETS_CDN = "//cdn.shopify.com"
|
14
|
+
ASSETS_CDN_REGEX = %r{(https?:)?#{ASSETS_CDN}}
|
15
|
+
ASSETS_SOURCE_MAP_REGEX = /\/[\/|\*]# sourceMappingURL\=(\/.*)/
|
16
|
+
|
17
|
+
def initialize(app, theme:)
|
18
|
+
@app = app
|
19
|
+
@theme = theme
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
path = env["PATH_INFO"]
|
24
|
+
|
25
|
+
# Serve assets from CDN
|
26
|
+
return serve_asset(env, path) if path.start_with?(ASSETS_PROXY_PATH)
|
27
|
+
|
28
|
+
# Proxy the request, and replace the URLs in the response
|
29
|
+
status, headers, body = @app.call(env)
|
30
|
+
body = replace_asset_urls(body)
|
31
|
+
[status, headers, body]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def serve_asset(env, path)
|
37
|
+
path = path.gsub(%r{^#{ASSETS_PROXY_PATH}}, "")
|
38
|
+
query = env["QUERY_STRING"]
|
39
|
+
uri = asset_cdn_uri(path, query)
|
40
|
+
|
41
|
+
status, headers, body = proxy_request(env, uri, @theme)
|
42
|
+
|
43
|
+
[status, headers, replace_source_map_url(body)]
|
44
|
+
end
|
45
|
+
|
46
|
+
def asset_cdn_uri(path, query)
|
47
|
+
uri = URI.join("https:#{ASSETS_CDN}", path)
|
48
|
+
uri.query = query.split("&").last
|
49
|
+
uri
|
50
|
+
end
|
51
|
+
|
52
|
+
def replace_asset_urls(body)
|
53
|
+
[body.join.gsub(ASSETS_CDN_REGEX, ASSETS_PROXY_PATH)]
|
54
|
+
end
|
55
|
+
|
56
|
+
def replace_source_map_url(body)
|
57
|
+
body_content = body.join
|
58
|
+
map_regex_match = body_content.match(ASSETS_SOURCE_MAP_REGEX)
|
59
|
+
return body if map_regex_match.nil?
|
60
|
+
|
61
|
+
map_url = map_regex_match[1]
|
62
|
+
return body if map_url.nil?
|
63
|
+
|
64
|
+
[body_content.gsub(map_url, "#{ASSETS_PROXY_PATH}#{map_url}")]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "cdn/cdn_helper"
|
4
|
+
|
3
5
|
module ShopifyCLI
|
4
6
|
module Theme
|
5
7
|
module DevServer
|
6
8
|
class CdnFonts
|
9
|
+
include Cdn::CdnHelper
|
10
|
+
|
7
11
|
FONTS_PATH = "/fonts"
|
8
12
|
FONTS_CDN = "https://fonts.shopifycdn.com"
|
9
13
|
FONTS_REGEX = %r{#{FONTS_CDN}}
|
@@ -17,7 +21,7 @@ module ShopifyCLI
|
|
17
21
|
path = env["PATH_INFO"]
|
18
22
|
|
19
23
|
# Serve from fonts CDN
|
20
|
-
return serve_font(env) if path.start_with?(FONTS_PATH)
|
24
|
+
return serve_font(env, path) if path.start_with?(FONTS_PATH)
|
21
25
|
|
22
26
|
# Proxy the request, and replace the URLs in the response
|
23
27
|
status, headers, body = @app.call(env)
|
@@ -27,35 +31,11 @@ module ShopifyCLI
|
|
27
31
|
|
28
32
|
private
|
29
33
|
|
30
|
-
def serve_font(env)
|
31
|
-
|
32
|
-
path, query, method, body_stream = *env.slice(*parameters).values
|
33
|
-
|
34
|
+
def serve_font(env, path)
|
35
|
+
query = env["QUERY_STRING"]
|
34
36
|
uri = fonts_cdn_uri(path, query)
|
35
37
|
|
36
|
-
|
37
|
-
req_class = Net::HTTP.const_get(method.capitalize)
|
38
|
-
req = req_class.new(uri)
|
39
|
-
req.initialize_http_header(fonts_cdn_headers)
|
40
|
-
req.body_stream = body_stream
|
41
|
-
http.request(req)
|
42
|
-
end
|
43
|
-
|
44
|
-
[
|
45
|
-
response.code.to_s,
|
46
|
-
{
|
47
|
-
"Content-Type" => response.content_type,
|
48
|
-
"Content-Length" => response.content_length.to_s,
|
49
|
-
},
|
50
|
-
[response.body],
|
51
|
-
]
|
52
|
-
end
|
53
|
-
|
54
|
-
def fonts_cdn_headers
|
55
|
-
{
|
56
|
-
"Referer" => "https://#{@theme.shop}",
|
57
|
-
"Transfer-Encoding" => "chunked",
|
58
|
-
}
|
38
|
+
proxy_request(env, uri, @theme)
|
59
39
|
end
|
60
40
|
|
61
41
|
def fonts_cdn_uri(path, query)
|
@@ -3,6 +3,7 @@ require_relative "development_theme"
|
|
3
3
|
require_relative "ignore_filter"
|
4
4
|
require_relative "syncer"
|
5
5
|
|
6
|
+
require_relative "dev_server/cdn_assets"
|
6
7
|
require_relative "dev_server/cdn_fonts"
|
7
8
|
require_relative "dev_server/hot_reload"
|
8
9
|
require_relative "dev_server/header_hash"
|
@@ -37,6 +38,7 @@ module ShopifyCLI
|
|
37
38
|
@app = Proxy.new(ctx, theme: theme, syncer: @syncer)
|
38
39
|
@app = CdnFonts.new(@app, theme: theme)
|
39
40
|
@app = LocalAssets.new(ctx, @app, theme: theme)
|
41
|
+
@app = CdnAssets.new(@app, theme: theme)
|
40
42
|
@app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, mode: mode, ignore_filter: ignore_filter)
|
41
43
|
stopped = false
|
42
44
|
address = "http://#{host}:#{port}"
|
@@ -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
|