@shopify/cli-kit 3.42.0 → 3.44.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.
- package/assets/cli-ruby/Gemfile +6 -3
- package/assets/cli-ruby/lib/project_types/extension/commands/serve.rb +10 -0
- package/assets/cli-ruby/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +1 -1
- package/assets/cli-ruby/lib/project_types/theme/cli.rb +1 -0
- package/assets/cli-ruby/lib/project_types/theme/commands/common/root_helper.rb +3 -2
- package/assets/cli-ruby/lib/project_types/theme/commands/console.rb +15 -0
- package/assets/cli-ruby/lib/project_types/theme/commands/push.rb +1 -0
- package/assets/cli-ruby/lib/project_types/theme/messages/messages.rb +4 -2
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/hot_reload/script_injector.rb +1 -1
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/hot_reload.rb +9 -2
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/proxy.rb +13 -12
- package/assets/cli-ruby/lib/shopify_cli/theme/dev_server.rb +2 -2
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/dev_server.rb +7 -4
- package/assets/cli-ruby/lib/shopify_cli/theme/extension/host_theme.rb +19 -14
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/api.rb +107 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/auth_dev_server.rb +80 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/auth_middleware.rb +73 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/resources/success.html +79 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl/resources/template.liquid +15 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/repl.rb +145 -0
- package/assets/cli-ruby/lib/shopify_cli/theme/syncer/standard_reporter.rb +2 -2
- package/assets/cli-ruby/lib/shopify_cli/theme/syncer.rb +1 -1
- package/assets/cli-ruby/lib/shopify_cli/theme/theme.rb +6 -6
- package/dist/private/node/analytics.d.ts +2 -2
- package/dist/private/node/analytics.js.map +1 -1
- package/dist/private/node/api/graphql.js +9 -16
- package/dist/private/node/api/graphql.js.map +1 -1
- package/dist/private/node/api/headers.js +8 -5
- package/dist/private/node/api/headers.js.map +1 -1
- package/dist/private/node/api.d.ts +9 -0
- package/dist/private/node/api.js +19 -0
- package/dist/private/node/api.js.map +1 -1
- package/dist/private/node/colors.js.map +1 -0
- package/dist/private/node/conf-store.d.ts +24 -5
- package/dist/private/node/conf-store.js +21 -3
- package/dist/private/node/conf-store.js.map +1 -1
- package/dist/private/node/constants.d.ts +3 -1
- package/dist/private/node/constants.js +3 -1
- package/dist/private/node/constants.js.map +1 -1
- package/dist/private/node/content-tokens.js +1 -1
- package/dist/private/node/content-tokens.js.map +1 -1
- package/dist/private/node/semver.js.map +1 -0
- package/dist/private/node/session/identity-token-validation.d.ts +1 -1
- package/dist/private/node/session/identity-token-validation.js +45 -20
- package/dist/private/node/session/identity-token-validation.js.map +1 -1
- package/dist/private/node/session/store.js +3 -46
- package/dist/private/node/session/store.js.map +1 -1
- package/dist/private/node/session.js +13 -10
- package/dist/private/node/session.js.map +1 -1
- package/dist/private/node/themes/generate-theme-name.js.map +1 -0
- package/dist/private/node/themes/replace-invalid-characters.js.map +1 -0
- package/dist/private/node/themes/themes-api/headers.js.map +1 -0
- package/dist/private/node/themes/themes-api/retry.js.map +1 -0
- package/dist/private/node/themes/themes-api/throttler.js.map +1 -0
- package/dist/private/node/ui/components/ConcurrentOutput.d.ts +0 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +0 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/public/common/string.d.ts +16 -4
- package/dist/public/common/string.js +25 -4
- package/dist/public/common/string.js.map +1 -1
- package/dist/public/common/ts/deep-required.d.ts +0 -1
- package/dist/public/common/ts/deep-required.js.map +1 -1
- package/dist/public/common/ts/pick-by-prefix.d.ts +0 -1
- package/dist/public/common/ts/pick-by-prefix.js.map +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/base-command.d.ts +8 -5
- package/dist/public/node/base-command.js +1 -1
- package/dist/public/node/base-command.js.map +1 -1
- package/dist/public/node/cli.d.ts +1 -1
- package/dist/public/node/cli.js +0 -1
- package/dist/public/node/cli.js.map +1 -1
- package/dist/public/node/context/local.d.ts +2 -2
- package/dist/public/node/context/local.js +3 -3
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/context/spin.d.ts +14 -0
- package/dist/public/node/context/spin.js +19 -0
- package/dist/public/node/context/spin.js.map +1 -1
- package/dist/public/node/environment.d.ts +12 -0
- package/dist/public/node/environment.js +14 -0
- package/dist/public/node/environment.js.map +1 -0
- package/dist/public/node/fs.d.ts +8 -12
- package/dist/public/node/fs.js +11 -35
- package/dist/public/node/fs.js.map +1 -1
- package/dist/public/node/http.js +5 -10
- package/dist/public/node/http.js.map +1 -1
- package/dist/public/node/local-storage.d.ts +37 -0
- package/dist/public/node/local-storage.js +44 -0
- package/dist/public/node/local-storage.js.map +1 -0
- package/dist/public/node/node-package-manager.js +1 -1
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/output.d.ts +0 -1
- package/dist/public/node/output.js +1 -2
- package/dist/public/node/output.js.map +1 -1
- package/dist/public/node/path.d.ts +58 -2
- package/dist/public/node/path.js +75 -3
- package/dist/public/node/path.js.map +1 -1
- package/dist/public/node/ruby.d.ts +2 -1
- package/dist/public/node/ruby.js +52 -16
- package/dist/public/node/ruby.js.map +1 -1
- package/dist/public/node/themes/conf.d.ts +12 -0
- package/dist/public/node/themes/conf.js +24 -0
- package/dist/public/node/themes/conf.js.map +1 -0
- package/dist/public/node/themes/theme-manager.js +1 -1
- package/dist/public/node/themes/theme-manager.js.map +1 -1
- package/dist/public/node/themes/themes-api.js +3 -3
- package/dist/public/node/themes/themes-api.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/assets/cli-ruby/Gemfile.lock +0 -190
- package/dist/private/node/secure-store.d.ts +0 -19
- package/dist/private/node/secure-store.js +0 -63
- package/dist/private/node/secure-store.js.map +0 -1
- package/dist/public/node/colors.js.map +0 -1
- package/dist/public/node/conf.d.ts +0 -2
- package/dist/public/node/conf.js +0 -3
- package/dist/public/node/conf.js.map +0 -1
- package/dist/public/node/semver.js.map +0 -1
- package/dist/public/node/themes/generate-theme-name.js.map +0 -1
- package/dist/public/node/themes/replace-invalid-characters.js.map +0 -1
- package/dist/public/node/themes/themes-api/headers.js.map +0 -1
- package/dist/public/node/themes/themes-api/retry.js.map +0 -1
- package/dist/public/node/themes/themes-api/throttler.js.map +0 -1
- package/dist/store/schema.d.ts +0 -3
- package/dist/store/schema.js +0 -27
- package/dist/store/schema.js.map +0 -1
- /package/dist/{public → private}/node/colors.d.ts +0 -0
- /package/dist/{public → private}/node/colors.js +0 -0
- /package/dist/{public → private}/node/semver.d.ts +0 -0
- /package/dist/{public → private}/node/semver.js +0 -0
- /package/dist/{public → private}/node/themes/generate-theme-name.d.ts +0 -0
- /package/dist/{public → private}/node/themes/generate-theme-name.js +0 -0
- /package/dist/{public → private}/node/themes/replace-invalid-characters.d.ts +0 -0
- /package/dist/{public → private}/node/themes/replace-invalid-characters.js +0 -0
- /package/dist/{public → private}/node/themes/themes-api/headers.d.ts +0 -0
- /package/dist/{public → private}/node/themes/themes-api/headers.js +0 -0
- /package/dist/{public → private}/node/themes/themes-api/retry.d.ts +0 -0
- /package/dist/{public → private}/node/themes/themes-api/retry.js +0 -0
- /package/dist/{public → private}/node/themes/themes-api/throttler.d.ts +0 -0
- /package/dist/{public → private}/node/themes/themes-api/throttler.js +0 -0
package/assets/cli-ruby/Gemfile
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# NOTE: These are development-only dependencies
|
|
2
2
|
source "https://rubygems.org"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
gem "bugsnag", "~> 6.22"
|
|
5
|
+
gem "listen", "~> 3.7.0"
|
|
6
|
+
gem "theme-check", "~> 1.14.0"
|
|
5
7
|
|
|
6
8
|
# None of these can actually be used in a development copy of dev
|
|
7
9
|
# They are all for CI and tests
|
|
8
10
|
# `dev` uses no gems
|
|
9
11
|
group :development, :test do
|
|
10
|
-
gem "rake"
|
|
11
12
|
gem "pry-byebug"
|
|
12
13
|
gem "byebug"
|
|
13
14
|
gem "rubocop-shopify", require: false
|
|
@@ -16,11 +17,13 @@ group :development, :test do
|
|
|
16
17
|
gem "iniparse", "~> 1.5"
|
|
17
18
|
gem "colorize", "~> 0.8.1"
|
|
18
19
|
gem "octokit", "~> 4.0"
|
|
20
|
+
gem "bundler", ">= 2.3.11"
|
|
21
|
+
gem "rake", "~> 12.3", ">= 12.3.3"
|
|
22
|
+
gem "minitest", "~> 5.0"
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
group :test do
|
|
22
26
|
gem "mocha", require: false
|
|
23
|
-
gem "minitest", ">= 5.0.0", require: false
|
|
24
27
|
gem "minitest-reporters", require: false
|
|
25
28
|
gem "minitest-fail-fast", require: false
|
|
26
29
|
gem "fakefs", ">= 1.0", require: false
|
|
@@ -20,6 +20,9 @@ module Extension
|
|
|
20
20
|
parser.on("-T", "--theme=NAME_OR_ID", "Theme ID or name of the theme app extension host theme.") do |theme|
|
|
21
21
|
flags[:theme] = theme
|
|
22
22
|
end
|
|
23
|
+
parser.on("--generate-tmp-theme", "Populate host theme, created by CLI 3, with assets") do |generate_tmp_theme|
|
|
24
|
+
flags[:generate_tmp_theme] = generate_tmp_theme
|
|
25
|
+
end
|
|
23
26
|
parser.on("--api-key=API_KEY", "Connect your extension and app by inserting your app's API key") do |api_key|
|
|
24
27
|
flags[:api_key] = api_key.gsub('"', "")
|
|
25
28
|
end
|
|
@@ -45,6 +48,7 @@ module Extension
|
|
|
45
48
|
property! :tunnel_requested, accepts: [true, false], reader: :tunnel_requested?, default: true
|
|
46
49
|
property :port, accepts: (1...(2**16))
|
|
47
50
|
property :theme, accepts: String, default: nil
|
|
51
|
+
property :generate_tmp_theme, accepts: [true, false], reader: :generate_tmp_theme?, default: false
|
|
48
52
|
property :api_key, accepts: String, default: nil
|
|
49
53
|
property :api_secret, accepts: String, default: nil
|
|
50
54
|
property :registration_id, accepts: String, default: nil
|
|
@@ -60,6 +64,7 @@ module Extension
|
|
|
60
64
|
resource_url: options.flags[:resource_url],
|
|
61
65
|
port: options.flags[:port],
|
|
62
66
|
theme: options.flags[:theme],
|
|
67
|
+
generate_tmp_theme: generate_tmp_theme?,
|
|
63
68
|
api_key: options.flags[:api_key],
|
|
64
69
|
api_secret: options.flags[:api_secret],
|
|
65
70
|
registration_id: options.flags[:registration_id],
|
|
@@ -103,6 +108,10 @@ module Extension
|
|
|
103
108
|
tunnel.nil? || !!tunnel
|
|
104
109
|
end
|
|
105
110
|
|
|
111
|
+
def generate_tmp_theme?
|
|
112
|
+
options.flags[:generate_tmp_theme] == true
|
|
113
|
+
end
|
|
114
|
+
|
|
106
115
|
def find_available_port(runtime_configuration)
|
|
107
116
|
return runtime_configuration unless runtime_configuration.port.nil?
|
|
108
117
|
return runtime_configuration unless specification_handler.choose_port?(@ctx)
|
|
@@ -137,6 +146,7 @@ module Extension
|
|
|
137
146
|
tunnel_url: runtime_configuration.tunnel_url,
|
|
138
147
|
port: runtime_configuration.port,
|
|
139
148
|
theme: runtime_configuration.theme,
|
|
149
|
+
generate_tmp_theme: runtime_configuration.generate_tmp_theme?,
|
|
140
150
|
api_key: runtime_configuration.api_key,
|
|
141
151
|
api_secret: runtime_configuration.api_secret,
|
|
142
152
|
registration_id: runtime_configuration.registration_id,
|
|
@@ -19,6 +19,7 @@ module Theme
|
|
|
19
19
|
subcommand :List, "list", Project.project_filepath("commands/list")
|
|
20
20
|
subcommand :Share, "share", Project.project_filepath("commands/share")
|
|
21
21
|
subcommand :LanguageServer, "language-server", Project.project_filepath("commands/language_server")
|
|
22
|
+
subcommand :Console, "console", Project.project_filepath("commands/console")
|
|
22
23
|
end
|
|
23
24
|
ShopifyCLI::Commands.register("Theme::Command", "theme")
|
|
24
25
|
|
|
@@ -49,13 +49,14 @@ module Theme
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def current_directory_confirmed?
|
|
52
|
-
|
|
52
|
+
return true if options.flags[:force]
|
|
53
53
|
|
|
54
|
+
@ctx.warn(@ctx.message("theme.current_directory_is_not_theme_directory"))
|
|
54
55
|
Forms::ConfirmStore.ask(
|
|
55
56
|
@ctx,
|
|
56
57
|
[],
|
|
57
58
|
title: @ctx.message("theme.confirm_current_directory"),
|
|
58
|
-
force:
|
|
59
|
+
force: !ShopifyCLI::Environment.interactive?,
|
|
59
60
|
).confirmed?
|
|
60
61
|
end
|
|
61
62
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "shopify_cli/theme/repl"
|
|
4
|
+
|
|
5
|
+
module Theme
|
|
6
|
+
class Command
|
|
7
|
+
class Console < ShopifyCLI::Command::SubCommand
|
|
8
|
+
recommend_default_ruby_range
|
|
9
|
+
|
|
10
|
+
def call(_args, *)
|
|
11
|
+
ShopifyCLI::Theme::Repl.new(@ctx).run
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module Theme
|
|
3
4
|
module Messages
|
|
4
5
|
MESSAGES = {
|
|
@@ -18,8 +19,9 @@ module Theme
|
|
|
18
19
|
ENSURE_USER
|
|
19
20
|
stable_flag_suggestion: "If the current command isn't working as expected," \
|
|
20
21
|
" we suggest re-running the command with the {{command: --stable}} flag",
|
|
21
|
-
|
|
22
|
-
"
|
|
22
|
+
current_directory_is_not_theme_directory: "It doesn’t seem like you’re running this command" \
|
|
23
|
+
" in a theme directory.",
|
|
24
|
+
confirm_current_directory: "Are you sure you want to proceed?",
|
|
23
25
|
init: {
|
|
24
26
|
help: <<~HELP,
|
|
25
27
|
{{command:%s theme init}}: Clones a Git repository to use as a starting point for building a new theme.
|
|
@@ -16,12 +16,15 @@ module ShopifyCLI
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def call(env)
|
|
19
|
-
|
|
19
|
+
path = env["PATH_INFO"]
|
|
20
|
+
if path == "/hot-reload"
|
|
20
21
|
create_stream
|
|
21
22
|
else
|
|
22
23
|
status, headers, body = @app.call(env)
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
if request_is_html?(headers) && leads_to_injectable_body?(path)
|
|
26
|
+
body = inject_hot_reload_javascript(body)
|
|
27
|
+
end
|
|
25
28
|
|
|
26
29
|
[status, headers, body]
|
|
27
30
|
end
|
|
@@ -43,6 +46,10 @@ module ShopifyCLI
|
|
|
43
46
|
headers["content-type"]&.start_with?("text/html")
|
|
44
47
|
end
|
|
45
48
|
|
|
49
|
+
def leads_to_injectable_body?(path)
|
|
50
|
+
path !~ /web-pixels-manager.+sandbox/
|
|
51
|
+
end
|
|
52
|
+
|
|
46
53
|
def inject_hot_reload_javascript(body)
|
|
47
54
|
@script_injector&.inject(body: body, dir: __dir__, mode: @mode)
|
|
48
55
|
end
|
|
@@ -78,6 +78,17 @@ module ShopifyCLI
|
|
|
78
78
|
[response.code, headers, body]
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
def secure_session_id
|
|
82
|
+
if secure_session_id_expired?
|
|
83
|
+
@ctx.debug("Refreshing preview _secure_session_id cookie")
|
|
84
|
+
response = request("HEAD", "/", query: [[:preview_theme_id, theme_id]])
|
|
85
|
+
@secure_session_id = extract_secure_session_id_from_response_headers(response)
|
|
86
|
+
@last_session_cookie_refresh = Time.now
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@secure_session_id
|
|
90
|
+
end
|
|
91
|
+
|
|
81
92
|
private
|
|
82
93
|
|
|
83
94
|
def patch_body(env, body)
|
|
@@ -164,17 +175,6 @@ module ShopifyCLI
|
|
|
164
175
|
headers["set-cookie"][SESSION_COOKIE_REGEXP, 1]
|
|
165
176
|
end
|
|
166
177
|
|
|
167
|
-
def secure_session_id
|
|
168
|
-
if secure_session_id_expired?
|
|
169
|
-
@ctx.debug("Refreshing preview _secure_session_id cookie")
|
|
170
|
-
response = request("HEAD", "/", query: [[:preview_theme_id, theme_id]])
|
|
171
|
-
@secure_session_id = extract_secure_session_id_from_response_headers(response)
|
|
172
|
-
@last_session_cookie_refresh = Time.now
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
@secure_session_id
|
|
176
|
-
end
|
|
177
|
-
|
|
178
178
|
def get_response_headers(response, env)
|
|
179
179
|
response_headers = normalize_headers(
|
|
180
180
|
response.respond_to?(:headers) ? response.headers : response.to_hash,
|
|
@@ -184,7 +184,8 @@ module ShopifyCLI
|
|
|
184
184
|
# (Taken from Rack::Proxy)
|
|
185
185
|
response_headers.reject! { |k| HOP_BY_HOP_HEADERS.include?(k.downcase) }
|
|
186
186
|
|
|
187
|
-
if response_headers["location"]&.include?("myshopify.com")
|
|
187
|
+
if response_headers["location"]&.include?("myshopify.com") ||
|
|
188
|
+
response_headers["location"]&.include?("spin.dev")
|
|
188
189
|
response_headers["location"].gsub!(%r{(https://#{shop})}, "http://#{host(env)}")
|
|
189
190
|
end
|
|
190
191
|
|
|
@@ -26,12 +26,13 @@ module ShopifyCLI
|
|
|
26
26
|
# Extensions
|
|
27
27
|
ScriptInjector = ShopifyCLI::Theme::Extension::DevServer::HotReload::ScriptInjector
|
|
28
28
|
|
|
29
|
-
attr_accessor :project, :specification_handler
|
|
29
|
+
attr_accessor :project, :specification_handler, :generate_tmp_theme
|
|
30
30
|
|
|
31
31
|
class << self
|
|
32
|
-
def start(ctx, root, port: 9292, theme: nil, project:, specification_handler:)
|
|
32
|
+
def start(ctx, root, port: 9292, theme: nil, generate_tmp_theme: false, project:, specification_handler:)
|
|
33
33
|
instance.project = project
|
|
34
34
|
instance.specification_handler = specification_handler
|
|
35
|
+
instance.generate_tmp_theme = generate_tmp_theme
|
|
35
36
|
|
|
36
37
|
super(ctx, root, port: port, theme: theme)
|
|
37
38
|
end
|
|
@@ -66,8 +67,10 @@ module ShopifyCLI
|
|
|
66
67
|
|
|
67
68
|
def theme
|
|
68
69
|
@theme ||= if theme_identifier
|
|
69
|
-
theme =
|
|
70
|
-
|
|
70
|
+
theme = HostTheme.find_by_identifier(ctx, identifier: theme_identifier)
|
|
71
|
+
ctx.abort(not_found_error_message) unless theme
|
|
72
|
+
theme.generate_tmp_theme if generate_tmp_theme
|
|
73
|
+
theme
|
|
71
74
|
else
|
|
72
75
|
HostTheme.find_or_create!(ctx)
|
|
73
76
|
end
|
|
@@ -61,20 +61,9 @@ module ShopifyCLI
|
|
|
61
61
|
new(ctx, root: nil).ensure_exists!
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
hostname = Socket.gethostname.split(".").shift
|
|
68
|
-
hash = SecureRandom.hex(3)
|
|
69
|
-
|
|
70
|
-
theme_name = "App Ext. Host ()"
|
|
71
|
-
hostname_character_limit = API_NAME_LIMIT - theme_name.length - hash.length - 1
|
|
72
|
-
identifier = encode_identifier("#{hash}-#{hostname[0, hostname_character_limit]}")
|
|
73
|
-
theme_name = "App Ext. Host (#{identifier})"
|
|
74
|
-
|
|
75
|
-
ShopifyCLI::DB.set(host_theme_name: theme_name)
|
|
76
|
-
|
|
77
|
-
theme_name
|
|
64
|
+
def self.find_by_identifier(ctx, root: nil, identifier:)
|
|
65
|
+
ShopifyCLI::DB.set(host_theme_id: identifier)
|
|
66
|
+
find(ctx, root: root)
|
|
78
67
|
end
|
|
79
68
|
|
|
80
69
|
def generate_tmp_theme
|
|
@@ -98,6 +87,22 @@ module ShopifyCLI
|
|
|
98
87
|
end
|
|
99
88
|
end
|
|
100
89
|
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def generate_host_theme_name
|
|
94
|
+
hostname = Socket.gethostname.split(".").shift
|
|
95
|
+
hash = SecureRandom.hex(3)
|
|
96
|
+
|
|
97
|
+
theme_name = "App Ext. Host ()"
|
|
98
|
+
hostname_character_limit = API_NAME_LIMIT - theme_name.length - hash.length - 1
|
|
99
|
+
identifier = encode_identifier("#{hash}-#{hostname[0, hostname_character_limit]}")
|
|
100
|
+
theme_name = "App Ext. Host (#{identifier})"
|
|
101
|
+
|
|
102
|
+
ShopifyCLI::DB.set(host_theme_name: theme_name)
|
|
103
|
+
|
|
104
|
+
theme_name
|
|
105
|
+
end
|
|
101
106
|
end
|
|
102
107
|
end
|
|
103
108
|
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyCLI
|
|
4
|
+
module Theme
|
|
5
|
+
class Repl
|
|
6
|
+
class Api
|
|
7
|
+
attr_reader :ctx, :repl
|
|
8
|
+
|
|
9
|
+
def initialize(ctx, repl)
|
|
10
|
+
@ctx = ctx
|
|
11
|
+
@repl = repl
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def request(liquid_snippet)
|
|
15
|
+
Net::HTTP.start(uri.host, 443, use_ssl: true) do |http|
|
|
16
|
+
req = Net::HTTP::Post.new(uri)
|
|
17
|
+
|
|
18
|
+
req.initialize_http_header(headers)
|
|
19
|
+
req.set_form_data(form_data(liquid_snippet))
|
|
20
|
+
res = http.request(req)
|
|
21
|
+
|
|
22
|
+
debug(res)
|
|
23
|
+
|
|
24
|
+
res
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def debug(response)
|
|
31
|
+
return response unless debug?
|
|
32
|
+
|
|
33
|
+
ctx.debug(<<~DEBUG)
|
|
34
|
+
URI: #{uri}
|
|
35
|
+
---
|
|
36
|
+
HTTP status: #{response.code}
|
|
37
|
+
---
|
|
38
|
+
Response body:
|
|
39
|
+
#{response.body}
|
|
40
|
+
DEBUG
|
|
41
|
+
|
|
42
|
+
response
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def debug?
|
|
46
|
+
@is_debug ||= ctx.debug?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def form_data(liquid_snippet)
|
|
50
|
+
template = ["", "", liquid_snippet, "", liquid_template].join("\n")
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
"replace_templates[sections/announcement-bar.liquid]" => template,
|
|
54
|
+
:_method => "GET",
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def liquid_template
|
|
59
|
+
@liquid_template ||= ::File.read("#{__dir__}/resources/template.liquid")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def cookie
|
|
63
|
+
@cookie ||= "storefront_digest=#{repl.storefront_digest}; _secure_session_id=#{repl.secure_session_id}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def shop
|
|
67
|
+
@shop ||= ShopifyCLI::Theme::ThemeAdminAPI.new(ctx).get_shop_or_abort
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def storefront_renderer_token
|
|
71
|
+
@storefront_renderer_token ||= ShopifyCLI::Environment.storefront_renderer_auth_token ||
|
|
72
|
+
ShopifyCLI::DB.get(:storefront_renderer_production_exchange_token)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def headers
|
|
76
|
+
@headers ||= if Environment.theme_access_password?
|
|
77
|
+
{
|
|
78
|
+
"Cookie" => cookie,
|
|
79
|
+
"X-Shopify-Access-Token" => Environment.admin_auth_token,
|
|
80
|
+
"X-Shopify-Shop" => shop,
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
"Cookie" => cookie,
|
|
85
|
+
"Authorization" => "Bearer #{storefront_renderer_token}",
|
|
86
|
+
"User-Agent" => "Shopify CLI",
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def uri
|
|
92
|
+
return @api_uri if @api_uri
|
|
93
|
+
|
|
94
|
+
uri_address = if Environment.theme_access_password?
|
|
95
|
+
"https://#{ThemeAccessAPI::BASE_URL}/cli/sfr"
|
|
96
|
+
else
|
|
97
|
+
"https://#{shop}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
@api_uri = URI(uri_address)
|
|
101
|
+
@api_uri.query = URI.encode_www_form([["section_id", "announcement-bar"], [:_fd, 0], [:pb, 0]])
|
|
102
|
+
@api_uri
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyCLI
|
|
4
|
+
module Theme
|
|
5
|
+
class Repl
|
|
6
|
+
class AuthDevServer < ShopifyCLI::Theme::DevServer
|
|
7
|
+
attr_accessor :app, :repl
|
|
8
|
+
|
|
9
|
+
REPL_THEME = "liquid-console-repl"
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def start(ctx, repl, port)
|
|
13
|
+
instance.repl = repl
|
|
14
|
+
|
|
15
|
+
super(ctx, nil, port: port, theme: REPL_THEME)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def theme
|
|
22
|
+
@theme ||= find_repl_theme || create_repl_theme
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find_repl_theme
|
|
26
|
+
Theme.find(ctx, nil) do |theme_hash|
|
|
27
|
+
theme_hash["name"] == theme_identifier && theme_hash["role"] == "development"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_repl_theme
|
|
32
|
+
repl_theme = Theme.new(ctx, name: theme_identifier, role: "development")
|
|
33
|
+
repl_theme.create
|
|
34
|
+
|
|
35
|
+
api_client = ThemeAdminAPI.new(ctx, repl_theme.shop)
|
|
36
|
+
status, _body, _response = api_client.put(
|
|
37
|
+
path: "themes/#{repl_theme.id}/assets/bulk.json",
|
|
38
|
+
method: "PUT",
|
|
39
|
+
body: JSON.generate({
|
|
40
|
+
assets: [
|
|
41
|
+
{ key: "sections/announcement-bar.liquid", value: "" },
|
|
42
|
+
{ key: "config/settings_schema.json", value: "[]" },
|
|
43
|
+
{ key: "config/settings_data.json", value: "{}" },
|
|
44
|
+
{ key: "layout/theme.liquid", value: "{{ content_for_header }}{{ content_for_layout }}" },
|
|
45
|
+
{ key: "layout/password.liquid", value: "{{ content_for_header }}{{ content_for_layout }}" },
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if status != 207
|
|
51
|
+
ctx.puts("{{red:Shopify Liquid console could not be initiatilize (HTTP status: #{status})}}")
|
|
52
|
+
raise ShopifyCLI::AbortSilent
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
repl_theme
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def middleware_stack
|
|
59
|
+
@app = proxy
|
|
60
|
+
@app = CdnFonts.new(app, theme: theme)
|
|
61
|
+
@app = AuthMiddleware.new(app, proxy, repl) { WebServer.shutdown }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def param_builder
|
|
65
|
+
@param_builder ||= ProxyParamBuilder.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def proxy
|
|
69
|
+
@proxy ||= Proxy.new(ctx, theme, param_builder)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def frame_title; end
|
|
73
|
+
def preview_message; end
|
|
74
|
+
def setup_server; end
|
|
75
|
+
def stop(_signal); end
|
|
76
|
+
def sync_theme; end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyCLI
|
|
4
|
+
module Theme
|
|
5
|
+
class Repl
|
|
6
|
+
class AuthMiddleware
|
|
7
|
+
def initialize(app, proxy, repl, &stop_dev_server)
|
|
8
|
+
@app = app
|
|
9
|
+
@proxy = proxy
|
|
10
|
+
@repl = repl
|
|
11
|
+
@stop_dev_server = stop_dev_server
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(env)
|
|
15
|
+
@env = env
|
|
16
|
+
|
|
17
|
+
return @app.call(env) unless authenticated?
|
|
18
|
+
|
|
19
|
+
authenticate!
|
|
20
|
+
shutdown
|
|
21
|
+
|
|
22
|
+
[
|
|
23
|
+
200,
|
|
24
|
+
{
|
|
25
|
+
"Content-Type" => "text/html",
|
|
26
|
+
"Content-Length" => success_body.size.to_s,
|
|
27
|
+
},
|
|
28
|
+
[success_body],
|
|
29
|
+
]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def close
|
|
33
|
+
@app.close
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def storefront_session
|
|
39
|
+
cookie["storefront_digest"]&.first
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def secure_session
|
|
43
|
+
@proxy.secure_session_id
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def authenticated?
|
|
47
|
+
storefront_session && secure_session
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def authenticate!
|
|
51
|
+
@repl.authenticate(storefront_session, secure_session)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def shutdown
|
|
55
|
+
Thread.new do
|
|
56
|
+
# Web server answers the request and shutdown itself
|
|
57
|
+
sleep(1)
|
|
58
|
+
|
|
59
|
+
@stop_dev_server.call
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def success_body
|
|
64
|
+
@success_body ||= ::File.read("#{__dir__}/resources/success.html")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def cookie
|
|
68
|
+
CGI::Cookie.parse(@env["HTTP_COOKIE"])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Shopify CLI</title>
|
|
8
|
+
<style type="text/css">
|
|
9
|
+
html {
|
|
10
|
+
font-family: -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif;
|
|
11
|
+
text-size-adjust: 100%;
|
|
12
|
+
text-rendering: optimizeLegibility;
|
|
13
|
+
-webkit-font-smoothing: antialiased;
|
|
14
|
+
-moz-osx-font-smoothing: grayscale;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
font-size: 26px;
|
|
19
|
+
line-height: normal;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
button, input, optgroup, select, textarea {
|
|
25
|
+
font-family: inherit;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h1 {
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
font-size: 1em;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
p {
|
|
34
|
+
font-weight: 400;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.body-success {
|
|
38
|
+
color: #F6F6F7;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.body-error {
|
|
42
|
+
color: #202223;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.app-success {
|
|
46
|
+
width: 100vw;
|
|
47
|
+
height: 100vh;
|
|
48
|
+
background-color: #054A49;
|
|
49
|
+
display: flex;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.app-error {
|
|
53
|
+
width: 100vw;
|
|
54
|
+
height: 100vh;
|
|
55
|
+
background-color: #F6F6F7;
|
|
56
|
+
display: flex;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.container {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 100%;
|
|
64
|
+
height: 100%;
|
|
65
|
+
padding-left: 7.5em;
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
68
|
+
<link rel="icon" href="favicon.svg" sizes="any" type="image/svg+xml">
|
|
69
|
+
</head>
|
|
70
|
+
|
|
71
|
+
<body class="body-success">
|
|
72
|
+
<div class="app-success">
|
|
73
|
+
<div class="container">
|
|
74
|
+
<h1>You've successfully activated the Shopify Liquid session!</h1>
|
|
75
|
+
<p>You can close this tab and return to your terminal.</p>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|