ruact 0.0.15 → 0.0.17
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 +42 -1
- data/lib/generators/ruact/install/install_generator.rb +2 -1
- data/lib/generators/ruact/install/templates/AGENTS.md.tt +2 -2
- data/lib/ruact/client_manifest.rb +11 -2
- data/lib/ruact/component_attributes.rb +91 -0
- data/lib/ruact/configuration.rb +28 -0
- data/lib/ruact/controller.rb +3 -1
- data/lib/ruact/doctor.rb +64 -4
- data/lib/ruact/erb_preprocessor.rb +1 -47
- data/lib/ruact/flight/renderer.rb +23 -9
- data/lib/ruact/flight/request.rb +31 -2
- data/lib/ruact/flight/row_emitter.rb +9 -0
- data/lib/ruact/flight/serializer.rb +22 -7
- data/lib/ruact/render_pipeline.rb +10 -3
- data/lib/ruact/server.rb +11 -7
- data/lib/ruact/server_functions/action_assigns.rb +52 -0
- data/lib/ruact/server_functions/bucket_two_payload.rb +1 -1
- data/lib/ruact/testing/component_query.rb +11 -4
- data/lib/ruact/testing/flight_wire_parser.rb +2 -2
- data/lib/ruact/version.rb +1 -1
- data/lib/ruact.rb +1 -0
- data/vendor/javascript/vite-plugin-ruact/bootstrap.test.mjs +44 -2
- data/vendor/javascript/vite-plugin-ruact/flight-conformance.test.mjs +286 -0
- data/vendor/javascript/vite-plugin-ruact/index.js +89 -14
- data/vendor/javascript/vite-plugin-ruact/package-lock.json +804 -3
- data/vendor/javascript/vite-plugin-ruact/package.json +5 -1
- data/vendor/javascript/vite-plugin-ruact/ruact-router.test.mjs +53 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/bootstrap.jsx +46 -17
- data/vendor/javascript/vite-plugin-ruact/runtime/flight-modules.js +40 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/flight-webpack-globals.js +15 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/root-boundary.js +41 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/ruact-router.js +75 -88
- data/vendor/javascript/vite-plugin-ruact/runtime/suspense-boundary.js +83 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/transport.js +41 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/vendor/react-server-dom-webpack/LICENSE +21 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/vendor/react-server-dom-webpack/VERSION.json +5 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/vendor/react-server-dom-webpack/client.browser.development.js +5473 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/vendor/react-server-dom-webpack/client.browser.production.js +2126 -0
- data/vendor/javascript/vite-plugin-ruact/scripts/vendor-flight-client.mjs +124 -0
- data/vendor/javascript/vite-plugin-ruact/vitest.config.mjs +14 -0
- metadata +15 -4
- data/vendor/javascript/vite-plugin-ruact/flight-client.test.mjs +0 -452
- data/vendor/javascript/vite-plugin-ruact/runtime/flight-client.js +0 -408
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import { installServerFunctionsHooks } from "./server-functions-codegen.mjs";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -56,6 +57,11 @@ const RESOLVED_REGISTRY_ID = "\0" + REGISTRY_VIRTUAL_ID;
|
|
|
56
57
|
// `/@id/__x00__virtual:ruact/bootstrap`; prod Vite-manifest key
|
|
57
58
|
// `virtual:ruact/bootstrap`). Mirrors REGISTRY_VIRTUAL_ID exactly.
|
|
58
59
|
export const BOOTSTRAP_VIRTUAL_ID = "virtual:ruact/bootstrap";
|
|
60
|
+
|
|
61
|
+
// Story 18-1 — React's Flight client, as the runtime imports it. The plugin
|
|
62
|
+
// decides which copy answers (see `flightClientSource`).
|
|
63
|
+
export const FLIGHT_CLIENT_VIRTUAL_ID = "virtual:ruact/flight-client";
|
|
64
|
+
const RESOLVED_FLIGHT_CLIENT_ID = "\0" + FLIGHT_CLIENT_VIRTUAL_ID;
|
|
59
65
|
const RESOLVED_BOOTSTRAP_ID = "\0" + BOOTSTRAP_VIRTUAL_ID;
|
|
60
66
|
|
|
61
67
|
// The gem-shipped runtime sources the virtual bootstrap pulls in. Resolved
|
|
@@ -71,18 +77,30 @@ export default function ruact(options = {}) {
|
|
|
71
77
|
|
|
72
78
|
let root;
|
|
73
79
|
let manifest = {};
|
|
80
|
+
let flightClient; // { source: "app" | "vendored", version, mode }
|
|
74
81
|
|
|
75
82
|
return installServerFunctionsHooks({
|
|
76
83
|
name: "vite-plugin-ruact",
|
|
77
84
|
|
|
85
|
+
// Story 18-1 — the runtime (and React's Flight client vendored with it) is
|
|
86
|
+
// served from the gem's directory, not the app's. Its `react` and
|
|
87
|
+
// `react-dom` imports must still be the app's: one React instance.
|
|
88
|
+
config() {
|
|
89
|
+
return { resolve: { dedupe: ["react", "react-dom"] } };
|
|
90
|
+
},
|
|
91
|
+
|
|
78
92
|
configResolved(config) {
|
|
79
93
|
root = config.root;
|
|
94
|
+
flightClient = resolveFlightClient(root, config.isProduction ? "production" : "development");
|
|
95
|
+
// Dev server and build alike, so a build log shows it too.
|
|
96
|
+
config.logger?.info?.(`[ruact] Flight client: ${describeFlightClient(flightClient)}`);
|
|
80
97
|
},
|
|
81
98
|
|
|
82
99
|
// Story 10.1b / 14.2 — resolve ruact's virtual module ids.
|
|
83
100
|
resolveId(id) {
|
|
84
101
|
if (id === REGISTRY_VIRTUAL_ID) return RESOLVED_REGISTRY_ID;
|
|
85
102
|
if (id === BOOTSTRAP_VIRTUAL_ID) return RESOLVED_BOOTSTRAP_ID;
|
|
103
|
+
if (id === FLIGHT_CLIENT_VIRTUAL_ID) return RESOLVED_FLIGHT_CLIENT_ID;
|
|
86
104
|
return null;
|
|
87
105
|
},
|
|
88
106
|
|
|
@@ -96,6 +114,7 @@ export default function ruact(options = {}) {
|
|
|
96
114
|
// Story 14.2 — serve the gem-shipped bootstrap source, with its relative
|
|
97
115
|
// runtime imports rewritten to absolute fs specifiers (see below).
|
|
98
116
|
if (id === RESOLVED_BOOTSTRAP_ID) return generateBootstrapSource();
|
|
117
|
+
if (id === RESOLVED_FLIGHT_CLIENT_ID) return flightClientSource(flightClient);
|
|
99
118
|
return null;
|
|
100
119
|
},
|
|
101
120
|
|
|
@@ -303,28 +322,84 @@ export function generateRegistrySource(manifest) {
|
|
|
303
322
|
// Story 14.2 (FR104) — render the virtual bootstrap source from the gem-shipped
|
|
304
323
|
// `runtime/bootstrap.jsx`. A `load` hook returns module TEXT whose relative
|
|
305
324
|
// imports resolve against the resolved id (`\0virtual:ruact/bootstrap`), which
|
|
306
|
-
// is NOT a filesystem path — so `./flight-
|
|
307
|
-
// would fail. We rewrite those
|
|
325
|
+
// is NOT a filesystem path — so `./flight-modules.js` / `./ruact-router.js`
|
|
326
|
+
// would fail. We rewrite those specifiers to ABSOLUTE fs specifiers into the
|
|
308
327
|
// gem `runtime/` dir (the same technique `generateRegistrySource` uses via
|
|
309
328
|
// `toImportSpecifier`). The bare `react` / `react-dom/client` specifiers and the
|
|
310
329
|
// `virtual:ruact/registry` id are left untouched — Vite resolves them from the
|
|
311
330
|
// app root (so React comes from the USER's node_modules: one React instance).
|
|
312
331
|
export function generateBootstrapSource(runtimeDir = RUNTIME_DIR) {
|
|
313
332
|
const src = fs.readFileSync(path.join(runtimeDir, "bootstrap.jsx"), "utf8");
|
|
314
|
-
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
333
|
+
// Rewrite EVERY relative `from './<runtime>.js'` and side-effect
|
|
334
|
+
// `import './<runtime>.js'` specifier to its absolute fs path. Matching the
|
|
335
|
+
// quoted specifier after `from`/`import` (not the bare filename) leaves prose
|
|
336
|
+
// mentions of a file in comments alone, and either quote style is accepted.
|
|
337
|
+
// The replacement is a function so a `$` in the absolute path is never read
|
|
338
|
+
// as a replacement pattern.
|
|
339
|
+
return src.replace(
|
|
340
|
+
/\b(from|import)(\s+)(['"])\.\/([\w.-]+\.js)\3/g,
|
|
341
|
+
(_match, keyword, space, _quote, name) =>
|
|
342
|
+
`${keyword}${space}'${toImportSpecifier(path.join(runtimeDir, name))}'`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Story 18-1 (packaging option C, docs/internal/decisions/flight-client-compatibility.md)
|
|
347
|
+
// — which copy of React's Flight client the runtime uses. The app's own
|
|
348
|
+
// `react-server-dom-webpack` when the app declares it in its package.json (an
|
|
349
|
+
// app that needs a different React version installs it); otherwise the copy
|
|
350
|
+
// vendored in the gem, which brings no `webpack` into the app's install. A copy
|
|
351
|
+
// that only resolves — another package's dependency, hoisted — is not chosen:
|
|
352
|
+
// it was not picked for this app's React.
|
|
353
|
+
export function resolveFlightClient(appRoot, mode, runtimeDir = RUNTIME_DIR) {
|
|
354
|
+
const vendored = () => {
|
|
355
|
+
const { version } = JSON.parse(
|
|
356
|
+
fs.readFileSync(path.join(runtimeDir, "vendor", "react-server-dom-webpack", "VERSION.json"), "utf8"),
|
|
325
357
|
);
|
|
358
|
+
return { source: "vendored", version, mode };
|
|
326
359
|
};
|
|
327
|
-
|
|
360
|
+
let declared = false;
|
|
361
|
+
try {
|
|
362
|
+
const app = JSON.parse(fs.readFileSync(path.join(appRoot, "package.json"), "utf8"));
|
|
363
|
+
declared = Boolean(app.dependencies?.["react-server-dom-webpack"] ?? app.devDependencies?.["react-server-dom-webpack"]);
|
|
364
|
+
} catch {
|
|
365
|
+
return vendored();
|
|
366
|
+
}
|
|
367
|
+
if (!declared) return vendored();
|
|
368
|
+
try {
|
|
369
|
+
const require = createRequire(path.join(appRoot, "package.json"));
|
|
370
|
+
const pkg = require("react-server-dom-webpack/package.json");
|
|
371
|
+
return { source: "app", version: pkg.version, mode };
|
|
372
|
+
} catch {
|
|
373
|
+
// Declared but not installed: npm install has not run. The vendored copy
|
|
374
|
+
// keeps the page working meanwhile.
|
|
375
|
+
return vendored();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function describeFlightClient({ source, version, mode }) {
|
|
380
|
+
return source === "app"
|
|
381
|
+
? `react-server-dom-webpack ${version} from the app (${mode})`
|
|
382
|
+
: `react-server-dom-webpack ${version} vendored in ruact (${mode})`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// The `virtual:ruact/flight-client` module. The vendored copy is an ES module
|
|
386
|
+
// whose webpack hooks are module-local. The app's copy is CommonJS reading
|
|
387
|
+
// webpack's globals, which must exist before it evaluates — hence the
|
|
388
|
+
// side-effect import first.
|
|
389
|
+
export function flightClientSource({ source, mode }, runtimeDir = RUNTIME_DIR) {
|
|
390
|
+
const names = "createFromReadableStream, createFromFetch";
|
|
391
|
+
if (source === "app") {
|
|
392
|
+
const globals = toImportSpecifier(path.join(runtimeDir, "flight-webpack-globals.js"));
|
|
393
|
+
return [
|
|
394
|
+
`import '${globals}';`,
|
|
395
|
+
`export { ${names} } from 'react-server-dom-webpack/client.browser';`,
|
|
396
|
+
"",
|
|
397
|
+
].join("\n");
|
|
398
|
+
}
|
|
399
|
+
const vendored = toImportSpecifier(
|
|
400
|
+
path.join(runtimeDir, "vendor", "react-server-dom-webpack", `client.browser.${mode}.js`),
|
|
401
|
+
);
|
|
402
|
+
return `export { ${names} } from '${vendored}';\n`;
|
|
328
403
|
}
|
|
329
404
|
|
|
330
405
|
// A bundler import specifier for an absolute fs path. Vite/Rollup resolve
|