quilt_rails 3.0.0 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c38993bec98aa474ec2a1833abc0ab868085cba9a4521eb2007bfcaca6c12194
4
- data.tar.gz: be6a1c6e8a9ff87a8030d118bbe33fc5779d506070ac05d0a817653980044023
3
+ metadata.gz: 16eb5798cc8dd719437110f06bb5cff4dc1bea6c79e1a9a49eedbba0b0ebd303
4
+ data.tar.gz: d2c9d7ab124b18b6535b57888f3fa5319913554a812e6f074a92eab962cd2f6d
5
5
  SHA512:
6
- metadata.gz: 9abee4ef36e0de59346a5a0310c50074cb54f3bb6b59f228cb3aa65c2307a0ef05b719bfe391849c98e866256bc3d9380e14e41a06310c5fb0c66d02ab87f259
7
- data.tar.gz: 6fc3bacb20732cae063fc83cbf4761ca2db8f5fdc952796187f640b0058ab8cf3904e171fdc755b8b4db1d1d6a2d5e6ed19a4d9eb005b02e2bb4a562ef67317b
6
+ metadata.gz: 0a032e43ca617b448cc77f7676a287a19259fed5a41fc4e2ccefa8730c5210f878bdb9bc59183fc0200df1593a42d63147158dedf1683a4db215af739ec18c11
7
+ data.tar.gz: 3d94f79c24568c45075d658e79428acd08c8fd7f15ccb1efd979767fdff97a1adedd637c4e04b50a31615934642d2d118093ee6f7ce8d32d1fba033a13b7a4af
data/README.md CHANGED
@@ -23,7 +23,7 @@ A turn-key solution for integrating Quilt client-side libraries into your Rails
23
23
 
24
24
  ## Server-side-rendering
25
25
 
26
- 🗒 This guide is focused on internal Shopify developers with access to [`dev`](https://github.com/Shopify/dev) and [@shopify/sewing-kit](https://github.com/Shopify/sewing-kit). A similar setup can be achieved using the [manual installation](./docs/manual-installation.md) , and following the [react-server webpack plugin](../../packages/react-server/README.md#webpack-plugin) guide. Apps not running on Shopify infrastructure should [disable server-side GraphQL queries](./docs/FAQ.md) to avoid scalability issue.
26
+ 🗒 This guide is focused on internal Shopify developers with access [@shopify/sewing-kit](https://github.com/Shopify/sewing-kit). A similar setup can be achieved using the [manual installation](./docs/manual-installation.md) , and following the [react-server webpack plugin](../../packages/react-server/README.md#webpack-plugin) guide. Apps not running on Shopify infrastructure should [disable server-side GraphQL queries](./docs/FAQ.md) to avoid scalability issue.
27
27
 
28
28
  ### Quick start
29
29
 
@@ -31,12 +31,7 @@ Using the magic of generators, we can spin up a basic app with a few console com
31
31
 
32
32
  #### Generate Rails boilerplate
33
33
 
34
- With access to [`dev`](https://github.com/Shopify/dev), you can use `dev init` to scaffold out a Rails application.
35
- When prompted, choose `rails`. This will generate a basic Rails application scaffold.
36
-
37
- Alternatively, you can use [`rails new .`](https://guides.rubyonrails.org/command_line.html#rails-new) to do the same.
38
-
39
- In either case, remove [`webpacker`](./docs/FAQ.md#i-run-into-webpacker-issue-while-setting-up-quilt_rails) and [these files](./docs/FAQ.md#any-other-files-i-should-remove-before-running-generator) that any create conflict before continuing.
34
+ Use [`rails new . --skip-javascript`](https://guides.rubyonrails.org/command_line.html#rails-new) to scaffold out a Rails application.to do the same.
40
35
 
41
36
  #### Add Ruby dependencies
42
37
 
@@ -52,13 +47,12 @@ This will generate a package.json file with common sewing-kit script tasks, defa
52
47
 
53
48
  `rails generate quilt:install`
54
49
 
55
- This will install Node dependencies, provide a basic React app (in TypeScript), and mount the Quilt engine in `config/routes.rb`. Basic linting and format configurations are also generated.
50
+ This command will install Node dependencies, mount the Quilt engine in `config/routes.rb`, and provide a bare bone React app (in TypeScript) that.
56
51
 
57
52
  #### Try it out
58
53
 
59
54
  ```sh
60
- dev up
61
- dev server
55
+ bin/rails server
62
56
  ```
63
57
 
64
58
  Will run the application, starting up both servers and compiling assets.
@@ -137,6 +131,8 @@ function App() {
137
131
  export default App;
138
132
  ```
139
133
 
134
+ **Note:** This solution works out of the box for initial server-side renders. If you wish to have consistent access to request headers on subsequent client-side renders, take a look at [`NetworkUniversalProvider`](https://github.com/Shopify/quilt/tree/master/packages/react-network#networkuniversalprovider).
135
+
140
136
  ##### Example: sending custom headers from Rails controller
141
137
 
142
138
  In some cases you may want to send custom headers from Rails to your React server. Quilt facilitates this case by providing consumers with a `headers` argument on the `render_react` call.
@@ -151,6 +147,8 @@ class ReactController < ApplicationController
151
147
  end
152
148
  ```
153
149
 
150
+ 🗒️ if you don't have a controller. Follow the [instruction](./docs/manual-installation#add-a-react-controller-and-routes) to setup `quilt_rails` in a controller instead of using the engine.
151
+
154
152
  Headers can be accessed during server-side-rendering with the `useRequestHeader` hook from `@shopify/react-network`.
155
153
 
156
154
  ```tsx
@@ -173,6 +171,8 @@ In some cases you may want to send basic data from Rails to your React server. Q
173
171
 
174
172
  **Note:** The data passed should be data that is unlikely or will never change over the course of the session before they render any React components.
175
173
 
174
+ **Note:** Please note the minimal dependencies require to use this feature is listed in [CHANGELOG](./CHANGELOG.md#[1.12.0]-2020-05-07)
175
+
176
176
  ```ruby
177
177
  class ReactController < ApplicationController
178
178
  include Quilt::ReactRenderable
@@ -183,7 +183,9 @@ class ReactController < ApplicationController
183
183
  end
184
184
  ```
185
185
 
186
- If using the webpack plugin, this will be automatically passed into your application as the `data` prop.
186
+ 🗒️ if you don't have a controller. Follow the [instruction](./docs/manual-installation#add-a-react-controller-and-routes) to setup `quilt_rails` in a controller instead of using the engine.
187
+
188
+ If using `react-server` without a customized server & client file, this will be automatically passed into your application as the `data` prop. If `react-server` is not being used or a customized server / client file was provided, check out [`react-server/webpack-plugin`](../../packages/react-server/src/webpack-plugin/webpack-plugin.ts) on how to pass the data to React.
187
189
 
188
190
  ```tsx
189
191
  // app/ui/index.tsx
@@ -222,7 +224,7 @@ export default App;
222
224
 
223
225
  With SSR enabled React apps, state must be serialized on the server and deserialized on the client to keep it consistent. When using `@shopify/react-server`, the best tool for this job is [`@shopify/react-html`](https://github.com/Shopify/quilt/tree/master/packages/react-html)'s [`useSerialized`](https://github.com/Shopify/quilt/tree/master/packages/react-html#in-your-application-code) hook.
224
226
 
225
- `useSerialized` can be used to implement [universal-providers](https://github.com/Shopify/quilt/tree/master/packages/react-universal-provider#what-is-a-universal-provider-), allowing application code to manage what is persisted between the server and client without adding any custom code to client or server entrypoints. We offer some for common use cases such as [CSRF](https://github.com/Shopify/quilt/tree/master/packages/react-csrf-universal-provider), [GraphQL](https://github.com/Shopify/quilt/tree/master/packages/react-graphql-universal-provider), [I18n](https://github.com/Shopify/quilt/tree/master/packages/react-i18n-universal-provider), and the [Shopify App Bridge](https://github.com/Shopify/quilt/tree/master/packages/react-app-bridge-universal-provider).
227
+ `useSerialized` can be used to implement [universal-providers](https://github.com/Shopify/quilt/tree/master/packages/react-universal-provider#what-is-a-universal-provider-), allowing application code to manage what is persisted between the server and client without adding any custom code to client or server entrypoints. We offer some for common use cases such as [GraphQL](https://github.com/Shopify/quilt/tree/master/packages/react-graphql-universal-provider), and [I18n](https://github.com/Shopify/quilt/tree/master/packages/react-i18n-universal-provider).
226
228
 
227
229
  #### Customizing the Node server
228
230
 
@@ -244,6 +246,10 @@ When a React component sends HTTP requests back to a Rails endpoint (e.g., `/gra
244
246
 
245
247
  If your API **does not** require session data, the easiest way to deal with this is to use `protect_from_forgery with: :null_session`. This will work for APIs that either have no authentication requirements, or use header based authentication.
246
248
 
249
+ While `Can't verify CSRF token authenticity` error will persist, `protect_from_forgery with: :null_session` will keep CSRF protection while ensuring the session is nullified when a token is not sent in to be more secure.
250
+
251
+ You can also add `self.log_warning_on_csrf_failure = false` to the controller to suppress this error all together.
252
+
247
253
  ##### Example
248
254
 
249
255
  ```rb
@@ -263,7 +269,7 @@ end
263
269
  If your API **does** require session data, you can follow these steps:
264
270
 
265
271
  - Add an `x-shopify-react-xhr` header to all GraphQL requests with a value of 1 (this is done automatically if you are using `@shopify/react-graphql-universal-provider`)
266
- - Add a `protect_from_forgery with: Quilt::HeaderCsrfStrategy` override to your controllers
272
+ - Add a `protect_from_forgery with: Quilt::HeaderCsrfStrategy` override to your API controllers
267
273
 
268
274
  ##### Example
269
275
 
@@ -281,6 +287,25 @@ class GraphqlController < ApplicationController
281
287
  end
282
288
  ```
283
289
 
290
+ #### Exception monitoring with Bugsnag
291
+
292
+ For an opinionated universal Bugsnag+React setup we provide and support [`@shopify/react-bugsnag`](https://github.com/Shopify/quilt/tree/master/packages/react-bugsnag).
293
+
294
+ ##### Example
295
+
296
+ ```typescript
297
+ // app/ui/index.tsx
298
+ import React from 'react';
299
+ import {Bugsnag, createBugsnagClient} from 'utilities/bugsnag';
300
+ import {bugsnagClientApiKey} from 'config/bugsnag';
301
+
302
+ const bugsnagClient = createBugsnagClient({apiKey: bugsnagClientApiKey});
303
+
304
+ export function App() {
305
+ return <Bugsnag client={bugsnagClient}>{/* actual app content here */}</Bugsnag>;
306
+ }
307
+ ```
308
+
284
309
  ## Performance tracking a React app
285
310
 
286
311
  To setup performance tracking with your React app with `quilt_rails`.
data/Rakefile CHANGED
@@ -24,3 +24,5 @@ Rake::TestTask.new do |t|
24
24
  t.name = 'test:unit'
25
25
  t.pattern = 'test/quilt_rails/**/*_test.rb'
26
26
  end
27
+
28
+ task(default: %i(test))
@@ -8,9 +8,9 @@ module Quilt
8
8
  def create
9
9
  process_report
10
10
 
11
- render json: { result: 'success' }, status: 200
11
+ render(json: { result: 'success' }, status: 200)
12
12
  rescue ActionController::ParameterMissing => error
13
- render json: { error: error.message, status: 422 }
13
+ render(json: { error: error.message, status: 422 })
14
14
  end
15
15
  end
16
16
  end
@@ -3,6 +3,11 @@
3
3
  module Quilt
4
4
  class UiController < ApplicationController
5
5
  include Quilt::ReactRenderable
6
+ layout(false)
7
+
8
+ rescue_from(Quilt::ReactRenderable::ReactServerNoResponseError) do
9
+ render(:react_render_error, status: :internal_server_error)
10
+ end
6
11
 
7
12
  def index
8
13
  render_react
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>React Render Error</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta http-equiv="refresh" content="3;URL='/'" />
7
+ <link rel="stylesheet" href="https://unpkg.com/@shopify/polaris@5.1.0/dist/styles.css" />
8
+ </head>
9
+ <body>
10
+ <div>
11
+ <div>
12
+ <h1>Waiting for React Sever to boot up.</h1>
13
+ <p>This page will refresh automatically.</p>
14
+ </div>
15
+ <p>
16
+ If this error persists, ensure <code>@shopify/react-server</code>
17
+ is running on <code>http://localhost:8081</code>.
18
+ </p>
19
+ </div>
20
+ </body>
21
+ </html>
@@ -12,7 +12,7 @@ module Quilt
12
12
  if File.exist?(procfile_path)
13
13
  append_file(procfile_path, File.read(File.expand_path(find_in_source_paths(procfile_path))))
14
14
  else
15
- copy_file procfile_path
15
+ copy_file(procfile_path)
16
16
  end
17
17
  end
18
18
 
@@ -20,12 +20,12 @@ module Quilt
20
20
  routes_path = "config/routes.rb"
21
21
 
22
22
  if File.exist?(routes_path)
23
- route "mount Quilt::Engine, at: '/'"
23
+ route("mount Quilt::Engine, at: '/'")
24
24
  else
25
- copy_file "routes.rb", routes_path
25
+ copy_file("routes.rb", routes_path)
26
26
  end
27
27
 
28
- say "Added Quilt engine mount"
28
+ say("Added Quilt engine mount")
29
29
  end
30
30
  end
31
31
  end
@@ -6,6 +6,18 @@ module Quilt
6
6
 
7
7
  desc "This generator adds a React app."
8
8
 
9
+ def set_app_config_javascript_path
10
+ config_path = "config/application.rb"
11
+
12
+ unless File.exist?(config_path)
13
+ inject_into_file(
14
+ config_path,
15
+ "\n config.javascript_path = \"ui\"\n",
16
+ before: /^ end$/,
17
+ )
18
+ end
19
+ end
20
+
9
21
  def create_app_file
10
22
  copy_file("App.tsx", "app/ui/index.tsx")
11
23
  end
@@ -3,19 +3,20 @@
3
3
  module Quilt
4
4
  class ReactSetupGenerator < Rails::Generators::Base
5
5
  source_root File.expand_path('templates', __dir__)
6
+ class_option :skip_yarn, type: :boolean, default: false
6
7
 
7
8
  desc "This generator adds a React app."
8
9
 
9
10
  def install_js_dependencies
10
- say "Installing @shopify/react-server and @shopify/sewing-kit dependencies"
11
+ return if options.skip_yarn?
12
+
13
+ say("Installing react and types dependencies")
11
14
  system("yarn add "\
12
- "@shopify/sewing-kit "\
13
- "@shopify/react-server "\
14
15
  "typescript@~3.8.0 "\
15
16
  "react@~16.11.0 "\
16
17
  "react-dom@~16.11.0 "\
17
18
  "@types/react@~16.9.0 "\
18
- "@types/react-dom@~16.9.0 ") unless Rails.env.test?
19
+ "@types/react-dom@~16.9.0 ")
19
20
  end
20
21
 
21
22
  def create_tsconfig
@@ -3,11 +3,12 @@ module Quilt
3
3
  end
4
4
 
5
5
  require "quilt_rails/version"
6
- require "quilt_rails/engine"
7
6
  require "quilt_rails/logger"
8
7
  require "quilt_rails/configuration"
9
8
  require "quilt_rails/react_renderable"
10
9
  require "quilt_rails/performance"
11
10
  require "quilt_rails/trusted_ui_server_csrf_strategy"
12
11
  require "quilt_rails/header_csrf_strategy"
12
+
13
+ require "quilt_rails/engine"
13
14
  require "quilt_rails/monkey_patches/active_support_reloader" if Rails.env.development?
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Quilt
4
- class Configuration
5
- attr_accessor :react_server_host, :react_server_protocol
3
+ require "active_support/ordered_options"
6
4
 
5
+ module Quilt
6
+ class Configuration < ActiveSupport::OrderedOptions
7
7
  def initialize
8
- ip = ENV['REACT_SERVER_IP'] || 'localhost'
9
- port = ENV['REACT_SERVER_PORT'] || 8081
8
+ super
9
+ react_server_ip = ENV['REACT_SERVER_IP'] || "localhost"
10
+ react_server_port = ENV['REACT_SERVER_PORT'] || 8081
11
+
12
+ self.react_server_host = "#{react_server_ip}:#{react_server_port}"
13
+ self.react_server_protocol = ENV['REACT_SERVER_PROTOCOL'] || "http"
14
+ self.mount = true
15
+ end
10
16
 
11
- @react_server_host = "#{ip}:#{port}"
12
- @react_server_protocol = ENV['REACT_SERVER_PROTOCOL'] || 'http'
17
+ def mount?
18
+ mount
13
19
  end
14
20
  end
15
21
 
@@ -3,5 +3,15 @@
3
3
  module Quilt
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace Quilt
6
+
7
+ config.quilt = Quilt.configuration
8
+
9
+ initializer(:mount_quilt, before: :add_builtin_route) do |app|
10
+ if config.quilt.mount?
11
+ app.routes.append do
12
+ mount(Quilt::Engine, at: '/') unless has_named_route?(:quilt)
13
+ end
14
+ end
15
+ end
6
16
  end
7
17
  end
@@ -25,9 +25,10 @@ module Quilt
25
25
 
26
26
  class NoSameSiteHeaderError < StandardError
27
27
  def initialize
28
- # rubocop:disable LineLength
29
- super "CSRF verification failed. This request is missing the `x-shopify-react-xhr` header, or it does not have the expected value."
30
- # rubocop:enable LineLength
28
+ super(<<~MSG.squish)
29
+ CSRF verification failed. This request is missing the
30
+ `x-shopify-react-xhr` header, or it does not have the expected value.
31
+ MSG
31
32
  end
32
33
  end
33
34
  end
@@ -3,7 +3,7 @@
3
3
  module Quilt
4
4
  module Logger
5
5
  def self.log(string)
6
- if defined? Rails && Rails.logger.nil?
6
+ if Rails.logger.nil?
7
7
  puts string
8
8
  else
9
9
  Rails.logger.info(string)
@@ -7,25 +7,39 @@ module Quilt
7
7
  attr_accessor :navigations
8
8
  attr_accessor :connection
9
9
 
10
- def self.from_params(params)
11
- params.transform_keys! { |key| key.underscore.to_sym }
12
- params[:connection] = { effectiveType: 'unknown' } if params[:connection].blank?
10
+ class << self
11
+ def from_params(params)
12
+ params.transform_keys! { |key| key.underscore.to_sym }
13
+ params[:connection] = { effectiveType: 'unknown' } if params[:connection].blank?
13
14
 
14
- connection = Connection.from_params(params[:connection])
15
+ connection = Connection.from_params(params[:connection])
15
16
 
16
- Report.new(
17
- connection: connection,
18
- navigations: (params[:navigations] || []).map do |navigation|
17
+ Report.new(
18
+ connection: connection,
19
+ navigations: build_navigations(params[:navigations], connection: connection),
20
+ events: build_events(params[:events], connection: connection),
21
+ )
22
+ end
23
+
24
+ private
25
+
26
+ def build_navigations(navigations_params, connection:)
27
+ navigations_params ||= []
28
+ navigations_params.map do |navigation|
19
29
  navigation = Navigation.from_params(navigation)
20
30
  navigation.connection = connection
21
31
  navigation
22
- end,
23
- events: (params[:events] || []).map do |event|
32
+ end
33
+ end
34
+
35
+ def build_events(events_params, connection:)
36
+ events_params ||= []
37
+ events_params.map do |event|
24
38
  event = Event.from_params(event)
25
39
  event.connection = connection
26
40
  event
27
- end,
28
- )
41
+ end
42
+ end
29
43
  end
30
44
 
31
45
  def initialize(events:, navigations:, connection:)
@@ -39,7 +39,7 @@ module Quilt
39
39
  begin
40
40
  reverse_proxy(
41
41
  url,
42
- headers: headers.merge('X-CSRF-Token': form_authenticity_token, 'X-Quilt-Data': data.to_json)
42
+ headers: headers.merge('X-Quilt-Data': data.to_json)
43
43
  ) do |callbacks|
44
44
  callbacks.on_response do |status_code, _response|
45
45
  Quilt::Logger.log("[ReactRenderable] #{url} returned #{status_code}")
@@ -52,17 +52,19 @@ module Quilt
52
52
 
53
53
  class ReactServerNoResponseError < StandardError
54
54
  def initialize(url)
55
- # rubocop:disable LineLength
56
- super "Errno::ECONNREFUSED: Waiting for React server to boot up. If this error presists verify that @shopify/react-server is configured on #{url}"
57
- # rubocop:enable LineLength
55
+ super(<<~MSG.squish)
56
+ Errno::ECONNREFUSED: Waiting for React server to boot up.
57
+ If this error persists verify that @shopify/react-server is configured on #{url}"
58
+ MSG
58
59
  end
59
60
  end
60
61
 
61
62
  class DoNotIntegrationTestError < StandardError
62
63
  def initialize
63
- # rubocop:disable LineLength
64
- super "Do not try to use Rails integration tests on your quilt_rails app. Instead use Jest and @shopify/react-testing to test your React application directly."
65
- # rubocop:enable LineLength
64
+ super(<<~MSG.squish)
65
+ Do not try to use Rails integration tests on your quilt_rails app.
66
+ Instead use Jest and @shopify/react-testing to test your React application directly."
67
+ MSG
66
68
  end
67
69
  end
68
70
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Quilt
3
- VERSION = "3.0.0"
3
+ VERSION = "3.3.1"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quilt_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathew Allen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-10 00:00:00.000000000 Z
11
+ date: 2020-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.1.3
83
- description: A turn-key solution for integrating server-rendered react into your Rails
83
+ description: A turn-key solution for integrating server-rendered React into your Rails
84
84
  app using Quilt libraries.
85
85
  email:
86
86
  - mathew.allen@shopify.com
@@ -92,6 +92,7 @@ files:
92
92
  - Rakefile
93
93
  - app/controllers/quilt/performance_report_controller.rb
94
94
  - app/controllers/quilt/ui_controller.rb
95
+ - app/views/quilt/ui/react_render_error.html
95
96
  - config/routes.rb
96
97
  - lib/generators/quilt/USAGE
97
98
  - lib/generators/quilt/install_generator.rb
@@ -104,7 +105,6 @@ files:
104
105
  - lib/generators/quilt/react_app/templates/App.tsx
105
106
  - lib/generators/quilt/react_setup/USAGE
106
107
  - lib/generators/quilt/react_setup/react_setup_generator.rb
107
- - lib/generators/quilt/react_setup/templates/App.tsx
108
108
  - lib/generators/quilt/react_setup/templates/tsconfig.json
109
109
  - lib/quilt_rails.rb
110
110
  - lib/quilt_rails/configuration.rb
@@ -147,6 +147,6 @@ requirements: []
147
147
  rubygems_version: 3.0.3
148
148
  signing_key:
149
149
  specification_version: 4
150
- summary: A turn-key solution for integrating server-rendered react into your Rails
150
+ summary: A turn-key solution for integrating server-rendered React into your Rails
151
151
  app using Quilt libraries.
152
152
  test_files: []
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
-
3
- function App() {
4
- return <div>Hello Quilt</div>;
5
- }
6
-
7
- export default App;