quilt_rails 1.12.0 → 3.0.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: 801ba2d2d619ae8c7a6714bf69f06a8edeb2ae7edc1d0255f8e9df57755be211
4
- data.tar.gz: bc17c1c983af45c573a589c1c9ae84d241b1af242a996d33998aa360ef560ae7
3
+ metadata.gz: c38993bec98aa474ec2a1833abc0ab868085cba9a4521eb2007bfcaca6c12194
4
+ data.tar.gz: be6a1c6e8a9ff87a8030d118bbe33fc5779d506070ac05d0a817653980044023
5
5
  SHA512:
6
- metadata.gz: a3f14ae99c0e106a9b3a71606c7489c20c99446a91e8ddfe20ed7b294bab50a358927cb6f6e70ed295b814fe7b520039e7f14dcb68cb1a2d7395b934f92f3c3d
7
- data.tar.gz: 21b493fecf60259353c8df068c219d51de3eb3859cf4889bc8f44b6c4ea8f2993c6e08d1f58dad0585bf98d9f10128a4001e651528afb779d3008f5b8ca902b1
6
+ metadata.gz: 9abee4ef36e0de59346a5a0310c50074cb54f3bb6b59f228cb3aa65c2307a0ef05b719bfe391849c98e866256bc3d9380e14e41a06310c5fb0c66d02ab87f259
7
+ data.tar.gz: 6fc3bacb20732cae063fc83cbf4761ca2db8f5fdc952796187f640b0058ab8cf3904e171fdc755b8b4db1d1d6a2d5e6ed19a4d9eb005b02e2bb4a562ef67317b
data/README.md CHANGED
@@ -8,55 +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
- - [Generate Rails boilerplate](#generate-rails-boilerplate)
15
- - [Install Dependencies](#install-dependencies)
16
- - [Setup the Rails app](#setup-the-rails-app)
17
- - [Add JavaScript](#add-javascript)
18
- - [Run the server](#run-the-server)
19
14
  - [Application Layout](#application-layout)
20
15
  - [Advanced Use](#advanced-use)
21
16
  - [Testing](#testing)
22
17
  - [Interacting with the request and response in React code](#interacting-with-the-request-and-response-in-react-code)
23
18
  - [Dealing with isomorphic state](#dealing-with-isomorphic-state)
24
- - [Customizing the node server](#customizing-the-node-server)
19
+ - [Customizing the Node server](#customizing-the-node-server)
25
20
  - [Fixing rejected CSRF tokens for new user sessions](#fixing-rejected-csrf-tokens-for-new-user-sessions)
26
21
  - [Performance tracking a React app](#performance-tracking-a-react-app)
27
- - [Install dependencies](#install-dependencies)
28
- - [Setup an endpoint for performance reports](setup-an-endpoint-for-performance-reports)
29
- - [Add annotations](#add-annotations)
30
- - [Send the report](#send-the-report)
31
- - [Verify in development](#verify-in-development)
32
- - [Configure StatsD for production](#configure-statsd-for-production)
33
22
  - [API](#api)
34
- - [ReactRenderable](#reactrenderable)
35
- - [Performance](#performance)
36
- - [Engine](#engine)
37
- - [Generators](#generators)
38
23
 
39
24
  ## Server-side-rendering
40
25
 
41
- ### Alpha functionality - do not use in high-traffic production applications
42
-
43
- **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:
44
-
45
- - Workshop applications
46
- - Proof of concept applications
47
- - Low traffic applications
48
-
49
- For a description of the current architecture's problems, see [this Github comment](https://github.com/Shopify/quilt/issues/1059#issuecomment-539195340).
50
-
51
- The ["decide on a scalable quilt_rails architecture" issue](https://github.com/Shopify/quilt/issues/1100) will track discussion of future architectures.
52
-
53
- To scale up existing quilt_rails applications, skip server-side queries in your components. e.g.:
54
-
55
- ```ts
56
- useQuery(MyQuery, {
57
- skip: typeof document === 'undefined',
58
- });
59
- ```
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.
60
27
 
61
28
  ### Quick start
62
29
 
@@ -64,21 +31,28 @@ Using the magic of generators, we can spin up a basic app with a few console com
64
31
 
65
32
  #### Generate Rails boilerplate
66
33
 
67
- `dev init`
68
-
34
+ With access to [`dev`](https://github.com/Shopify/dev), you can use `dev init` to scaffold out a Rails application.
69
35
  When prompted, choose `rails`. This will generate a basic Rails application scaffold.
70
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.
40
+
71
41
  #### Add Ruby dependencies
72
42
 
73
43
  `bundle add sewing_kit quilt_rails`
74
44
 
75
45
  This will install our ruby dependencies and update the project's gemfile.
76
46
 
77
- #### Generate Quilt boilerplate
47
+ #### Generate app boilerplate
48
+
49
+ `rails generate sewing_kit:install`
50
+
51
+ This will generate a package.json file with common sewing-kit script tasks, default lint, format configuration; a sewing-kit configuration file, and other project default configurations.
78
52
 
79
53
  `rails generate quilt:install`
80
54
 
81
- This will install the Node dependencies, provide a basic React app (in TypeScript) and mounts the Quilt engine inside of `config/routes.rb`.
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.
82
56
 
83
57
  #### Try it out
84
58
 
@@ -91,173 +65,7 @@ Will run the application, starting up both servers and compiling assets.
91
65
 
92
66
  ### Manual installation
93
67
 
94
- An application can also be setup manually using the following steps.
95
-
96
- [Generate Rails boilerplate](#generate-rails-boilerplate)
97
-
98
- #### Install dependencies
99
-
100
- ```sh
101
- # Add ruby dependencies
102
- bundle add sewing_kit quilt_rails
103
-
104
- # Add core Node dependencies
105
- yarn add @shopify/sewing-kit @shopify/react-server
106
-
107
- # Add React
108
- yarn add react react-dom
109
-
110
- # Add Typescript
111
- yarn add typescript @types/react @types/react-dom
112
- ```
113
-
114
- ##### Define typescript config
115
-
116
- ```json
117
- // tsconfig.json
118
- {
119
- "extends": "@shopify/typescript-configs/application.json",
120
- "compilerOptions": {
121
- "baseUrl": ".",
122
- "rootDir": "."
123
- },
124
- "include": ["app/ui"]
125
- }
126
- ```
127
-
128
- ```sh
129
- yarn
130
- dev up
131
- ```
132
-
133
- #### Setup the Rails app
134
-
135
- There are 2 ways to consume this package.
136
-
137
- ##### Option 1: Mount the Engine
138
-
139
- Add the engine to `routes.rb`.
140
-
141
- ```ruby
142
- # config/routes.rb
143
- Rails.application.routes.draw do
144
- # ...
145
- mount Quilt::Engine, at: '/'
146
- end
147
- ```
148
-
149
- If only a sub-section of routes should respond with the React App, it can be configured using the `at` parameter.
150
-
151
- ```ruby
152
- # config/routes.rb
153
- Rails.application.routes.draw do
154
- # ...
155
- mount Quilt::Engine, at: '/path/to/react'
156
- end
157
- ```
158
-
159
- ##### Option 2: Add a React controller and routes
160
-
161
- Create a `ReactController` to handle react requests.
162
-
163
- ```ruby
164
- class ReactController < ApplicationController
165
- include Quilt::ReactRenderable
166
-
167
- def index
168
- render_react
169
- end
170
- end
171
- ```
172
-
173
- Add routes to default to the `ReactController`.
174
-
175
- ```ruby
176
- get '/*path', to: 'react#index'
177
- root 'react#index'
178
- ```
179
-
180
- #### Add JavaScript
181
-
182
- `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.
183
-
184
- We will add a basic entrypoint using React with some HTML.
185
-
186
- ```tsx
187
- // app/ui/index.tsx
188
-
189
- import React from 'react';
190
-
191
- function App() {
192
- return <h1>My application ❤️</h1>;
193
- }
194
-
195
- export default App;
196
- ```
197
-
198
- #### Run the server
199
-
200
- `dev server`
201
-
202
- Will run the application, starting up both servers and compiling assets.
203
-
204
- ### Application layout
205
-
206
- #### Minimal
207
-
208
- 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.
209
-
210
- ```
211
- ├── Gemfile (must contain "gem 'sewing_kit" and "gem 'quilt_rails'")
212
- ├── package.json (must specify '@shopify/sewing-kit' and `@shopify/react-server` as 'dependencies')
213
-
214
- └── app
215
- └── ui
216
- │ └─- index.{js|ts} (exports a React component)
217
- └── controllers
218
- └─- react_controller.rb (see above)
219
- ```
220
-
221
- #### Rails and React
222
-
223
- A more complex application will want a more complex layout. The following shows scalable locations for:
224
-
225
- - Global SCSS settings
226
- - App sections (roughly analogous to Rails routes)
227
- - Components
228
- - Co-located CSS modules
229
- - Co-located unit tests
230
- - Test setup files
231
-
232
- ```
233
- └── app
234
- └── ui
235
- ├─- index.{js|ts} (exports a React component)
236
- ├── styles (optional)
237
- └── shared.scss (common functions/mixins you want available in every scss file. Requires configuring `plugin.sass`'s `autoInclude` option in `sewing-kit.config.js`)
238
-
239
- └── tests (optional)
240
- │ └── each-test.{js|ts}
241
- │ └── setup.{js|ts}
242
- └── features (optional)
243
- ├── App
244
- │ ├── index.{js|ts}
245
- │ ├── App.{js|ts}x
246
- │ └── tests
247
- │ └── App.test.{js|ts}x
248
-
249
- ├-─ MyComponent
250
- │ ├-─ index.{js|ts}
251
- │ ├-─ MyComponent.{js|ts}x
252
- │ ├── MyComponent.scss (optional; component-scoped CSS styles, mixins, etc)
253
- │ └── tests
254
- │ └── MyComponent.test.{js|ts}x
255
-
256
- └── sections (optional; container views that compose presentation components into UI blocks)
257
- └── Home
258
- ├-─ index.{js|ts}
259
- └── Home.{js|ts}
260
- ```
68
+ Follow [this guide](./docs/manual-installation.md) on how to do manual setup without the generator.
261
69
 
262
70
  ### Advanced use
263
71
 
@@ -329,9 +137,39 @@ function App() {
329
137
  export default App;
330
138
  ```
331
139
 
140
+ ##### Example: sending custom headers from Rails controller
141
+
142
+ 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.
143
+
144
+ ```ruby
145
+ class ReactController < ApplicationController
146
+ include Quilt::ReactRenderable
147
+
148
+ def index
149
+ render_react(headers: {'x-custom-header': 'header-value-a'})
150
+ end
151
+ end
152
+ ```
153
+
154
+ Headers can be accessed during server-side-rendering with the `useRequestHeader` hook from `@shopify/react-network`.
155
+
156
+ ```tsx
157
+ // app/ui/index.tsx
158
+
159
+ import React from 'react';
160
+ import {useRequestHeader} from '@shopify/react-network';
161
+
162
+ function App() {
163
+ const header = useRequestHeader('x-custom-header');
164
+ return <h1>Data: {header}</h1>;
165
+ }
166
+
167
+ export default App;
168
+ ```
169
+
332
170
  ##### Example: sending custom data from Rails controller
333
171
 
334
- In some cases you may want to send custom headers or basic data from Rails to your React server. Quilt facilitates this case by providing consumers with a `headers` and `data` argument on the `render_react` call.
172
+ 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.
335
173
 
336
174
  **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.
337
175
 
@@ -340,29 +178,23 @@ class ReactController < ApplicationController
340
178
  include Quilt::ReactRenderable
341
179
 
342
180
  def index
343
- render_react(headers: {'x-custom-header': 'header-value-a'}, data: {'some_id': 123})
181
+ render_react(data: {'some_id': 123})
344
182
  end
345
183
  end
346
184
  ```
347
185
 
348
- The React server will serialize the provided quilt data using `x-quilt-data` as the ID. You can then get this serialized data on the client with `getSerialized` from `@shopify/react-html`.
186
+ If using the webpack plugin, this will be automatically passed into your application as the `data` prop.
349
187
 
350
188
  ```tsx
351
189
  // app/ui/index.tsx
352
190
 
353
191
  import React from 'react';
354
- import {getSerialized} from '@shopify/react-html';
355
-
356
- const IS_CLIENT = typeof window !== 'undefined';
357
-
358
- function App() {
359
- // get `x-quilt-data` from the request that was sent through Rails ReactController
360
- const quiltData = IS_CLIENT ? getSerialized<{[key: string]: any}>('x-quilt-data') : null;
361
192
 
362
- // Logs {"x-custom-header":"header-value-a","some_id":123}
363
- console.log(quiltData);
193
+ function App({data}: {data: Record<string, any>}) {
194
+ // Logs {"some_id":123}
195
+ console.log(data);
364
196
 
365
- return <h1>Data: {quiltData}</h1>;
197
+ return <h1>Data: {data}</h1>;
366
198
  }
367
199
 
368
200
  export default App;
@@ -392,9 +224,9 @@ With SSR enabled React apps, state must be serialized on the server and deserial
392
224
 
393
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).
394
226
 
395
- #### Customizing the node server
227
+ #### Customizing the Node server
396
228
 
397
- 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.
229
+ By default, sewing-kit bundles in [`@shopify/react-server/webpack-plugin`](../../packages/react-server/README.md#webpack-plugin) for `quilt_rails` applications to get apps up and running fast without needing to manually write any Node server code.
398
230
 
399
231
  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.
400
232
 
@@ -451,361 +283,13 @@ end
451
283
 
452
284
  ## Performance tracking a React app
453
285
 
454
- 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.
455
-
456
- ### Install dependencies
457
-
458
- 1. Install the gem (if your app is not already using `quilt_rails`).
459
-
460
- ```bash
461
- bundle add quilt_rails
462
- ```
463
-
464
- 2. Install `@shopify/react-performance`, the library we will use to annotate our React application and send performance reports to our server.
465
-
466
- ```bash
467
- yarn add @shopify/react-performance
468
- ```
469
-
470
- ### Setup an endpoint for performance reports
471
-
472
- 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.
473
-
474
- 1. Add a `PerformanceController` and the corresponding routes to your Rails app.
475
-
476
- ```ruby
477
- # app/controllers/performance_report_controller.rb
478
-
479
- class PerformanceReportController < ActionController::Base
480
- include Quilt::Performance::Reportable
481
- protect_from_forgery with: :null_session
482
-
483
- def create
484
- process_report
485
-
486
- render(json: { result: 'success' }, status: 200)
487
- rescue ActionController::ParameterMissing => error
488
- render(json: { error: error.message }, status: 422)
489
- end
490
- end
491
- ```
492
-
493
- 2. Add a route pointing at the controller.
494
-
495
- ```ruby
496
- # config/routes.rb
497
-
498
- post '/performance_report', to: 'performance_report#create'
499
-
500
- # rest of routes
501
- ```
502
-
503
- ### Add annotations
504
-
505
- Add a [`usePerformanceMark`](https://github.com/Shopify/quilt/tree/master/packages/react-performance#useperformancemark) call to each of your route-level components.
506
-
507
- ```tsx
508
- // app/ui/features/Home/Home.tsx
509
- import {usePerformanceMark} from '@shopify/react-performance';
510
-
511
- export function Home() {
512
- // tell the library the page has finished rendering completely
513
- usePerformanceMark('complete', 'Home');
514
-
515
- return <>{/* your Home page JSX goes here*/}</>;
516
- }
517
- ```
518
-
519
- ### Send the report
520
-
521
- Add a [`usePerformanceReport`](https://github.com/Shopify/quilt/tree/master/packages/react-performance#usePerformanceReport) call to your top-level `<App />` component.
522
-
523
- ```tsx
524
- // app/ui/foundation/App/App.tsx
525
- import {usePerformanceReport} from '@shopify/react-performance';
526
-
527
- export function App() {
528
- // send the report to the server
529
- usePerformanceReport('/performance_report');
530
-
531
- return <>{/* your app JSX goes here*/}</>;
532
- }
533
- ```
534
-
535
- 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).
536
-
537
- ### Verify in development
538
-
539
- 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.
540
-
541
- 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.
542
-
543
- ```ruby
544
- # app/controllers/performance_report_controller.rb
545
-
546
- class PerformanceReportController < ActionController::Base
547
- include Quilt::Performance::Reportable
548
- protect_from_forgery with: :null_session
549
-
550
- def create
551
- # customize process_report's behaviour with a block
552
- process_report do |client|
553
- client.on_distribution do |name, value, tags|
554
- # We log out the details of each distribution that would be sent in production.
555
- Rails.logger.debug("Distribution: #{name}, #{value}, #{tags}")
556
- end
557
- end
558
-
559
- render json: { result: 'success' }, status: 200
560
- rescue ActionController::ParameterMissing => error
561
- render json: { error: error.message, status: 422 }
562
- end
563
- end
564
- ```
565
-
566
- Now you can check your Rails console output and verify that metrics are reported as expected.
567
-
568
- ### Configure StatsD for production
569
-
570
- > Attention Shopifolk! If using `dev` your `StatsD` endpoint will already be configured for you in production. You should not need to do the following. ✨
571
-
572
- To tell `Quilt::Performance::Reportable` where to send it's distributions, setup the environment variables detailed [documentation](https://github.com/Shopify/statsd-instrument#configuration).
286
+ To setup performance tracking with your React app with `quilt_rails`.
287
+ Follow details guide [here](./docs/performance-tracking.md).
573
288
 
574
289
  ## API
575
290
 
576
- ### ReactRenderable
577
-
578
- 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`.
579
-
580
- ```ruby
581
- class ReactController < ApplicationController
582
- include Quilt::ReactRenderable
583
-
584
- def index
585
- render_react
586
- end
587
- end
588
- ```
589
-
590
- ### Performance
591
-
592
- #### Reportable
593
-
594
- 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).
595
-
596
- > **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.
597
-
598
- ```ruby
599
- class PerformanceController < ApplicationController
600
- include Quilt::Performance::Reportable
601
-
602
- def create
603
- process_report
604
- end
605
- end
606
- ```
607
-
608
- The params sent to the controller are expected to be of type `application/json`. Given the following example JSON sent by `@shopify/react-performance`,
609
-
610
- ```json
611
- {
612
- "connection": {
613
- "rtt": 100,
614
- "downlink": 2,
615
- "effectiveType": "3g",
616
- "type": "4g"
617
- },
618
- "navigations": [
619
- {
620
- "details": {
621
- "start": 12312312,
622
- "duration": 23924,
623
- "target": "/",
624
- "events": [
625
- {
626
- "type": "script",
627
- "start": 23123,
628
- "duration": 124
629
- },
630
- {
631
- "type": "style",
632
- "start": 23,
633
- "duration": 14
634
- }
635
- ],
636
- "result": 0
637
- },
638
- "metadata": {
639
- "index": 0,
640
- "supportsDetailedTime": true,
641
- "supportsDetailedEvents": true
642
- }
643
- }
644
- ],
645
- "events": [
646
- {
647
- "type": "ttfb",
648
- "start": 2,
649
- "duration": 1000
650
- }
651
- ]
652
- }
653
- ```
654
-
655
- given the the above controller input, the library would send the following metrics:
656
-
657
- ```ruby
658
- StatsD.distribution('time_to_first_byte', 2, tags: {
659
- browser_connection_type:'3g',
660
- })
661
- StatsD.distribution('time_to_first_byte', 2, tags: {
662
- browser_connection_type:'3g' ,
663
- })
664
- StatsD.distribution('navigation_complete', 23924, tags: {
665
- browser_connection_type:'3g' ,
666
- })
667
- StatsD.distribution('navigation_usable', 23924, tags: {
668
- browser_connection_type:'3g' ,
669
- })
670
- ```
671
-
672
- ##### Default Metrics
673
-
674
- The full list of metrics sent by default are as follows:
675
-
676
- ###### For full-page load
677
-
678
- - `AppName.time_to_first_byte`, representing the time from the start of the request to when the server began responding with data.
679
- - `AppName.time_to_first_paint`, representing the time from the start of the request to when the browser rendered anything to the screen.
680
- - `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.
681
- - `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.
682
- - `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.
683
-
684
- ###### For both full-page navigations and client-side page transitions
685
-
686
- - `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.
687
- - `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.
688
- - `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`.
689
- - `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`.
690
-
691
- ##### Customizing `process_report` with a block
692
-
693
- The behaviour of `process_report` can be customized by manipulating the `Quilt::Performance::Client` instance yielded into its implicit block parameter.
694
-
695
- ```ruby
696
- process_report do |client|
697
- # client.on_distribution do ....
698
- end
699
- ```
700
-
701
- #### Client
702
-
703
- 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.
704
-
705
- ##### Client#on_distribution
706
-
707
- The `on_distribution` method takes a block which is run for each distribution (including custom ones) sent during `process_report`.
708
-
709
- The provided callback can be used to easily add logging or other side-effects to your measurements.
710
-
711
- ```ruby
712
- client.on_distribution do |metric_name, value, tags|
713
- Rails.logger.debug "#{metric_name}: #{value}, tags: #{tags}"
714
- end
715
- ```
716
-
717
- ##### Client#on_navigation
718
-
719
- 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.
720
-
721
- The provided callback can be used to add tags to the default `distributions` for a given navigation.
722
-
723
- ```ruby
724
- client.on_navigation do |navigation, tags|
725
- # add tags to be sent with each distribution for this navigation
726
- tags[:connection_rtt] = navigation.connection.rtt
727
- tags[:connection_type] = navigation.connection.type
728
- tags[:navigation_target] = navigation.target
729
-
730
- # add a tag to allow filtering out navigations that are too long
731
- # this is useful when you are unable to rule out missing performance marks on some pages
732
- tags[:too_long_dont_read] = navigation.duration > 30.seconds.in_milliseconds
733
- end
734
- ```
735
-
736
- It can also be used to compute and send entirely custom metrics.
737
-
738
- ```ruby
739
- client.on_navigation do |navigation, tags|
740
- # calculate and then send an additional distribution
741
- weight = navigation.events_with_size.reduce(0) do |total, event|
742
- total + event.size
743
- end
744
- client.distribution('navigation_total_resource_weight', weight, tags)
745
- end
746
- ```
747
-
748
- ##### Client#on_event
749
-
750
- 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.
751
-
752
- The provided callback can be used to add tags to the default `distributions` for a given event, or perform other side-effects.
753
-
754
- ```ruby
755
- client.on_event do |event, tags|
756
- # add tags to be sent with each distribution for this event
757
- tags[:connection_rtt] = event.connection.rtt
758
- tags[:connection_type] = event.connection.type
759
- end
760
- ```
761
-
762
- ### Engine
763
-
764
- `Quilt::Engine` provides:
765
-
766
- - a preconfigured `UiController` which consumes `ReactRenderable`
767
- - a preconfigured `PerformanceReportController` which consumes `Performance::Reportable`
768
- - a `/performance_report` route mapped to `performance_report#index`
769
- - a catch-all index route mapped to the `UiController#index`
770
-
771
- ```ruby
772
- # config/routes.rb
773
- Rails.application.routes.draw do
774
- # ...
775
- mount Quilt::Engine, at: '/my-front-end'
776
- end
777
- ```
778
-
779
- The above is the equivalent of
780
-
781
- ```ruby
782
- post '/my-front-end/performance_report', to: 'performance_report#create'
783
- get '/my-front-end/*path', to: 'ui#index'
784
- get '/my-front-end', to: 'ui#index'
785
- ```
786
-
787
- ### Configuration
788
-
789
- The `configure` method allows customization of the address the service will proxy to for UI rendering.
790
-
791
- ```ruby
792
- # config/initializers/quilt.rb
793
- Quilt.configure do |config|
794
- config.react_server_host = "localhost:3000"
795
- config.react_server_protocol = 'https'
796
- end
797
- ```
798
-
799
- ### StatsD environment variables
800
-
801
- 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).
802
-
803
- ### Generators
804
-
805
- #### `quilt:install`
806
-
807
- Installs the Node dependencies, provide a basic React app (in TypeScript) and mounts the Quilt engine in `config/routes.rb`.
291
+ Find all features this gem offer in this [API doc](./docs/api.md).
808
292
 
809
- #### `sewing_kit:install`
293
+ ## FAQ
810
294
 
811
- Adds a basic `sewing-kit.config.ts` file.
295
+ Find your [here](./docs/FAQ.md).
@@ -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
  }
@@ -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
@@ -37,10 +37,9 @@ module Quilt
37
37
  end
38
38
 
39
39
  begin
40
-
41
40
  reverse_proxy(
42
41
  url,
43
- headers: headers.merge('X-CSRF-Token': form_authenticity_token, 'X-Quilt-Data': headers.merge(data).to_json)
42
+ headers: headers.merge('X-CSRF-Token': form_authenticity_token, 'X-Quilt-Data': data.to_json)
44
43
  ) do |callbacks|
45
44
  callbacks.on_response do |status_code, _response|
46
45
  Quilt::Logger.log("[ReactRenderable] #{url} returned #{status_code}")
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Quilt
3
- VERSION = "1.12.0"
3
+ VERSION = "3.0.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.12.0
4
+ version: 3.0.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-05-07 00:00:00.000000000 Z
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -95,12 +95,17 @@ 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
101
- - lib/generators/sewing_kit/USAGE
102
- - lib/generators/sewing_kit/install_generator.rb
103
- - lib/generators/sewing_kit/templates/sewing-kit.config.ts
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
104
109
  - lib/quilt_rails.rb
105
110
  - lib/quilt_rails/configuration.rb
106
111
  - lib/quilt_rails/engine.rb
@@ -1,5 +0,0 @@
1
- Description:
2
- This generator creates a sewing-kit config file.
3
-
4
- Example:
5
- rails generate sewing-kit:install
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SewingKit
4
- class InstallGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- desc "This generator creates a sewing-kit config file."
8
-
9
- def create_config
10
- config_path = "config/sewing-kit.config.ts"
11
-
12
- if File.exist?(config_path)
13
- say "Sewing kit config already exists"
14
- else
15
- copy_file "sewing-kit.config.ts", config_path
16
-
17
- say "Sewing kit config"
18
- end
19
- end
20
- end
21
- end
@@ -1,7 +0,0 @@
1
- /* eslint-env node */
2
-
3
- import {Env, Plugins} from '@shopify/sewing-kit';
4
-
5
- export default function sewingKitConfig(_plugins: Plugins, _env: Env) {
6
- return {};
7
- }