quilt_rails 1.10.0 → 1.13.0

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: f4580b1b1870a7fb2afa9bc64bab975e8ec3c7041f775de16356d31151ab4768
4
- data.tar.gz: 8914278b57d9bb8bad32c33c2e9697289073fe80576248b08bfe60f0b6f2536d
3
+ metadata.gz: dee878b6b3d7207ed8c6883791e896ccc1208d6508aae5c592487d2da900cf91
4
+ data.tar.gz: 38f3fbd9695b816b0a873891b3fd6b90377f541595ac3427b11677d62289e6aa
5
5
  SHA512:
6
- metadata.gz: e908b7d01e781f43ccf9c3a21d595aef8fffb612fe5da76d29f1cbef9a635ef5cbd8b8b29e6e9cbd8a9bdf170ac7fbabe7a4e0c8880a3bd81a90fde2d2da5a03
7
- data.tar.gz: 66fe45a859a30dde3a31e76563cb6d5ef5c0d6621f9127cb1a41c270dde190b47227a5d31bcca81bdb26098e172a67e811d7c58c551a74fa8b7d6d8e0d1968bb
6
+ metadata.gz: 0fb1891f8f988bb0b1b8c60c7b5b25e45c8859ba06ef855625d4656fd24f54a79ead749bad2ee0a2a15559ce097106215d5801a6eda9b3b6c55b7fdc2ee42895
7
+ data.tar.gz: 74d4d5a3b5ed3e96ce4d3348c67ecc89e6022c24cf631c3be192eb43b7ca4202e4d82351d0d22fd8385ddc75c3ddc82657ca54689b35a1341b44de8b3bf70aa9
data/README.md CHANGED
@@ -8,53 +8,22 @@ A turn-key solution for integrating Quilt client-side libraries into your Rails
8
8
  - [Quick start](#quick-start)
9
9
  - [Generate Rails boilerplate](#generate-rails-boilerplate)
10
10
  - [Add Ruby dependencies](#add-ruby-dependencies)
11
- - [Generate Quilt boilerplate](#generate-quilt-boilerplate)
11
+ - [Generate app boilerplate](#generate-app-boilerplate)
12
12
  - [Try it out](#try-it-out)
13
13
  - [Manual Install](#manual-installation)
14
- - [Install Dependencies](#install-dependencies)
15
- - [Setup the Rails app](#setup-the-rails-app)
16
- - [Add JavaScript](#add-javascript)
17
- - [Run the server](#run-the-server)
18
14
  - [Application Layout](#application-layout)
19
15
  - [Advanced Use](#advanced-use)
20
16
  - [Testing](#testing)
21
17
  - [Interacting with the request and response in React code](#interacting-with-the-request-and-response-in-react-code)
22
18
  - [Dealing with isomorphic state](#dealing-with-isomorphic-state)
23
- - [Customizing the node server](#customizing-the-node-server)
19
+ - [Customizing the Node server](#customizing-the-node-server)
20
+ - [Fixing rejected CSRF tokens for new user sessions](#fixing-rejected-csrf-tokens-for-new-user-sessions)
24
21
  - [Performance tracking a React app](#performance-tracking-a-react-app)
25
- - [Install dependencies](#install-dependencies)
26
- - [Setup an endpoint for performance reports](setup-an-endpoint-for-performance-reports)
27
- - [Add annotations](#add-annotations)
28
- - [Send the report](#send-the-report)
29
- - [Verify in development](#verify-in-development)
30
- - [Configure StatsD for production](#configure-statsd-for-production)
31
22
  - [API](#api)
32
- - [ReactRenderable](#reactrenderable)
33
- - [Performance](#performance)
34
- - [Engine](#engine)
35
- - [Generators](#generators)
36
23
 
37
24
  ## Server-side-rendering
38
25
 
39
- ### Alpha functionality - do not use in high-traffic production applications
40
-
41
- **Warning:** quilt_rails's server-side-rendering module `ReactRenderable` does not work at scale. Improvements to its architecture are being investigated. In its current state, it can be used for:
42
-
43
- - Workshop applications
44
- - Proof of concept applications
45
- - Low traffic applications
46
-
47
- For a description of the current architecture's problems, see [this Github comment](https://github.com/Shopify/quilt/issues/1059#issuecomment-539195340).
48
-
49
- The ["decide on a scalable quilt_rails architecture" issue](https://github.com/Shopify/quilt/issues/1100) will track discussion of future architectures.
50
-
51
- To scale up existing quilt_rails applications, skip server-side queries in your components. e.g.:
52
-
53
- ```ts
54
- useQuery(MyQuery, {
55
- skip: typeof document === 'undefined',
56
- });
57
- ```
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) , and following the [react-server-webpack-plugin](../../packages/react-server-webpack-plugin/README.md) guide. Apps not running on Shopify infrastructure should [disable server-side GraphQL queries](./docs/FAQ.md) to avoid scalability issue.
58
27
 
59
28
  ### Quick start
60
29
 
@@ -62,173 +31,37 @@ Using the magic of generators, we can spin up a basic app with a few console com
62
31
 
63
32
  #### Generate Rails boilerplate
64
33
 
65
- `dev init`
66
-
34
+ With access to [`dev`](https://github.com/Shopify/dev), you can use `dev init` to scaffold out a Rails application.
67
35
  When prompted, choose `rails`. This will generate a basic Rails application scaffold.
68
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) before continuing.
40
+
69
41
  #### Add Ruby dependencies
70
42
 
71
43
  `bundle add sewing_kit quilt_rails`
72
44
 
73
45
  This will install our ruby dependencies and update the project's gemfile.
74
46
 
75
- #### Generate Quilt boilerplate
47
+ #### Generate app boilerplate
76
48
 
77
- `rails generate quilt:install`
49
+ `rails generate quilt_rails:install`
78
50
 
79
- This will install the Node dependencies, provide a basic React app (in TypeScript) and mounts the Quilt engine inside of `config/routes.rb`.
51
+ 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.
80
52
 
81
53
  #### Try it out
82
54
 
83
- `dev server`
84
-
85
- Will run the application, starting up both servers and compiling assets.
86
-
87
- ### Manual installation
88
-
89
- An application can also be setup manually using the following steps.
90
-
91
- #### Install dependencies
92
-
93
55
  ```sh
94
- # Add core Node dependencies
95
- yarn add @shopify/sewing-kit @shopify/react-server
96
-
97
- # Add React
98
- yarn add react react-dom
99
-
100
- yarn
101
56
  dev up
57
+ dev server
102
58
  ```
103
59
 
104
- #### Setup the Rails app
105
-
106
- There are 2 ways to consume this package.
107
-
108
- ##### Option 1: Mount the Engine
109
-
110
- Add the engine to `routes.rb`.
111
-
112
- ```ruby
113
- # config/routes.rb
114
- Rails.application.routes.draw do
115
- # ...
116
- mount Quilt::Engine, at: '/'
117
- end
118
- ```
119
-
120
- If only a sub-section of routes should respond with the React App, it can be configured using the `at` parameter.
121
-
122
- ```ruby
123
- # config/routes.rb
124
- Rails.application.routes.draw do
125
- # ...
126
- mount Quilt::Engine, at: '/path/to/react'
127
- end
128
- ```
129
-
130
- ##### Option 2: Add a React controller and routes
131
-
132
- Create a `ReactController` to handle react requests.
133
-
134
- ```ruby
135
- class ReactController < ApplicationController
136
- include Quilt::ReactRenderable
137
-
138
- def index
139
- render_react
140
- end
141
- end
142
- ```
143
-
144
- Add routes to default to the `ReactController`.
145
-
146
- ```ruby
147
- get '/*path', to: 'react#index'
148
- root 'react#index'
149
- ```
150
-
151
- #### Add JavaScript
152
-
153
- `sewing_kit` looks for the top level component of your React app in `app/ui/index`. The component exported from this component (and any imported JS/CSS) will be built into a `main` bundle, and used to render the initial server-rendered markup.
154
-
155
- We will add a basic entrypoint using React with some HTML.
156
-
157
- ```tsx
158
- // app/ui/index.tsx
159
-
160
- import React from 'react';
161
-
162
- function App() {
163
- return <h1>My application ❤️</h1>;
164
- }
165
-
166
- export default App;
167
- ```
168
-
169
- #### Run the server
170
-
171
- `dev server`
172
-
173
60
  Will run the application, starting up both servers and compiling assets.
174
61
 
175
- ### Application layout
176
-
177
- #### Minimal
178
-
179
- The basic layout for an app using `quilt_rails` and friends will have a `ui` folder nested inside the normal Rails `app` folder, containing at _least_ an index.js file exporting a React component.
180
-
181
- ```
182
- ├── Gemfile (must contain "gem 'sewing_kit" and "gem 'quilt_rails'")
183
- ├── package.json (must specify '@shopify/sewing-kit' and `@shopify/react-server` as 'dependencies')
184
-
185
- └── app
186
- └── ui
187
- │ └─- index.{js|ts} (exports a React component)
188
- └── controllers
189
- └─- react_controller.rb (see above)
190
- ```
191
-
192
- #### Rails and React
193
-
194
- A more complex application will want a more complex layout. The following shows scalable locations for:
195
-
196
- - Global SCSS settings
197
- - App sections (roughly analogous to Rails routes)
198
- - Components
199
- - Co-located CSS modules
200
- - Co-located unit tests
201
- - Test setup files
62
+ ### Manual installation
202
63
 
203
- ```
204
- └── app
205
- └── ui
206
- ├─- index.{js|ts} (exports a React component)
207
- ├── styles (optional)
208
- └── shared.scss (common functions/mixins you want available in every scss file. Requires configuring `plugin.sass`'s `autoInclude` option in `sewing-kit.config.js`)
209
-
210
- └── tests (optional)
211
- │ └── each-test.{js|ts}
212
- │ └── setup.{js|ts}
213
- └── features (optional)
214
- ├── App
215
- │ ├── index.{js|ts}
216
- │ ├── App.{js|ts}x
217
- │ └── tests
218
- │ └── App.test.{js|ts}x
219
-
220
- ├-─ MyComponent
221
- │ ├-─ index.{js|ts}
222
- │ ├-─ MyComponent.{js|ts}x
223
- │ ├── MyComponent.scss (optional; component-scoped CSS styles, mixins, etc)
224
- │ └── tests
225
- │ └── MyComponent.test.{js|ts}x
226
-
227
- └── sections (optional; container views that compose presentation components into UI blocks)
228
- └── Home
229
- ├-─ index.{js|ts}
230
- └── Home.{js|ts}
231
- ```
64
+ Follow [this guide](./docs/manual-installation) on how to do manual setup without the generator.
232
65
 
233
66
  ### Advanced use
234
67
 
@@ -300,65 +133,67 @@ function App() {
300
133
  export default App;
301
134
  ```
302
135
 
303
- ##### Example: sending headers from Rails controller
136
+ ##### Example: sending custom headers from Rails controller
137
+
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.
304
139
 
305
140
  ```ruby
306
141
  class ReactController < ApplicationController
307
142
  include Quilt::ReactRenderable
308
143
 
309
144
  def index
310
- render_react(headers: { 'x-custom-header': 'header-value-a' })
145
+ render_react(headers: {'x-custom-header': 'header-value-a'})
311
146
  end
312
147
  end
313
148
  ```
314
149
 
315
- You will need to serialize the result of the useRequestHeader hook in order for it to persist to the client
316
-
317
- ```tsx
318
- // app/ui/foundation/CustomUniversalProvider.tsx
319
- import {createContext} from 'react';
320
- import {createUniversalProvider} from '@shopify/react-universal-provider';
321
-
322
- export const CustomContext = createContext<string | null>(null);
323
- export const CustomUniversalProvider = createUniversalProvider('custom-key', CustomContext);
324
- ```
150
+ Headers can be accessed during server-side-rendering with the `useRequestHeader` hook from `@shopify/react-network`.
325
151
 
326
152
  ```tsx
327
153
  // app/ui/index.tsx
328
154
 
329
155
  import React from 'react';
330
156
  import {useRequestHeader} from '@shopify/react-network';
331
- import {CustomUniversalProvider} from './foundation/CustomUniversalProvider';
332
- import {ComponentWithCustomHeader} from './components/ComponentWithCustomHeader';
333
157
 
334
158
  function App() {
335
- // get `x-custom-header` from the request that was sent through Rails ReactController
336
- const customHeader = useRequestHeader('x-custom-header');
337
-
338
- return (
339
- <CustomUniversalProvider value={customHeader}>
340
- <h1>My application ❤️</h1>
341
- <ComponentWithCustomHeader />
342
- </CustomUniversalProvider>
343
- );
159
+ const header = useRequestHeader('x-custom-header');
160
+ return <h1>Data: {header}</h1>;
344
161
  }
345
162
 
346
163
  export default App;
347
164
  ```
348
165
 
166
+ ##### Example: sending custom data from Rails controller
167
+
168
+ In some cases you may want to send basic data from Rails to your React server. Quilt facilitates this case by providing consumers with a `data` argument on the `render_react` call.
169
+
170
+ **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.
171
+
172
+ ```ruby
173
+ class ReactController < ApplicationController
174
+ include Quilt::ReactRenderable
175
+
176
+ def index
177
+ render_react(data: {'some_id': 123})
178
+ end
179
+ end
180
+ ```
181
+
182
+ If using the webpack plugin, this will be automatically passed into your application as the `data` prop.
183
+
349
184
  ```tsx
350
- // app/ui/components/ComponentWithCustomHeader.tsx
185
+ // app/ui/index.tsx
351
186
 
352
- import React, {useContext} from 'react';
353
- import {CustomContext} from '../foundation/CustomUniversalProvider';
187
+ import React from 'react';
354
188
 
355
- export function ComponentWithCustomHeader() {
356
- // get `x-custom-header` from serialized context
357
- // will be 'header-value-a' in this example
358
- const customHeader = useContext(CustomContext);
189
+ function App({data}: {data: Record<string, any>}) {
190
+ // Logs {"some_id":123}
191
+ console.log(data);
359
192
 
360
- return <span>{customHeader}</span>;
193
+ return <h1>Data: {data}</h1>;
361
194
  }
195
+
196
+ export default App;
362
197
  ```
363
198
 
364
199
  ##### Example: redirecting
@@ -385,57 +220,31 @@ With SSR enabled React apps, state must be serialized on the server and deserial
385
220
 
386
221
  `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).
387
222
 
388
- #### Customizing the node server
223
+ #### Customizing the Node server
224
+
225
+ By default, sewing-kit bundles in [`@shopify/react-server-webpack-plugin`](../../packages/react-server-webpack-plugin/README.md) for `quilt_rails` applications to get apps up and running fast without needing to manually write any Node server code.
389
226
 
390
- By default, sewing-kit bundles in `@shopify/react-server-webpack-plugin` for `quilt_rails` applications to get apps up and running fast without needing to manually write any node server code. If what it provides is not sufficient, a custom server can be defined by adding a `server.js` or `server.ts` file to the app folder.
227
+ If what it provides is not sufficient, a completely custom server can be defined by adding a `server.js` or `server.ts` file to the `app/ui` folder. The simplest way to customize the server is to export the object created by [`@shopify/react-server`](../../packages/react-server/README.md#node-usage)'s `createServer` call in `server.ts` file.
391
228
 
392
229
  ```
393
- └── app
230
+ └── appeon
394
231
  └── ui
395
232
  └─- app.{js|ts}x
396
233
  └─- index.{js|ts}
397
234
  └─- server.{js|ts}x
398
235
  ```
399
236
 
400
- ```tsx
401
- // app/ui/server.tsx
402
- import '@shopify/polyfills/fetch';
403
- import {createServer} from '@shopify/react-server';
404
- import {Context} from 'koa';
405
- import React from 'react';
406
-
407
- import App from './app';
408
-
409
- // The simplest way to build a custom server that will work with this library is to use the APIs provided by @shopify/react-server.
410
- // https://github.com/Shopify/quilt/blob/master/packages/react-server/README.md#L8
411
- const app = createServer({
412
- port: process.env.PORT ? parseInt(process.env.PORT, 10) : 8081,
413
- ip: process.env.IP,
414
- assetPrefix: process.env.CDN_URL || 'localhost:8080/assets/webpack',
415
- render: (ctx, {locale}) => {
416
- const whatever = /* do something special with the koa context */;
417
- // any special data we add to the incoming request in our rails controller we can access here to pass into our component
418
- return <App server someCustomProp={whatever} location={ctx.request.url} locale={locale} />;
419
- },
420
- });
421
-
422
- export default app;
423
- ```
424
-
425
237
  #### Fixing rejected CSRF tokens for new user sessions
426
238
 
427
- If a React component calls back to a Rails endpoint (e.g., `/graphql`), Rails may throw a `Can't verify CSRF token authenticity` exception. This stems from the Rails CSRF tokens not persisting until after the first `UiController` call ends.
428
-
429
- To fix this:
239
+ When a React component sends HTTP requests back to a Rails endpoint (e.g., `/graphql`), Rails may throw a `Can't verify CSRF token authenticity` exception. This stems from the Rails CSRF tokens not persisting until after the first `UiController` call ends.
430
240
 
431
- - Add an `X-Shopify-Server-Side-Rendered: 1` header to all server-side GraphQL requests
432
- - Add a `protect_from_forgery with: Quilt::TrustedUiServerCsrfStrategy` override to Node-accessed controllers
241
+ 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.
433
242
 
434
- e.g.:
243
+ ##### Example
435
244
 
436
245
  ```rb
437
246
  class GraphqlController < ApplicationController
438
- protect_from_forgery with: Quilt::TrustedUiServerCsrfStrategy
247
+ protect_from_forgery with: :null_session
439
248
 
440
249
  def execute
441
250
  # Get GraphQL query, etc
@@ -447,363 +256,36 @@ class GraphqlController < ApplicationController
447
256
  end
448
257
  ```
449
258
 
450
- ## Performance tracking a React app
451
-
452
- Using [`Quilt::Performance::Reportable`](#performanceReportable) and [@shopify/react-performance](https://www.npmjs.com/package/@shopify/react-performance) it's easy to add performance tracking to apps using[`sewing_kit`](https://github.com/Shopify/sewing-kit/tree/master/gems/sewing_kit#sewing_kit-) for client-side-rendering or `quilt_rails` for server-side-rendering.
453
-
454
- ### Install dependencies
455
-
456
- 1. Install the gem (if your app is not already using `quilt_rails`).
457
-
458
- ```bash
459
- bundle add quilt_rails
460
- ```
461
-
462
- 2. Install `@shopify/react-performance`, the library we will use to annotate our React application and send performance reports to our server.
463
-
464
- ```bash
465
- yarn add @shopify/react-performance
466
- ```
467
-
468
- ### Setup an endpoint for performance reports
469
-
470
- If your application is not using `Quilt::Engine`, you will need to manually configure the server-side portion of performance tracking. If it _is_ using the engine, the following will be done automatically.
471
-
472
- 1. Add a `PerformanceController` and the corresponding routes to your Rails app.
473
-
474
- ```ruby
475
- # app/controllers/performance_report_controller.rb
476
-
477
- class PerformanceReportController < ActionController::Base
478
- include Quilt::Performance::Reportable
479
- protect_from_forgery with: :null_session
480
-
481
- def create
482
- process_report
483
-
484
- render(json: { result: 'success' }, status: 200)
485
- rescue ActionController::ParameterMissing => error
486
- render(json: { error: error.message }, status: 422)
487
- end
488
- end
489
- ```
490
-
491
- 2. Add a route pointing at the controller.
492
-
493
- ```ruby
494
- # config/routes.rb
259
+ If your API **does** require session data, you can follow these steps:
495
260
 
496
- post '/performance_report', to: 'performance_report#create'
261
+ - 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`)
262
+ - Add a `protect_from_forgery with: Quilt::HeaderCsrfStrategy` override to your controllers
497
263
 
498
- # rest of routes
499
- ```
500
-
501
- ### Add annotations
502
-
503
- Add a [`usePerformanceMark`](https://github.com/Shopify/quilt/tree/master/packages/react-performance#useperformancemark) call to each of your route-level components.
504
-
505
- ```tsx
506
- // app/ui/features/Home/Home.tsx
507
- import {usePerformanceMark} from '@shopify/react-performance';
508
-
509
- export function Home() {
510
- // tell the library the page has finished rendering completely
511
- usePerformanceMark('complete', 'Home');
512
-
513
- return <>{/* your Home page JSX goes here*/}</>;
514
- }
515
- ```
516
-
517
- ### Send the report
518
-
519
- Add a [`usePerformanceReport`](https://github.com/Shopify/quilt/tree/master/packages/react-performance#usePerformanceReport) call to your top-level `<App />` component.
520
-
521
- ```tsx
522
- // app/ui/foundation/App/App.tsx
523
- import {usePerformanceReport} from '@shopify/react-performance';
524
-
525
- export function App() {
526
- // send the report to the server
527
- usePerformanceReport('/performance_report');
528
-
529
- return <>{/* your app JSX goes here*/}</>;
530
- }
531
- ```
532
-
533
- For more details on how to use the APIs from `@shopify/react-performance` check out its [documentation](https://github.com/Shopify/quilt/tree/master/packages/react-performance).
534
-
535
- ### Verify in development
536
-
537
- By default `quilt_rails` will not send metrics in development mode. To verify your app is setup correctly you can check in your network tab when visiting your application and see that POST requests are sent to `/performance_report`, and recieve a `200 OK` response.
264
+ ##### Example
538
265
 
539
- If you want more insight into what distributions _would_ be sent in production, you can use the `on_distribution` callback provided by the library to setup logging.
266
+ ```rb
267
+ class GraphqlController < ApplicationController
268
+ protect_from_forgery with: Quilt::HeaderCsrfStrategy
540
269
 
541
- ```ruby
542
- # app/controllers/performance_report_controller.rb
270
+ def execute
271
+ # Get GraphQL query, etc
543
272
 
544
- class PerformanceReportController < ActionController::Base
545
- include Quilt::Performance::Reportable
546
- protect_from_forgery with: :null_session
273
+ result = MySchema.execute(query, operation_name: operation_name, variables: variables, context: context)
547
274
 
548
- def create
549
- # customize process_report's behaviour with a block
550
- process_report do |client|
551
- client.on_distribution do |name, value, tags|
552
- # We log out the details of each distribution that would be sent in production.
553
- Rails.logger.debug("Distribution: #{name}, #{value}, #{tags}")
554
- end
555
- end
556
-
557
- render json: { result: 'success' }, status: 200
558
- rescue ActionController::ParameterMissing => error
559
- render json: { error: error.message, status: 422 }
275
+ render(json: result)
560
276
  end
561
277
  end
562
278
  ```
563
279
 
564
- Now you can check your Rails console output and verify that metrics are reported as expected.
565
-
566
- ### Configure StatsD for production
567
-
568
- > Attention Shopifolk! If using `dev` your `StatsD` endpoint will already be configured for you in production. You should not need to do the following. ✨
280
+ ## Performance tracking a React app
569
281
 
570
- To tell `Quilt::Performance::Reportable` where to send it's distributions, setup the environment variables detailed [documentation](https://github.com/Shopify/statsd-instrument#configuration).
282
+ To setup performance tracking with your React app with `quilt_rails`.
283
+ Follow details guide [here](./docs/performance-tracking).
571
284
 
572
285
  ## API
573
286
 
574
- ### ReactRenderable
575
-
576
- The `ReactRenderable` mixin is intended to be used in Rails controllers, and provides only the `render_react` method. This method handles proxying to a running `@shopify/react-server`.
577
-
578
- ```ruby
579
- class ReactController < ApplicationController
580
- include Quilt::ReactRenderable
581
-
582
- def index
583
- render_react
584
- end
585
- end
586
- ```
587
-
588
- ### Performance
589
-
590
- #### Reportable
591
-
592
- The `Quilt::Performance::Reportable` mixin is intended to be used in Rails controllers, and provides only the `process_report` method. This method handles parsing an incoming report from [@shopify/react-performance's](https://www.npmjs.com/package/@shopify/react-performance) `<PerformanceReport />` component (or a custom report in the same format) and sending it to your application's StatsD endpoint as `distribution`s using [`StatsD-Instrument`](https://rubygems.org/gems/statsd-instrument).
593
-
594
- > **Note** `Quilt::Performance::Reportable` does not require you to use the `React::Renderable` mixin, React-Server, or even any server-side-rendering solution at all. It should work perfectly fine for applications using something like `sewing_kit_script_tag` based client-side-rendering.
595
-
596
- ```ruby
597
- class PerformanceController < ApplicationController
598
- include Quilt::Performance::Reportable
599
-
600
- def create
601
- process_report
602
- end
603
- end
604
- ```
605
-
606
- The params sent to the controller are expected to be of type `application/json`. Given the following example JSON sent by `@shopify/react-performance`,
607
-
608
- ```json
609
- {
610
- "connection": {
611
- "rtt": 100,
612
- "downlink": 2,
613
- "effectiveType": "3g",
614
- "type": "4g"
615
- },
616
- "navigations": [
617
- {
618
- "details": {
619
- "start": 12312312,
620
- "duration": 23924,
621
- "target": "/",
622
- "events": [
623
- {
624
- "type": "script",
625
- "start": 23123,
626
- "duration": 124
627
- },
628
- {
629
- "type": "style",
630
- "start": 23,
631
- "duration": 14
632
- }
633
- ],
634
- "result": 0
635
- },
636
- "metadata": {
637
- "index": 0,
638
- "supportsDetailedTime": true,
639
- "supportsDetailedEvents": true
640
- }
641
- }
642
- ],
643
- "events": [
644
- {
645
- "type": "ttfb",
646
- "start": 2,
647
- "duration": 1000
648
- }
649
- ]
650
- }
651
- ```
652
-
653
- given the the above controller input, the library would send the following metrics:
654
-
655
- ```ruby
656
- StatsD.distribution('time_to_first_byte', 2, tags: {
657
- browser_connection_type:'3g',
658
- })
659
- StatsD.distribution('time_to_first_byte', 2, tags: {
660
- browser_connection_type:'3g' ,
661
- })
662
- StatsD.distribution('navigation_complete', 23924, tags: {
663
- browser_connection_type:'3g' ,
664
- })
665
- StatsD.distribution('navigation_usable', 23924, tags: {
666
- browser_connection_type:'3g' ,
667
- })
668
- ```
669
-
670
- ##### Default Metrics
671
-
672
- The full list of metrics sent by default are as follows:
673
-
674
- ###### For full-page load
675
-
676
- - `AppName.time_to_first_byte`, representing the time from the start of the request to when the server began responding with data.
677
- - `AppName.time_to_first_paint`, representing the time from the start of the request to when the browser rendered anything to the screen.
678
- - `AppName.time_to_first_contentful_paint` representing the time from the start of the request to when the browser rendered meaningful content to the screen.
679
- - `AppName.dom_content_loaded` representing the time from the start of the request to when the browser fired the [DOMContentLoaded](https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event) event.
680
- - `AppName.dom_load` representing the time from the start of the request to when the browser fired the [window.load](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event) event.
681
-
682
- ###### For both full-page navigations and client-side page transitions
683
-
684
- - `AppName.navigation_usable`, representing the time it took before for the page to be rendered in a usable state. Usually this does not include data fetching or asynchronous tasks.
685
- - `AppName.navigation_complete` representing the time it took for the page to be fully loaded, including any data fetching which blocks above-the-fold content.
686
- - `AppName.navigation_download_size`, representing the total weight of all client-side assets (eg. CSS, JS, images). This will only be sent if there are any events with a `type` of `script` or `style`.
687
- - `AppName.navigation_cache_effectiveness`, representing what percentage of client-side assets (eg. CSS, JS, images) were returned from the browser's cache. This will only be sent if there are any events with a `type` of `script` or `style`.
688
-
689
- ##### Customizing `process_report` with a block
690
-
691
- The behaviour of `process_report` can be customized by manipulating the `Quilt::Performance::Client` instance yielded into its implicit block parameter.
692
-
693
- ```ruby
694
- process_report do |client|
695
- # client.on_distribution do ....
696
- end
697
- ```
698
-
699
- #### Client
700
-
701
- The `Quilt::Performance::Client` class is yielded into the block parameter for `process_report`, and is the primary API for customizing what metrics are sent for a given POST.
702
-
703
- ##### Client#on_distribution
704
-
705
- The `on_distribution` method takes a block which is run for each distribution (including custom ones) sent during `process_report`.
706
-
707
- The provided callback can be used to easily add logging or other side-effects to your measurements.
708
-
709
- ```ruby
710
- client.on_distribution do |metric_name, value, tags|
711
- Rails.logger.debug "#{metric_name}: #{value}, tags: #{tags}"
712
- end
713
- ```
714
-
715
- ##### Client#on_navigation
716
-
717
- The `on_navigation` method takes a block which is run once per navigation reported to the performance controller _before_ the default distributions for the navigation are sent.
718
-
719
- The provided callback can be used to add tags to the default `distributions` for a given navigation.
720
-
721
- ```ruby
722
- client.on_navigation do |navigation, tags|
723
- # add tags to be sent with each distribution for this navigation
724
- tags[:connection_rtt] = navigation.connection.rtt
725
- tags[:connection_type] = navigation.connection.type
726
- tags[:navigation_target] = navigation.target
727
-
728
- # add a tag to allow filtering out navigations that are too long
729
- # this is useful when you are unable to rule out missing performance marks on some pages
730
- tags[:too_long_dont_read] = navigation.duration > 30.seconds.in_milliseconds
731
- end
732
- ```
733
-
734
- It can also be used to compute and send entirely custom metrics.
735
-
736
- ```ruby
737
- client.on_navigation do |navigation, tags|
738
- # calculate and then send an additional distribution
739
- weight = navigation.events_with_size.reduce(0) do |total, event|
740
- total + event.size
741
- end
742
- client.distribution('navigation_total_resource_weight', weight, tags)
743
- end
744
- ```
745
-
746
- ##### Client#on_event
747
-
748
- The `on_event` method takes a block which is run once per event reported to the performance controller _before_ the default distributions for the event are sent.
749
-
750
- The provided callback can be used to add tags to the default `distributions` for a given event, or perform other side-effects.
751
-
752
- ```ruby
753
- client.on_event do |event, tags|
754
- # add tags to be sent with each distribution for this event
755
- tags[:connection_rtt] = event.connection.rtt
756
- tags[:connection_type] = event.connection.type
757
- end
758
- ```
759
-
760
- ### Engine
761
-
762
- `Quilt::Engine` provides:
763
-
764
- - a preconfigured `UiController` which consumes `ReactRenderable`
765
- - a preconfigured `PerformanceReportController` which consumes `Performance::Reportable`
766
- - a `/performance_report` route mapped to `performance_report#index`
767
- - a catch-all index route mapped to the `UiController#index`
768
-
769
- ```ruby
770
- # config/routes.rb
771
- Rails.application.routes.draw do
772
- # ...
773
- mount Quilt::Engine, at: '/my-front-end'
774
- end
775
- ```
776
-
777
- The above is the equivalent of
778
-
779
- ```ruby
780
- post '/my-front-end/performance_report', to: 'performance_report#create'
781
- get '/my-front-end/*path', to: 'ui#index'
782
- get '/my-front-end', to: 'ui#index'
783
- ```
784
-
785
- ### Configuration
786
-
787
- The `configure` method allows customization of the address the service will proxy to for UI rendering.
788
-
789
- ```ruby
790
- # config/initializers/quilt.rb
791
- Quilt.configure do |config|
792
- config.react_server_host = "localhost:3000"
793
- config.react_server_protocol = 'https'
794
- end
795
- ```
796
-
797
- ### StatsD environment variables
798
-
799
- The `Performance::Reportable` mixin uses [https://github.com/Shopify/statsd-instrument](StatsD-Instrument) to send distributions. For detailed instructions on configuring where it sends data see [the documentation](https://github.com/Shopify/statsd-instrument#configuration).
800
-
801
- ### Generators
802
-
803
- #### `quilt:install`
804
-
805
- Installs the Node dependencies, provide a basic React app (in TypeScript) and mounts the Quilt engine in `config/routes.rb`.
287
+ Find all features this gem offer in this [API doc](./docs/api).
806
288
 
807
- #### `sewing_kit:install`
289
+ ## FAQ
808
290
 
809
- Adds a basic `sewing-kit.config.ts` file.
291
+ Find your [here](./docs/FAQ).
@@ -2,52 +2,10 @@
2
2
 
3
3
  module Quilt
4
4
  class InstallGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- desc "This generator mounts the Quilt engine and adds a React app."
8
-
9
- def install_js_dependencies
10
- say "Installing @shopify/react-server and @shopify/sewing-kit dependencies"
11
- system("yarn add "\
12
- "@shopify/sewing-kit "\
13
- "@shopify/react-server "\
14
- "typescript "\
15
- "react "\
16
- "react-dom "\
17
- "@types/react "\
18
- "@types/react-dom") unless Rails.env.test?
19
- end
20
-
21
- def create_tsconfig
22
- tsconfig_path = "tsconfig.json"
23
-
24
- unless File.exist?(tsconfig_path)
25
- copy_file "tsconfig.json", tsconfig_path
26
-
27
- log(tsconfig_path, 'wrote')
28
- end
29
- end
30
-
31
- def create_app_file
32
- app_path = "app/ui/index.tsx"
33
-
34
- unless File.exist?(app_path)
35
- copy_file "App.tsx", app_path
36
-
37
- log("React App at #{app_path}", 'wrote')
38
- end
39
- end
40
-
41
- def create_route_file
42
- routes_path = "config/routes.rb"
43
-
44
- if File.exist?(routes_path)
45
- route "mount Quilt::Engine, at: '/'"
46
- else
47
- copy_file "routes.rb", routes_path
48
- end
49
-
50
- say "Added Quilt engine mount"
5
+ def run_all_generators
6
+ generate("quilt:rails_setup")
7
+ generate("quilt:react_setup")
8
+ generate("quilt:react_app")
51
9
  end
52
10
  end
53
11
  end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ This generator mounts the Quilt engine.
3
+
4
+ Example:
5
+ rails generate quilt:rails_setup
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quilt
4
+ class RailsSetupGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ desc "This generator mounts the Quilt engine and add Procfile."
8
+
9
+ def create_procfile_entry
10
+ procfile_path = "Procfile"
11
+
12
+ if File.exist?(procfile_path)
13
+ append_file(procfile_path, File.read(File.expand_path(find_in_source_paths(procfile_path))))
14
+ else
15
+ copy_file procfile_path
16
+ end
17
+ end
18
+
19
+ def create_route_file
20
+ routes_path = "config/routes.rb"
21
+
22
+ if File.exist?(routes_path)
23
+ route "mount Quilt::Engine, at: '/'"
24
+ else
25
+ copy_file "routes.rb", routes_path
26
+ end
27
+
28
+ say "Added Quilt engine mount"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ node: node build/server/main.js
2
+ web: bundle exec rails server -p $PORT -e $RAILS_ENV
@@ -0,0 +1,5 @@
1
+ Description:
2
+ This generator adds a React app.
3
+
4
+ Example:
5
+ rails generate quilt:react_app
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quilt
4
+ class ReactAppGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ desc "This generator adds a React app."
8
+
9
+ def create_app_file
10
+ copy_file("App.tsx", "app/ui/index.tsx")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ This generator adds Node dependencies necessary for a React app.
3
+
4
+ Example:
5
+ rails generate quilt:react_setup
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quilt
4
+ class ReactSetupGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ desc "This generator adds a React app."
8
+
9
+ def install_js_dependencies
10
+ say "Installing @shopify/react-server and @shopify/sewing-kit dependencies"
11
+ system("yarn add "\
12
+ "@shopify/sewing-kit "\
13
+ "@shopify/react-server "\
14
+ "typescript@~3.8.0 "\
15
+ "react@~16.11.0 "\
16
+ "react-dom@~16.11.0 "\
17
+ "@types/react@~16.9.0 "\
18
+ "@types/react-dom@~16.9.0 ") unless Rails.env.test?
19
+ end
20
+
21
+ def create_tsconfig
22
+ copy_file("tsconfig.json")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ function App() {
4
+ return <div>Hello Quilt</div>;
5
+ }
6
+
7
+ export default App;
@@ -2,7 +2,10 @@
2
2
  "extends": "@shopify/typescript-configs/application.json",
3
3
  "compilerOptions": {
4
4
  "baseUrl": ".",
5
- "rootDir": "."
5
+ "rootDir": ".",
6
+ "paths": {
7
+ "*": ["app/ui/*"]
8
+ }
6
9
  },
7
- "include": ["app/ui"]
10
+ "include": ["./config/*.ts", "./app/ui/**/*"]
8
11
  }
@@ -0,0 +1,5 @@
1
+ Description:
2
+ This generator runs all the generators
3
+
4
+ Example:
5
+ rails generate quilt_rails:install
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QuiltRails
4
+ class InstallGenerator < Rails::Generators::Base
5
+ def run_all_generators
6
+ generate("sewing_kit:install")
7
+ generate("quilt:install")
8
+ end
9
+ end
10
+ end
@@ -2,4 +2,4 @@ Description:
2
2
  This generator creates a sewing-kit config file.
3
3
 
4
4
  Example:
5
- rails generate sewing-kit:install
5
+ rails generate sewing_kit:install
@@ -6,16 +6,29 @@ module SewingKit
6
6
 
7
7
  desc "This generator creates a sewing-kit config file."
8
8
 
9
- def create_config
10
- config_path = "config/sewing-kit.config.ts"
9
+ def initialize(args, *options)
10
+ @application_name = Rails.application.class.module_parent.to_s.underscore
11
+ super(args, *options)
12
+ end
13
+
14
+ def create_package_json
15
+ package_json_path = "package.json"
16
+
17
+ copy_file(package_json_path)
18
+ gsub_file(package_json_path, "\${application_name}", @application_name)
19
+ end
20
+
21
+ def create_sk_config
22
+ sk_config_path = "config/sewing-kit.config.ts"
11
23
 
12
- if File.exist?(config_path)
13
- say "Sewing kit config already exists"
14
- else
15
- copy_file "sewing-kit.config.ts", config_path
24
+ copy_file("sewing-kit.config.ts", sk_config_path)
25
+ gsub_file(sk_config_path, "\${application_name}", @application_name)
26
+ end
16
27
 
17
- say "Sewing kit config"
18
- end
28
+ def create_config_files
29
+ copy_file(".editorconfig")
30
+ copy_file(".eslintignore")
31
+ copy_file(".prettierignore")
19
32
  end
20
33
  end
21
34
  end
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "${application_name}",
3
+ "private": true,
4
+ "sideEffects": false,
5
+ "scripts": {
6
+ "dev": "sewing-kit dev",
7
+ "check": "sewing-kit check",
8
+ "lint": "sewing-kit lint",
9
+ "type-check": "sewing-kit type-check",
10
+ "nuke": "sewing-kit nuke",
11
+ "test": "sewing-kit test"
12
+ },
13
+ "eslintConfig": {
14
+ "extends": [
15
+ "plugin:@shopify/typescript",
16
+ "plugin:@shopify/react",
17
+ "plugin:@shopify/prettier",
18
+ "plugin:@shopify/jest",
19
+ "plugin:@shopify/polaris"
20
+ ]
21
+ },
22
+ "prettier": "@shopify/prettier-config",
23
+ "stylelint": {
24
+ "extends": [
25
+ "@shopify/stylelint-plugin/prettier"
26
+ ]
27
+ },
28
+ "resolutions": {
29
+ "babel-plugin-dynamic-import-node": "2.3.0"
30
+ }
31
+ }
@@ -1,7 +1,11 @@
1
1
  /* eslint-env node */
2
2
 
3
- import {Env, Plugins} from '@shopify/sewing-kit';
3
+ import {Plugins} from '@shopify/sewing-kit';
4
4
 
5
- export default function sewingKitConfig(_plugins: Plugins, _env: Env) {
6
- return {};
5
+ export default function sewingKitConfig(plugins: Plugins) {
6
+ return {
7
+ name: "${application_name}",
8
+ plugins: [
9
+ ],
10
+ };
7
11
  }
@@ -9,4 +9,5 @@ require "quilt_rails/configuration"
9
9
  require "quilt_rails/react_renderable"
10
10
  require "quilt_rails/performance"
11
11
  require "quilt_rails/trusted_ui_server_csrf_strategy"
12
+ require "quilt_rails/header_csrf_strategy"
12
13
  require "quilt_rails/monkey_patches/active_support_reloader" if Rails.env.development?
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quilt
4
+ class HeaderCsrfStrategy
5
+ HEADER = "x-shopify-react-xhr"
6
+ HEADER_VALUE = "1"
7
+
8
+ def initialize(controller)
9
+ @controller = controller
10
+ end
11
+
12
+ def handle_unverified_request
13
+ raise NoSameSiteHeaderError unless same_site?
14
+ end
15
+
16
+ private
17
+
18
+ def same_site?
19
+ @controller.request.headers[HEADER] == HEADER_VALUE
20
+ end
21
+
22
+ def fallback_handler
23
+ ActionController::RequestForgeryProtection::ProtectionMethods::Exception.new(@controller)
24
+ end
25
+
26
+ class NoSameSiteHeaderError < StandardError
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
31
+ end
32
+ end
33
+ end
34
+ end
@@ -9,7 +9,7 @@ module Quilt
9
9
 
10
10
  def self.from_params(params)
11
11
  params.transform_keys! { |key| key.underscore.to_sym }
12
- params.require(:connection)
12
+ params[:connection] = { effectiveType: 'unknown' } if params[:connection].blank?
13
13
 
14
14
  connection = Connection.from_params(params[:connection])
15
15
 
@@ -4,8 +4,15 @@ module Quilt
4
4
  module Performance
5
5
  module Reportable
6
6
  def process_report(&block)
7
- params.transform_keys! { |key| key.underscore.to_sym }
8
- Client.send!(Report.from_params(params), &block)
7
+ Client.send!(Report.from_params(normalized_params), &block)
8
+ end
9
+
10
+ private
11
+
12
+ def normalized_params
13
+ return params unless request.content_type == 'text/plain'
14
+
15
+ ActionController::Parameters.new(JSON.parse(request.body.read))
9
16
  end
10
17
  end
11
18
  end
@@ -6,29 +6,29 @@ module Quilt
6
6
  module ReactRenderable
7
7
  include ReverseProxy::Controller
8
8
 
9
- def render_react(headers: {})
9
+ def render_react(headers: {}, data: {})
10
10
  raise DoNotIntegrationTestError if Rails.env.test?
11
11
 
12
12
  # Allow concurrent loading to prevent this thread from blocking class
13
13
  # loading in controllers called by the Node server.
14
14
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
15
- call_proxy(headers)
15
+ call_proxy(headers, data)
16
16
  end
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def call_proxy(headers)
21
+ def call_proxy(headers, data)
22
22
  if defined? ShopifySecurityBase
23
23
  ShopifySecurityBase::HTTPHostRestriction.whitelist([Quilt.configuration.react_server_host]) do
24
- proxy(headers)
24
+ proxy(headers, data)
25
25
  end
26
26
  else
27
- proxy(headers)
27
+ proxy(headers, data)
28
28
  end
29
29
  end
30
30
 
31
- def proxy(headers)
31
+ def proxy(headers, data)
32
32
  url = "#{Quilt.configuration.react_server_protocol}://#{Quilt.configuration.react_server_host}"
33
33
  Quilt::Logger.log("[ReactRenderable] proxying to React server at #{url}")
34
34
 
@@ -37,7 +37,11 @@ module Quilt
37
37
  end
38
38
 
39
39
  begin
40
- reverse_proxy(url, headers: headers.merge('X-CSRF-Token': form_authenticity_token)) do |callbacks|
40
+
41
+ reverse_proxy(
42
+ url,
43
+ headers: headers.merge('X-CSRF-Token': form_authenticity_token, 'X-Quilt-Data': headers.merge(data).to_json)
44
+ ) do |callbacks|
41
45
  callbacks.on_response do |status_code, _response|
42
46
  Quilt::Logger.log("[ReactRenderable] #{url} returned #{status_code}")
43
47
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Quilt
3
- VERSION = "1.10.0"
3
+ VERSION = "1.13.0"
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: 1.10.0
4
+ version: 1.13.0
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-01-30 00:00:00.000000000 Z
11
+ date: 2020-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -95,15 +95,27 @@ files:
95
95
  - config/routes.rb
96
96
  - lib/generators/quilt/USAGE
97
97
  - lib/generators/quilt/install_generator.rb
98
- - lib/generators/quilt/templates/App.tsx
99
- - lib/generators/quilt/templates/routes.rb
100
- - lib/generators/quilt/templates/tsconfig.json
98
+ - lib/generators/quilt/rails_setup/USAGE
99
+ - lib/generators/quilt/rails_setup/rails_setup_generator.rb
100
+ - lib/generators/quilt/rails_setup/templates/Procfile
101
+ - lib/generators/quilt/rails_setup/templates/routes.rb
102
+ - lib/generators/quilt/react_app/USAGE
103
+ - lib/generators/quilt/react_app/react_app_generator.rb
104
+ - lib/generators/quilt/react_app/templates/App.tsx
105
+ - lib/generators/quilt/react_setup/USAGE
106
+ - lib/generators/quilt/react_setup/react_setup_generator.rb
107
+ - lib/generators/quilt/react_setup/templates/App.tsx
108
+ - lib/generators/quilt/react_setup/templates/tsconfig.json
109
+ - lib/generators/quilt_rails/USAGE
110
+ - lib/generators/quilt_rails/install_generator.rb
101
111
  - lib/generators/sewing_kit/USAGE
102
112
  - lib/generators/sewing_kit/install_generator.rb
113
+ - lib/generators/sewing_kit/templates/package.json
103
114
  - lib/generators/sewing_kit/templates/sewing-kit.config.ts
104
115
  - lib/quilt_rails.rb
105
116
  - lib/quilt_rails/configuration.rb
106
117
  - lib/quilt_rails/engine.rb
118
+ - lib/quilt_rails/header_csrf_strategy.rb
107
119
  - lib/quilt_rails/logger.rb
108
120
  - lib/quilt_rails/monkey_patches/active_support_reloader.rb
109
121
  - lib/quilt_rails/performance.rb
@@ -121,7 +133,8 @@ files:
121
133
  homepage: https://github.com/Shopify/quilt/tree/master/gems/quilt_rails
122
134
  licenses:
123
135
  - MIT
124
- metadata: {}
136
+ metadata:
137
+ allowed_push_host: https://rubygems.org
125
138
  post_install_message:
126
139
  rdoc_options: []
127
140
  require_paths: