@uniflowed/vite 0.0.0-alpha.39 → 0.0.0-alpha.40

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.
package/driver.js CHANGED
@@ -1862,6 +1862,25 @@ serve({ handle: fetch, staticDir, beginRequest, routing${cron.option} }).catch((
1862
1862
  `;
1863
1863
  }
1864
1864
 
1865
+ /**
1866
+ * The oldest Bun that can run what this adapter writes.
1867
+ *
1868
+ * Kept equal to `uf_runtime`'s `BUN_MINIMUM` by
1869
+ * `the_bun_entry_refuses_a_bun_older_than_the_declared_minimum`, which reads
1870
+ * the number out of the artefact rather than out of this file — two places
1871
+ * holding one version is exactly the shape that lets them disagree.
1872
+ *
1873
+ * Why there is a floor at all: React's published server build contains a
1874
+ * labelled statement in `else` position — `else a: if (…)`, in every
1875
+ * `react-dom-server*.production.js` including the `bun` one — and Bun's engine
1876
+ * rejects it with `Cannot find scope for the label 'a'` before this release.
1877
+ * Bun 1.3.13 rejects the built `handler.js`; 1.3.14 imports it and serves it.
1878
+ * It is ordinary ES that Node runs, uf does not emit it, and the `bun` export
1879
+ * condition does not avoid it, so there is nothing for uf to lower. See
1880
+ * ubugeeei-prod/uf#1048.
1881
+ */
1882
+ const MINIMUM_BUN = "1.3.14";
1883
+
1865
1884
  /**
1866
1885
  * The source of `server.js`: the Bun socket around that handler.
1867
1886
  *
@@ -1869,6 +1888,16 @@ serve({ handle: fetch, staticDir, beginRequest, routing${cron.option} }).catch((
1869
1888
  * `serve` from — `@uniflowed/server/bun` exports the same signature on
1870
1889
  * purpose, so that the two adapters are one contract and a project moving
1871
1890
  * between them changes a flag and nothing else.
1891
+ *
1892
+ * # Why the handler is imported dynamically
1893
+ *
1894
+ * The one place this is *not* `nodeEntrySource`'s twin. A static
1895
+ * `import … from "./handler.js"` is linked before a single statement of this
1896
+ * file runs, so a version check written above it would never execute: Bun
1897
+ * would fail parsing `handler.js` and the sentence explaining why would never
1898
+ * be reached. `await import()` puts the check first, which is the same move
1899
+ * `denoEntrySource` makes to get its globals in before the handler is
1900
+ * evaluated.
1872
1901
  */
1873
1902
  function bunEntrySource(handlerSpecifier, schedules) {
1874
1903
  const cron = scheduleLines("@uniflowed/server/schedule", schedules);
@@ -1878,11 +1907,42 @@ import { fileURLToPath } from "node:url";
1878
1907
 
1879
1908
  import { serve } from "@uniflowed/server/bun";
1880
1909
  ${cron.imports}
1910
+ // The Bun this build needs. React's server build carries a labelled statement
1911
+ // in \`else\` position that Bun's engine rejects before this release, so an
1912
+ // older Bun cannot parse \`handler.js\` at all — see ubugeeei-prod/uf#1048.
1913
+ //
1914
+ // This runs *before* the handler is imported, and that ordering is the whole
1915
+ // point: a static import would be linked first and the parse would fail with
1916
+ // Bun's message rather than this one.
1917
+ const MINIMUM_BUN = ${JSON.stringify(MINIMUM_BUN)};
1918
+
1919
+ function ufBunIsOlderThanMinimum(version) {
1920
+ const actual = String(version).split(".");
1921
+ const floor = MINIMUM_BUN.split(".");
1922
+ for (let index = 0; index < floor.length; index += 1) {
1923
+ const mine = Number.parseInt(actual[index], 10);
1924
+ const want = Number.parseInt(floor[index], 10);
1925
+ if (!Number.isFinite(mine)) return true;
1926
+ if (mine !== want) return mine < want;
1927
+ }
1928
+ return false;
1929
+ }
1930
+
1931
+ if (typeof Bun !== "undefined" && ufBunIsOlderThanMinimum(Bun.version)) {
1932
+ process.stderr.write(
1933
+ \`uf: this build needs Bun \${MINIMUM_BUN} or newer, and this is Bun \${Bun.version}. \` +
1934
+ "React's server build uses a labelled statement Bun rejects before " +
1935
+ \`\${MINIMUM_BUN}, so \\\`handler.js\\\` beside this file cannot be parsed here. \` +
1936
+ "Upgrade with \`bun upgrade\`, or build with \`--adapter node\` and run it on Node.\\n",
1937
+ );
1938
+ process.exit(1);
1939
+ }
1940
+
1881
1941
  // \`beginRequest\` comes from the handler beside this file rather than from
1882
1942
  // \`@uniflowed/server/bun\` above, because the request has to be established in
1883
1943
  // the storage the *application* reads, which is the copy bundled into
1884
1944
  // \`handler.js\`. See ubugeeei-prod/uf#389.
1885
- import { beginRequest, fetch, routing } from ${JSON.stringify(handlerSpecifier)};
1945
+ const { beginRequest, fetch, routing } = await import(${JSON.stringify(handlerSpecifier)});
1886
1946
 
1887
1947
  // Resolved from this file and not from the working directory: a process
1888
1948
  // manager, a container entrypoint and a person in a shell each start a server
@@ -581,10 +581,21 @@ export function loadClientModule(url) {
581
581
  * @param {Map<string, string>} chunkUrls
582
582
  */
583
583
  export function builtReferencesSource(chunkUrls) {
584
- const entries = [...chunkUrls].map(
585
- ([file, url]) => ` [${JSON.stringify(url)}, () => import(${JSON.stringify(file)})],`,
586
- );
587
- return `const table = new Map([
584
+ const imports = [];
585
+ const entries = [];
586
+ let staticIndex = 0;
587
+ for (const [file, url] of chunkUrls) {
588
+ if (serverBundleAlreadyImportsClientReference(file)) {
589
+ const name = `staticReference${staticIndex}`;
590
+ staticIndex += 1;
591
+ imports.push(`import * as ${name} from ${JSON.stringify(file)};`);
592
+ entries.push(` [${JSON.stringify(url)}, () => Promise.resolve(${name})],`);
593
+ } else {
594
+ entries.push(` [${JSON.stringify(url)}, () => import(${JSON.stringify(file)})],`);
595
+ }
596
+ }
597
+ const prelude = imports.length === 0 ? "" : `${imports.join("\n")}\n`;
598
+ return `${prelude}const table = new Map([
588
599
  ${entries.join("\n")}
589
600
  ]);
590
601
  export function loadClientModule(url) {
@@ -599,6 +610,28 @@ export function loadClientModule(url) {
599
610
  `;
600
611
  }
601
612
 
613
+ /**
614
+ * Router internals that are client modules and also already in the server
615
+ * bundle through the router's own synchronous imports.
616
+ *
617
+ * They still need entries in the client-reference table, because a Flight
618
+ * payload can name their chunks. Loading them with `import()`, however, makes
619
+ * Rollup print INEFFECTIVE_DYNAMIC_IMPORT: the dynamic import cannot split a
620
+ * module the server chunk has already imported. Return the namespace the
621
+ * server loaded anyway.
622
+ */
623
+ function serverBundleAlreadyImportsClientReference(file) {
624
+ const normalized = file.split(path.sep).join("/");
625
+ return ROUTER_CLIENT_REFERENCES_IN_SERVER.some((suffix) => normalized.endsWith(suffix));
626
+ }
627
+
628
+ const ROUTER_CLIENT_REFERENCES_IN_SERVER = Object.freeze([
629
+ "/@uniflowed/router/internal/boundaries.js",
630
+ "/@uniflowed/router/internal/error-view.js",
631
+ "/packages/router/internal/boundaries.js",
632
+ "/packages/router/internal/error-view.js",
633
+ ]);
634
+
602
635
  /**
603
636
  * `virtual:uf/client` for an application React Server Components render.
604
637
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniflowed/vite",
3
- "version": "0.0.0-alpha.39",
3
+ "version": "0.0.0-alpha.40",
4
4
  "description": "Vite, driven by uf.config.js: every Flow module through `uf transform`, MDX, the file-system router and static rendering as Vite plugins.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,10 +34,10 @@
34
34
  "dependencies": {
35
35
  "@mdx-js/rollup": "^3.1.1",
36
36
  "@shikijs/rehype": "^3.23.0",
37
- "@uniflowed/host": "0.0.0-alpha.39",
38
- "@uniflowed/router": "0.0.0-alpha.39",
39
- "@uniflowed/server": "0.0.0-alpha.39",
40
- "@uniflowed/validator": "0.0.0-alpha.39",
37
+ "@uniflowed/host": "0.0.0-alpha.40",
38
+ "@uniflowed/router": "0.0.0-alpha.40",
39
+ "@uniflowed/server": "0.0.0-alpha.40",
40
+ "@uniflowed/validator": "0.0.0-alpha.40",
41
41
  "estree-util-value-to-estree": "^3.5.0",
42
42
  "rehype-slug": "^6.0.0",
43
43
  "remark-frontmatter": "^5.0.0",