react_on_rails 16.2.0.beta.3 → 16.2.0.beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +59 -0
- data/CONTRIBUTING.md +48 -0
- data/Gemfile.development_dependencies +1 -0
- data/Gemfile.lock +25 -2
- data/SWITCHING_CI_CONFIGS.md +55 -6
- data/Steepfile +51 -0
- data/bin/ci-rerun-failures +34 -11
- data/bin/ci-run-failed-specs +25 -1
- data/bin/ci-switch-config +254 -32
- data/bin/lefthook/check-trailing-newlines +2 -12
- data/bin/lefthook/eslint-lint +0 -10
- data/bin/lefthook/prettier-format +0 -10
- data/bin/lefthook/ruby-autofix +1 -5
- data/lib/react_on_rails/configuration.rb +56 -12
- data/lib/react_on_rails/controller.rb +3 -3
- data/lib/react_on_rails/doctor.rb +4 -2
- data/lib/react_on_rails/helper.rb +3 -3
- data/lib/react_on_rails/pro_helper.rb +2 -44
- data/lib/react_on_rails/react_component/render_options.rb +7 -7
- data/lib/react_on_rails/utils.rb +40 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/react_on_rails_pro/CHANGELOG.md +135 -29
- data/react_on_rails_pro/Gemfile.development_dependencies +1 -0
- data/react_on_rails_pro/Gemfile.lock +6 -3
- data/react_on_rails_pro/README.md +559 -38
- data/react_on_rails_pro/docs/installation.md +40 -22
- data/react_on_rails_pro/docs/node-renderer/basics.md +26 -19
- data/react_on_rails_pro/docs/node-renderer/js-configuration.md +24 -22
- data/react_on_rails_pro/docs/node-renderer/troubleshooting.md +2 -0
- data/react_on_rails_pro/lib/react_on_rails_pro/version.rb +1 -1
- data/react_on_rails_pro/package.json +1 -1
- data/react_on_rails_pro/packages/node-renderer/src/master/restartWorkers.ts +39 -17
- data/react_on_rails_pro/packages/node-renderer/src/master.ts +15 -4
- data/react_on_rails_pro/packages/node-renderer/src/shared/configBuilder.ts +44 -5
- data/react_on_rails_pro/packages/node-renderer/src/shared/utils.ts +4 -2
- data/react_on_rails_pro/packages/node-renderer/src/worker/handleGracefulShutdown.ts +49 -0
- data/react_on_rails_pro/packages/node-renderer/src/worker/vm.ts +3 -3
- data/react_on_rails_pro/packages/node-renderer/src/worker.ts +5 -2
- data/react_on_rails_pro/packages/node-renderer/tests/helper.ts +8 -8
- data/react_on_rails_pro/packages/node-renderer/tests/testingNodeRendererConfigs.js +1 -1
- data/react_on_rails_pro/packages/node-renderer/tests/worker.test.ts +19 -19
- data/react_on_rails_pro/rakelib/rbs.rake +47 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/cache.rbs +13 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/configuration.rbs +100 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/error.rbs +4 -0
- data/react_on_rails_pro/sig/react_on_rails_pro/utils.rbs +7 -0
- data/react_on_rails_pro/sig/react_on_rails_pro.rbs +5 -0
- data/react_on_rails_pro/spec/dummy/Gemfile.lock +6 -3
- data/react_on_rails_pro/spec/dummy/client/node-renderer.js +1 -1
- data/react_on_rails_pro/spec/dummy/spec/system/integration_spec.rb +16 -17
- data/sig/react_on_rails/controller.rbs +1 -1
- data/sig/react_on_rails/error.rbs +4 -0
- data/sig/react_on_rails/helper.rbs +2 -2
- data/sig/react_on_rails/json_parse_error.rbs +10 -0
- data/sig/react_on_rails/prerender_error.rbs +21 -0
- data/sig/react_on_rails/smart_error.rbs +28 -0
- data/sig/react_on_rails.rbs +3 -24
- metadata +14 -3
- data/lib/react_on_rails/pro_utils.rb +0 -37
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import cluster from 'cluster';
|
|
2
|
+
import { FastifyInstance } from './types';
|
|
3
|
+
import { SHUTDOWN_WORKER_MESSAGE } from '../shared/utils';
|
|
4
|
+
import log from '../shared/log';
|
|
5
|
+
|
|
6
|
+
const handleGracefulShutdown = (app: FastifyInstance) => {
|
|
7
|
+
const { worker } = cluster;
|
|
8
|
+
if (!worker) {
|
|
9
|
+
log.error('handleGracefulShutdown is called on master, expected to call it on worker only');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let activeRequestsCount = 0;
|
|
14
|
+
let isShuttingDown = false;
|
|
15
|
+
|
|
16
|
+
process.on('message', (msg) => {
|
|
17
|
+
if (msg === SHUTDOWN_WORKER_MESSAGE) {
|
|
18
|
+
log.debug('Worker #%d received graceful shutdown message', worker.id);
|
|
19
|
+
isShuttingDown = true;
|
|
20
|
+
if (activeRequestsCount === 0) {
|
|
21
|
+
log.debug('Worker #%d has no active requests, killing the worker', worker.id);
|
|
22
|
+
worker.destroy();
|
|
23
|
+
} else {
|
|
24
|
+
log.debug(
|
|
25
|
+
'Worker #%d has "%d" active requests, disconnecting the worker',
|
|
26
|
+
worker.id,
|
|
27
|
+
activeRequestsCount,
|
|
28
|
+
);
|
|
29
|
+
worker.disconnect();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
app.addHook('onRequest', (_req, _reply, done) => {
|
|
35
|
+
activeRequestsCount += 1;
|
|
36
|
+
done();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
app.addHook('onResponse', (_req, _reply, done) => {
|
|
40
|
+
activeRequestsCount -= 1;
|
|
41
|
+
if (isShuttingDown && activeRequestsCount === 0) {
|
|
42
|
+
log.debug('Worker #%d served all active requests and going to be killed', worker.id);
|
|
43
|
+
worker.destroy();
|
|
44
|
+
}
|
|
45
|
+
done();
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default handleGracefulShutdown;
|
|
@@ -112,7 +112,7 @@ export async function runInVM(
|
|
|
112
112
|
filePath: string,
|
|
113
113
|
vmCluster?: typeof cluster,
|
|
114
114
|
): Promise<RenderResult> {
|
|
115
|
-
const {
|
|
115
|
+
const { serverBundleCachePath } = getConfig();
|
|
116
116
|
|
|
117
117
|
try {
|
|
118
118
|
// Wait for VM creation if it's in progress
|
|
@@ -137,7 +137,7 @@ export async function runInVM(
|
|
|
137
137
|
const workerId = vmCluster?.worker?.id;
|
|
138
138
|
log.debug(`worker ${workerId ? `${workerId} ` : ''}received render request for bundle ${filePath} with code
|
|
139
139
|
${smartTrim(renderingRequest)}`);
|
|
140
|
-
const debugOutputPathCode = path.join(
|
|
140
|
+
const debugOutputPathCode = path.join(serverBundleCachePath, 'code.js');
|
|
141
141
|
log.debug(`Full code executed written to: ${debugOutputPathCode}`);
|
|
142
142
|
await writeFileAsync(debugOutputPathCode, renderingRequest);
|
|
143
143
|
}
|
|
@@ -165,7 +165,7 @@ ${smartTrim(renderingRequest)}`);
|
|
|
165
165
|
if (log.level === 'debug') {
|
|
166
166
|
log.debug(`result from JS:
|
|
167
167
|
${smartTrim(result)}`);
|
|
168
|
-
const debugOutputPathResult = path.join(
|
|
168
|
+
const debugOutputPathResult = path.join(serverBundleCachePath, 'result.json');
|
|
169
169
|
log.debug(`Wrote result to file: ${debugOutputPathResult}`);
|
|
170
170
|
await writeFileAsync(debugOutputPathResult, result);
|
|
171
171
|
}
|
|
@@ -17,6 +17,7 @@ import type { FastifyInstance, FastifyReply, FastifyRequest } from './worker/typ
|
|
|
17
17
|
import checkProtocolVersion from './worker/checkProtocolVersionHandler';
|
|
18
18
|
import authenticate from './worker/authHandler';
|
|
19
19
|
import { handleRenderRequest, type ProvidedNewBundle } from './worker/handleRenderRequest';
|
|
20
|
+
import handleGracefulShutdown from './worker/handleGracefulShutdown';
|
|
20
21
|
import {
|
|
21
22
|
errorResponseResult,
|
|
22
23
|
formatExceptionMessage,
|
|
@@ -117,7 +118,7 @@ export default function run(config: Partial<Config>) {
|
|
|
117
118
|
// getConfig():
|
|
118
119
|
buildConfig(config);
|
|
119
120
|
|
|
120
|
-
const {
|
|
121
|
+
const { serverBundleCachePath, logHttpLevel, port, fastifyServerOptions, workersCount } = getConfig();
|
|
121
122
|
|
|
122
123
|
const app = fastify({
|
|
123
124
|
http2: useHttp2 as true,
|
|
@@ -127,6 +128,8 @@ export default function run(config: Partial<Config>) {
|
|
|
127
128
|
...fastifyServerOptions,
|
|
128
129
|
});
|
|
129
130
|
|
|
131
|
+
handleGracefulShutdown(app);
|
|
132
|
+
|
|
130
133
|
// We shouldn't have unhandled errors here, but just in case
|
|
131
134
|
app.addHook('onError', (req, res, err, done) => {
|
|
132
135
|
// Not errorReporter.error so that integrations can decide how to log the errors.
|
|
@@ -148,7 +151,7 @@ export default function run(config: Partial<Config>) {
|
|
|
148
151
|
fileSize: Infinity,
|
|
149
152
|
},
|
|
150
153
|
onFile: async (part) => {
|
|
151
|
-
const destinationPath = path.join(
|
|
154
|
+
const destinationPath = path.join(serverBundleCachePath, 'uploads', part.filename);
|
|
152
155
|
// TODO: inline here
|
|
153
156
|
await saveMultipartFile(part, destinationPath);
|
|
154
157
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -35,23 +35,23 @@ export function getOtherFixtureAsset() {
|
|
|
35
35
|
return path.resolve(__dirname, `./fixtures/${ASSET_UPLOAD_OTHER_FILE}`);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function
|
|
38
|
+
export function serverBundleCachePath(testName: string) {
|
|
39
39
|
return path.resolve(__dirname, 'tmp', testName);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function setConfig(testName: string) {
|
|
43
43
|
buildConfig({
|
|
44
|
-
|
|
44
|
+
serverBundleCachePath: serverBundleCachePath(testName),
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function vmBundlePath(testName: string) {
|
|
49
|
-
return path.resolve(
|
|
49
|
+
return path.resolve(serverBundleCachePath(testName), `${BUNDLE_TIMESTAMP}`, `${BUNDLE_TIMESTAMP}.js`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function vmSecondaryBundlePath(testName: string) {
|
|
53
53
|
return path.resolve(
|
|
54
|
-
|
|
54
|
+
serverBundleCachePath(testName),
|
|
55
55
|
`${SECONDARY_BUNDLE_TIMESTAMP}`,
|
|
56
56
|
`${SECONDARY_BUNDLE_TIMESTAMP}.js`,
|
|
57
57
|
);
|
|
@@ -76,7 +76,7 @@ export function secondaryLockfilePath(testName: string) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export function uploadedBundleDir(testName: string) {
|
|
79
|
-
return path.resolve(
|
|
79
|
+
return path.resolve(serverBundleCachePath(testName), 'uploads');
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export function uploadedBundlePath(testName: string) {
|
|
@@ -96,11 +96,11 @@ export function uploadedAssetOtherPath(testName: string) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function assetPath(testName: string, bundleTimestamp: string) {
|
|
99
|
-
return path.resolve(
|
|
99
|
+
return path.resolve(serverBundleCachePath(testName), bundleTimestamp, ASSET_UPLOAD_FILE);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export function assetPathOther(testName: string, bundleTimestamp: string) {
|
|
103
|
-
return path.resolve(
|
|
103
|
+
return path.resolve(serverBundleCachePath(testName), bundleTimestamp, ASSET_UPLOAD_OTHER_FILE);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export async function createUploadedBundle(testName: string) {
|
|
@@ -129,7 +129,7 @@ export async function createAsset(testName: string, bundleTimestamp: string) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
export async function resetForTest(testName: string) {
|
|
132
|
-
await fsExtra.emptyDir(
|
|
132
|
+
await fsExtra.emptyDir(serverBundleCachePath(testName));
|
|
133
133
|
resetVM();
|
|
134
134
|
setConfig(testName);
|
|
135
135
|
}
|
|
@@ -8,7 +8,7 @@ if (fs.existsSync(BUNDLE_PATH)) {
|
|
|
8
8
|
|
|
9
9
|
const config = {
|
|
10
10
|
// This is the default but avoids searching for the Rails root
|
|
11
|
-
|
|
11
|
+
serverBundleCachePath: BUNDLE_PATH,
|
|
12
12
|
port: env.RENDERER_PORT || 3800, // Listen at RENDERER_PORT env value or default port 3800
|
|
13
13
|
logLevel: env.RENDERER_LOG_LEVEL || 'info',
|
|
14
14
|
|
|
@@ -15,14 +15,14 @@ import {
|
|
|
15
15
|
getFixtureAsset,
|
|
16
16
|
getOtherFixtureAsset,
|
|
17
17
|
createAsset,
|
|
18
|
-
|
|
18
|
+
serverBundleCachePath,
|
|
19
19
|
assetPath,
|
|
20
20
|
assetPathOther,
|
|
21
21
|
} from './helper';
|
|
22
22
|
|
|
23
23
|
const testName = 'worker';
|
|
24
24
|
const createVmBundleForTest = () => createVmBundle(testName);
|
|
25
|
-
const
|
|
25
|
+
const serverBundleCachePathForTest = () => serverBundleCachePath(testName);
|
|
26
26
|
|
|
27
27
|
const gemVersion = packageJson.version;
|
|
28
28
|
const { protocolVersion } = packageJson;
|
|
@@ -41,7 +41,7 @@ describe('worker', () => {
|
|
|
41
41
|
|
|
42
42
|
test('POST /bundles/:bundleTimestamp/render/:renderRequestDigest when bundle is provided and did not yet exist', async () => {
|
|
43
43
|
const app = worker({
|
|
44
|
-
|
|
44
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
const form = formAutoContent({
|
|
@@ -69,7 +69,7 @@ describe('worker', () => {
|
|
|
69
69
|
|
|
70
70
|
test('POST /bundles/:bundleTimestamp/render/:renderRequestDigest', async () => {
|
|
71
71
|
const app = worker({
|
|
72
|
-
|
|
72
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
const form = formAutoContent({
|
|
@@ -105,7 +105,7 @@ describe('worker', () => {
|
|
|
105
105
|
await createVmBundleForTest();
|
|
106
106
|
|
|
107
107
|
const app = worker({
|
|
108
|
-
|
|
108
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
109
109
|
password: 'password',
|
|
110
110
|
});
|
|
111
111
|
|
|
@@ -132,7 +132,7 @@ describe('worker', () => {
|
|
|
132
132
|
await createVmBundleForTest();
|
|
133
133
|
|
|
134
134
|
const app = worker({
|
|
135
|
-
|
|
135
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
136
136
|
password: 'password',
|
|
137
137
|
});
|
|
138
138
|
|
|
@@ -159,7 +159,7 @@ describe('worker', () => {
|
|
|
159
159
|
await createVmBundleForTest();
|
|
160
160
|
|
|
161
161
|
const app = worker({
|
|
162
|
-
|
|
162
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
163
163
|
password: 'my_password',
|
|
164
164
|
});
|
|
165
165
|
|
|
@@ -187,7 +187,7 @@ describe('worker', () => {
|
|
|
187
187
|
await createVmBundleForTest();
|
|
188
188
|
|
|
189
189
|
const app = worker({
|
|
190
|
-
|
|
190
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
const res = await app
|
|
@@ -211,7 +211,7 @@ describe('worker', () => {
|
|
|
211
211
|
await createAsset(testName, bundleHash);
|
|
212
212
|
|
|
213
213
|
const app = worker({
|
|
214
|
-
|
|
214
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
215
215
|
password: 'my_password',
|
|
216
216
|
});
|
|
217
217
|
|
|
@@ -237,7 +237,7 @@ describe('worker', () => {
|
|
|
237
237
|
await createAsset(testName, bundleHash);
|
|
238
238
|
|
|
239
239
|
const app = worker({
|
|
240
|
-
|
|
240
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
241
241
|
password: 'my_password',
|
|
242
242
|
});
|
|
243
243
|
|
|
@@ -261,7 +261,7 @@ describe('worker', () => {
|
|
|
261
261
|
test('post /asset-exists requires targetBundles (protocol version 2.0.0)', async () => {
|
|
262
262
|
await createAsset(testName, String(BUNDLE_TIMESTAMP));
|
|
263
263
|
const app = worker({
|
|
264
|
-
|
|
264
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
265
265
|
password: 'my_password',
|
|
266
266
|
});
|
|
267
267
|
|
|
@@ -283,7 +283,7 @@ describe('worker', () => {
|
|
|
283
283
|
const bundleHash = 'some-bundle-hash';
|
|
284
284
|
|
|
285
285
|
const app = worker({
|
|
286
|
-
|
|
286
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
287
287
|
password: 'my_password',
|
|
288
288
|
});
|
|
289
289
|
|
|
@@ -307,7 +307,7 @@ describe('worker', () => {
|
|
|
307
307
|
const bundleHashOther = 'some-other-bundle-hash';
|
|
308
308
|
|
|
309
309
|
const app = worker({
|
|
310
|
-
|
|
310
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
311
311
|
password: 'my_password',
|
|
312
312
|
});
|
|
313
313
|
|
|
@@ -334,7 +334,7 @@ describe('worker', () => {
|
|
|
334
334
|
await createVmBundleForTest();
|
|
335
335
|
|
|
336
336
|
const app = worker({
|
|
337
|
-
|
|
337
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
338
338
|
});
|
|
339
339
|
|
|
340
340
|
const res = await app
|
|
@@ -355,7 +355,7 @@ describe('worker', () => {
|
|
|
355
355
|
await createVmBundleForTest();
|
|
356
356
|
|
|
357
357
|
const app = worker({
|
|
358
|
-
|
|
358
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
359
359
|
});
|
|
360
360
|
|
|
361
361
|
const res = await app
|
|
@@ -379,7 +379,7 @@ describe('worker', () => {
|
|
|
379
379
|
await createVmBundleForTest();
|
|
380
380
|
|
|
381
381
|
const app = worker({
|
|
382
|
-
|
|
382
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
383
383
|
});
|
|
384
384
|
|
|
385
385
|
const res = await app
|
|
@@ -400,7 +400,7 @@ describe('worker', () => {
|
|
|
400
400
|
await createVmBundleForTest();
|
|
401
401
|
|
|
402
402
|
const app = worker({
|
|
403
|
-
|
|
403
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
404
404
|
});
|
|
405
405
|
|
|
406
406
|
// If package version is 4.0.0, this tests that 4.0.0.rc.1 gets normalized to 4.0.0-rc.1
|
|
@@ -426,7 +426,7 @@ describe('worker', () => {
|
|
|
426
426
|
await createVmBundleForTest();
|
|
427
427
|
|
|
428
428
|
const app = worker({
|
|
429
|
-
|
|
429
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
430
430
|
});
|
|
431
431
|
|
|
432
432
|
const gemVersionUpperCase = packageJson.version.toUpperCase();
|
|
@@ -449,7 +449,7 @@ describe('worker', () => {
|
|
|
449
449
|
await createVmBundleForTest();
|
|
450
450
|
|
|
451
451
|
const app = worker({
|
|
452
|
-
|
|
452
|
+
serverBundleCachePath: serverBundleCachePathForTest(),
|
|
453
453
|
});
|
|
454
454
|
|
|
455
455
|
const gemVersionWithWhitespace = ` ${packageJson.version} `;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "timeout"
|
|
5
|
+
|
|
6
|
+
# NOTE: Pro package does not include Steep tasks (:steep, :all) as it does not
|
|
7
|
+
# use Steep type checker. Only RBS validation is performed.
|
|
8
|
+
# rubocop:disable Metrics/BlockLength
|
|
9
|
+
namespace :rbs do
|
|
10
|
+
desc "Validate RBS type signatures"
|
|
11
|
+
task :validate do
|
|
12
|
+
require "rbs"
|
|
13
|
+
require "rbs/cli"
|
|
14
|
+
|
|
15
|
+
puts "Validating RBS type signatures..."
|
|
16
|
+
|
|
17
|
+
# Use Open3 for better error handling - captures stdout, stderr, and exit status separately
|
|
18
|
+
# This allows us to distinguish between actual validation errors and warnings
|
|
19
|
+
# Note: Must use bundle exec even though rake runs in bundle context because
|
|
20
|
+
# spawned shell commands via Open3.capture3() do NOT inherit bundle context
|
|
21
|
+
# Wrap in Timeout to prevent hung processes in CI environments (60 second timeout)
|
|
22
|
+
stdout, stderr, status = Timeout.timeout(60) do
|
|
23
|
+
Open3.capture3("bundle exec rbs -I sig validate")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if status.success?
|
|
27
|
+
puts "✓ RBS validation passed"
|
|
28
|
+
else
|
|
29
|
+
puts "✗ RBS validation failed"
|
|
30
|
+
puts stdout unless stdout.empty?
|
|
31
|
+
warn stderr unless stderr.empty?
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "Check RBS type signatures (alias for validate)"
|
|
37
|
+
task check: :validate
|
|
38
|
+
|
|
39
|
+
desc "List all RBS files"
|
|
40
|
+
task :list do
|
|
41
|
+
sig_files = Dir.glob("sig/**/*.rbs")
|
|
42
|
+
puts "RBS type signature files:"
|
|
43
|
+
sig_files.each { |f| puts " #{f}" }
|
|
44
|
+
puts "\nTotal: #{sig_files.count} files"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable Metrics/BlockLength
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ReactOnRailsPro
|
|
2
|
+
class Cache
|
|
3
|
+
def self.fetch_react_component: (String component_name, Hash[Symbol, untyped] options) { () -> untyped } -> untyped
|
|
4
|
+
|
|
5
|
+
def self.use_cache?: (Hash[Symbol, untyped] options) -> bool
|
|
6
|
+
|
|
7
|
+
def self.base_cache_key: (String type, ?prerender: bool?) -> Array[String]
|
|
8
|
+
|
|
9
|
+
def self.dependencies_cache_key: () -> String?
|
|
10
|
+
|
|
11
|
+
def self.react_component_cache_key: (String component_name, Hash[Symbol, untyped] options) -> Array[untyped]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module ReactOnRailsPro
|
|
2
|
+
class Configuration
|
|
3
|
+
DEFAULT_RENDERER_URL: String
|
|
4
|
+
DEFAULT_RENDERER_METHOD: String
|
|
5
|
+
DEFAULT_RENDERER_FALLBACK_EXEC_JS: bool
|
|
6
|
+
DEFAULT_RENDERER_HTTP_POOL_SIZE: Integer
|
|
7
|
+
DEFAULT_RENDERER_HTTP_POOL_TIMEOUT: Integer
|
|
8
|
+
DEFAULT_RENDERER_HTTP_POOL_WARN_TIMEOUT: Float
|
|
9
|
+
DEFAULT_SSR_TIMEOUT: Integer
|
|
10
|
+
DEFAULT_PRERENDER_CACHING: bool
|
|
11
|
+
DEFAULT_TRACING: bool
|
|
12
|
+
DEFAULT_DEPENDENCY_GLOBS: Array[String]
|
|
13
|
+
DEFAULT_EXCLUDED_DEPENDENCY_GLOBS: Array[String]
|
|
14
|
+
DEFAULT_REMOTE_BUNDLE_CACHE_ADAPTER: nil
|
|
15
|
+
DEFAULT_RENDERER_REQUEST_RETRY_LIMIT: Integer
|
|
16
|
+
DEFAULT_THROW_JS_ERRORS: bool
|
|
17
|
+
DEFAULT_RENDERING_RETURNS_PROMISES: bool
|
|
18
|
+
DEFAULT_PROFILE_SERVER_RENDERING_JS_CODE: bool
|
|
19
|
+
DEFAULT_RAISE_NON_SHELL_SERVER_RENDERING_ERRORS: bool
|
|
20
|
+
DEFAULT_ENABLE_RSC_SUPPORT: bool
|
|
21
|
+
DEFAULT_RSC_PAYLOAD_GENERATION_URL_PATH: String
|
|
22
|
+
DEFAULT_RSC_BUNDLE_JS_FILE: String
|
|
23
|
+
DEFAULT_REACT_CLIENT_MANIFEST_FILE: String
|
|
24
|
+
DEFAULT_REACT_SERVER_CLIENT_MANIFEST_FILE: String
|
|
25
|
+
|
|
26
|
+
attr_accessor renderer_url: String?
|
|
27
|
+
attr_accessor renderer_password: String?
|
|
28
|
+
attr_accessor tracing: bool?
|
|
29
|
+
attr_accessor server_renderer: String?
|
|
30
|
+
attr_accessor renderer_use_fallback_exec_js: bool?
|
|
31
|
+
attr_accessor prerender_caching: bool?
|
|
32
|
+
attr_accessor renderer_http_pool_size: Integer?
|
|
33
|
+
attr_accessor renderer_http_pool_timeout: Integer?
|
|
34
|
+
attr_accessor renderer_http_pool_warn_timeout: Float?
|
|
35
|
+
attr_accessor dependency_globs: Array[String]?
|
|
36
|
+
attr_accessor excluded_dependency_globs: Array[String]?
|
|
37
|
+
attr_accessor rendering_returns_promises: bool?
|
|
38
|
+
attr_accessor remote_bundle_cache_adapter: Module?
|
|
39
|
+
attr_accessor ssr_pre_hook_js: String?
|
|
40
|
+
attr_accessor assets_to_copy: Array[String]?
|
|
41
|
+
attr_accessor renderer_request_retry_limit: Integer?
|
|
42
|
+
attr_accessor throw_js_errors: bool?
|
|
43
|
+
attr_accessor ssr_timeout: Integer?
|
|
44
|
+
attr_accessor profile_server_rendering_js_code: bool?
|
|
45
|
+
attr_accessor raise_non_shell_server_rendering_errors: bool?
|
|
46
|
+
attr_accessor enable_rsc_support: bool?
|
|
47
|
+
attr_accessor rsc_payload_generation_url_path: String?
|
|
48
|
+
attr_accessor rsc_bundle_js_file: String?
|
|
49
|
+
attr_accessor react_client_manifest_file: String?
|
|
50
|
+
attr_accessor react_server_client_manifest_file: String?
|
|
51
|
+
|
|
52
|
+
def initialize: (
|
|
53
|
+
?renderer_url: String?,
|
|
54
|
+
?renderer_password: String?,
|
|
55
|
+
?server_renderer: String?,
|
|
56
|
+
?renderer_use_fallback_exec_js: bool?,
|
|
57
|
+
?prerender_caching: bool?,
|
|
58
|
+
?renderer_http_pool_size: Integer?,
|
|
59
|
+
?renderer_http_pool_timeout: Integer?,
|
|
60
|
+
?renderer_http_pool_warn_timeout: Float?,
|
|
61
|
+
?tracing: bool?,
|
|
62
|
+
?dependency_globs: Array[String]?,
|
|
63
|
+
?excluded_dependency_globs: Array[String]?,
|
|
64
|
+
?rendering_returns_promises: bool?,
|
|
65
|
+
?remote_bundle_cache_adapter: Module?,
|
|
66
|
+
?ssr_pre_hook_js: String?,
|
|
67
|
+
?assets_to_copy: Array[String]?,
|
|
68
|
+
?renderer_request_retry_limit: Integer?,
|
|
69
|
+
?throw_js_errors: bool?,
|
|
70
|
+
?ssr_timeout: Integer?,
|
|
71
|
+
?profile_server_rendering_js_code: bool?,
|
|
72
|
+
?raise_non_shell_server_rendering_errors: bool?,
|
|
73
|
+
?enable_rsc_support: bool?,
|
|
74
|
+
?rsc_payload_generation_url_path: String?,
|
|
75
|
+
?rsc_bundle_js_file: String?,
|
|
76
|
+
?react_client_manifest_file: String?,
|
|
77
|
+
?react_server_client_manifest_file: String?
|
|
78
|
+
) -> void
|
|
79
|
+
|
|
80
|
+
def setup_config_values: () -> void
|
|
81
|
+
|
|
82
|
+
def check_react_on_rails_support_for_rsc: () -> void
|
|
83
|
+
|
|
84
|
+
def setup_execjs_profiler_if_needed: () -> void
|
|
85
|
+
|
|
86
|
+
def node_renderer?: () -> bool
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def setup_assets_to_copy: () -> void
|
|
91
|
+
|
|
92
|
+
def configure_default_url_if_not_provided: () -> void
|
|
93
|
+
|
|
94
|
+
def validate_url: () -> void
|
|
95
|
+
|
|
96
|
+
def validate_remote_bundle_cache_adapter: () -> void
|
|
97
|
+
|
|
98
|
+
def setup_renderer_password: () -> void
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -9,7 +9,7 @@ GIT
|
|
|
9
9
|
PATH
|
|
10
10
|
remote: ../../..
|
|
11
11
|
specs:
|
|
12
|
-
react_on_rails (16.2.0.beta.
|
|
12
|
+
react_on_rails (16.2.0.beta.4)
|
|
13
13
|
addressable
|
|
14
14
|
connection_pool
|
|
15
15
|
execjs (~> 2.5)
|
|
@@ -20,14 +20,14 @@ PATH
|
|
|
20
20
|
PATH
|
|
21
21
|
remote: ../..
|
|
22
22
|
specs:
|
|
23
|
-
react_on_rails_pro (16.2.0.beta.
|
|
23
|
+
react_on_rails_pro (16.2.0.beta.4)
|
|
24
24
|
addressable
|
|
25
25
|
connection_pool
|
|
26
26
|
execjs (~> 2.9)
|
|
27
27
|
httpx (~> 1.5)
|
|
28
28
|
jwt (~> 2.7)
|
|
29
29
|
rainbow
|
|
30
|
-
react_on_rails (= 16.2.0.beta.
|
|
30
|
+
react_on_rails (= 16.2.0.beta.4)
|
|
31
31
|
|
|
32
32
|
GEM
|
|
33
33
|
remote: https://rubygems.org/
|
|
@@ -327,6 +327,8 @@ GEM
|
|
|
327
327
|
rb-fsevent (0.11.2)
|
|
328
328
|
rb-inotify (0.11.1)
|
|
329
329
|
ffi (~> 1.0)
|
|
330
|
+
rbs (3.9.5)
|
|
331
|
+
logger
|
|
330
332
|
rdoc (6.12.0)
|
|
331
333
|
psych (>= 4.0.0)
|
|
332
334
|
redis (5.4.0)
|
|
@@ -524,6 +526,7 @@ DEPENDENCIES
|
|
|
524
526
|
pry-theme
|
|
525
527
|
puma (~> 6)
|
|
526
528
|
rails (~> 7.1)
|
|
529
|
+
rbs
|
|
527
530
|
react_on_rails!
|
|
528
531
|
react_on_rails_pro!
|
|
529
532
|
redis
|
|
@@ -29,7 +29,7 @@ require('@shakacode-tools/react-on-rails-pro-node-renderer/integrations/sentry')
|
|
|
29
29
|
|
|
30
30
|
const config = {
|
|
31
31
|
// This is the default but avoids searching for the Rails root
|
|
32
|
-
|
|
32
|
+
serverBundleCachePath: path.resolve(__dirname, '../.node-renderer-bundles'),
|
|
33
33
|
port: env.RENDERER_PORT || 3800, // Listen at RENDERER_PORT env value or default port 3800
|
|
34
34
|
logLevel: env.RENDERER_LOG_LEVEL || 'info',
|
|
35
35
|
|