fuzor 0.5.1 → 0.5.2
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/dist/browser.d.ts +2 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/compiler/data-document.d.ts +5 -1
- package/dist/compiler/data-document.d.ts.map +1 -1
- package/dist/compiler/data-document.js +36 -7
- package/dist/compiler/data-document.js.map +1 -1
- package/dist/compiler/docs.d.ts.map +1 -1
- package/dist/compiler/docs.js +49 -11
- package/dist/compiler/docs.js.map +1 -1
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +23 -6
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/islands.d.ts +1 -0
- package/dist/compiler/islands.d.ts.map +1 -1
- package/dist/compiler/native-authoring.d.ts +45 -0
- package/dist/compiler/native-authoring.d.ts.map +1 -0
- package/dist/compiler/native-authoring.js +576 -0
- package/dist/compiler/native-authoring.js.map +1 -0
- package/dist/compiler/native-manifest.d.ts +33 -0
- package/dist/compiler/native-manifest.d.ts.map +1 -0
- package/dist/compiler/native-manifest.js +53 -0
- package/dist/compiler/native-manifest.js.map +1 -0
- package/dist/compiler/native-ranges.d.ts +8 -0
- package/dist/compiler/native-ranges.d.ts.map +1 -0
- package/dist/compiler/native-ranges.js +92 -0
- package/dist/compiler/native-ranges.js.map +1 -0
- package/dist/compiler/native-scripts.js +2 -2
- package/dist/compiler/native-scripts.js.map +1 -1
- package/dist/compiler/page-scripts.d.ts +6 -0
- package/dist/compiler/page-scripts.d.ts.map +1 -1
- package/dist/compiler/page-scripts.js +25 -6
- package/dist/compiler/page-scripts.js.map +1 -1
- package/dist/compiler/route-policy.d.ts +1 -0
- package/dist/compiler/route-policy.d.ts.map +1 -1
- package/dist/compiler/route-policy.js +4 -1
- package/dist/compiler/route-policy.js.map +1 -1
- package/dist/compiler/server-actions.d.ts +9 -0
- package/dist/compiler/server-actions.d.ts.map +1 -0
- package/dist/compiler/server-actions.js +65 -0
- package/dist/compiler/server-actions.js.map +1 -0
- package/dist/data-bindings.d.ts +4 -0
- package/dist/data-bindings.d.ts.map +1 -0
- package/dist/data-bindings.js +16 -0
- package/dist/data-bindings.js.map +1 -0
- package/dist/dom-ownership.d.ts +5 -0
- package/dist/dom-ownership.d.ts.map +1 -0
- package/dist/dom-ownership.js +49 -0
- package/dist/dom-ownership.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal-specifiers.d.ts +3 -0
- package/dist/internal-specifiers.d.ts.map +1 -0
- package/dist/internal-specifiers.js +22 -0
- package/dist/internal-specifiers.js.map +1 -0
- package/dist/islands/react.d.ts +0 -6
- package/dist/islands/react.d.ts.map +1 -1
- package/dist/islands/react.js +9 -26
- package/dist/islands/react.js.map +1 -1
- package/dist/json-value.d.ts +4 -0
- package/dist/json-value.d.ts.map +1 -0
- package/dist/json-value.js +10 -0
- package/dist/json-value.js.map +1 -0
- package/dist/native-expressions.d.ts +34 -0
- package/dist/native-expressions.d.ts.map +1 -0
- package/dist/native-expressions.js +118 -0
- package/dist/native-expressions.js.map +1 -0
- package/dist/native-manifest.d.ts +7 -0
- package/dist/native-manifest.d.ts.map +1 -0
- package/dist/native-manifest.js +4 -0
- package/dist/native-manifest.js.map +1 -0
- package/dist/native-ranges.d.ts +25 -0
- package/dist/native-ranges.d.ts.map +1 -0
- package/dist/native-ranges.js +51 -0
- package/dist/native-ranges.js.map +1 -0
- package/dist/native-state.d.ts +14 -0
- package/dist/native-state.d.ts.map +1 -0
- package/dist/native-state.js +27 -0
- package/dist/native-state.js.map +1 -0
- package/dist/route-path.d.ts +2 -0
- package/dist/route-path.d.ts.map +1 -1
- package/dist/route-path.js +2 -0
- package/dist/route-path.js.map +1 -1
- package/dist/runtime/binding-updates.d.ts +11 -0
- package/dist/runtime/binding-updates.d.ts.map +1 -0
- package/dist/runtime/binding-updates.js +45 -0
- package/dist/runtime/binding-updates.js.map +1 -0
- package/dist/runtime/data.d.ts +28 -1
- package/dist/runtime/data.d.ts.map +1 -1
- package/dist/runtime/data.js +101 -25
- package/dist/runtime/data.js.map +1 -1
- package/dist/runtime/docs-search.d.ts +20 -4
- package/dist/runtime/docs-search.d.ts.map +1 -1
- package/dist/runtime/docs-search.js +135 -159
- package/dist/runtime/docs-search.js.map +1 -1
- package/dist/runtime/docs-ui.d.ts +3 -1
- package/dist/runtime/docs-ui.d.ts.map +1 -1
- package/dist/runtime/docs-ui.js +19 -29
- package/dist/runtime/docs-ui.js.map +1 -1
- package/dist/runtime/islands.d.ts.map +1 -1
- package/dist/runtime/islands.js +2 -1
- package/dist/runtime/islands.js.map +1 -1
- package/dist/runtime/native-graph.d.ts +15 -0
- package/dist/runtime/native-graph.d.ts.map +1 -0
- package/dist/runtime/native-graph.js +103 -0
- package/dist/runtime/native-graph.js.map +1 -0
- package/dist/runtime/native-manifest.d.ts +17 -0
- package/dist/runtime/native-manifest.d.ts.map +1 -0
- package/dist/runtime/native-manifest.js +111 -0
- package/dist/runtime/native-manifest.js.map +1 -0
- package/dist/runtime/native-ranges.d.ts +8 -0
- package/dist/runtime/native-ranges.d.ts.map +1 -0
- package/dist/runtime/native-ranges.js +100 -0
- package/dist/runtime/native-ranges.js.map +1 -0
- package/dist/runtime/native-stream-reader.d.ts +7 -0
- package/dist/runtime/native-stream-reader.d.ts.map +1 -0
- package/dist/runtime/native-stream-reader.js +79 -0
- package/dist/runtime/native-stream-reader.js.map +1 -0
- package/dist/runtime/native-stream.d.ts +5 -0
- package/dist/runtime/native-stream.d.ts.map +1 -0
- package/dist/runtime/native-stream.js +14 -0
- package/dist/runtime/native-stream.js.map +1 -0
- package/dist/runtime/native.d.ts +41 -0
- package/dist/runtime/native.d.ts.map +1 -1
- package/dist/runtime/native.js +176 -55
- package/dist/runtime/native.js.map +1 -1
- package/dist/runtime/page-scripts.d.ts.map +1 -1
- package/dist/runtime/page-scripts.js +8 -3
- package/dist/runtime/page-scripts.js.map +1 -1
- package/dist/runtime/router.d.ts.map +1 -1
- package/dist/runtime/router.js +6 -1
- package/dist/runtime/router.js.map +1 -1
- package/dist/runtime/server-actions.d.ts +5 -0
- package/dist/runtime/server-actions.d.ts.map +1 -0
- package/dist/runtime/server-actions.js +79 -0
- package/dist/runtime/server-actions.js.map +1 -0
- package/dist/server-actions.d.ts +18 -0
- package/dist/server-actions.d.ts.map +1 -0
- package/dist/server-actions.js +138 -0
- package/dist/server-actions.js.map +1 -0
- package/dist/server-components/closures.d.ts +1 -12
- package/dist/server-components/closures.d.ts.map +1 -1
- package/dist/server-components/closures.js +0 -75
- package/dist/server-components/closures.js.map +1 -1
- package/dist/server-components/continuation.d.ts +7 -0
- package/dist/server-components/continuation.d.ts.map +1 -0
- package/dist/server-components/continuation.js +17 -0
- package/dist/server-components/continuation.js.map +1 -0
- package/dist/server-components/environment.js +1 -1
- package/dist/server-components/environment.js.map +1 -1
- package/dist/server-components/protocol.d.ts +5 -1
- package/dist/server-components/protocol.d.ts.map +1 -1
- package/dist/server-components/protocol.js +9 -1
- package/dist/server-components/protocol.js.map +1 -1
- package/dist/server-components/server.d.ts +4 -7
- package/dist/server-components/server.d.ts.map +1 -1
- package/dist/server-components/server.js +2 -185
- package/dist/server-components/server.js.map +1 -1
- package/dist/server-components/stream-server.d.ts +6 -0
- package/dist/server-components/stream-server.d.ts.map +1 -0
- package/dist/server-components/stream-server.js +197 -0
- package/dist/server-components/stream-server.js.map +1 -0
- package/dist/server-components/stream.d.ts +16 -3
- package/dist/server-components/stream.d.ts.map +1 -1
- package/dist/server-components/stream.js +31 -4
- package/dist/server-components/stream.js.map +1 -1
- package/dist/server-components/transport.d.ts +20 -0
- package/dist/server-components/transport.d.ts.map +1 -0
- package/dist/server-components/transport.js +2 -0
- package/dist/server-components/transport.js.map +1 -0
- package/dist/server-components/updates.d.ts +56 -0
- package/dist/server-components/updates.d.ts.map +1 -0
- package/dist/server-components/updates.js +319 -0
- package/dist/server-components/updates.js.map +1 -0
- package/dist/server-data-protocol.d.ts +14 -0
- package/dist/server-data-protocol.d.ts.map +1 -0
- package/dist/server-data-protocol.js +15 -0
- package/dist/server-data-protocol.js.map +1 -0
- package/dist/server-data.d.ts +18 -0
- package/dist/server-data.d.ts.map +1 -1
- package/dist/server-data.js.map +1 -1
- package/dist/server-native-stream.d.ts +5 -0
- package/dist/server-native-stream.d.ts.map +1 -0
- package/dist/server-native-stream.js +39 -0
- package/dist/server-native-stream.js.map +1 -0
- package/dist/server-native.d.ts +24 -0
- package/dist/server-native.d.ts.map +1 -0
- package/dist/server-native.js +67 -0
- package/dist/server-native.js.map +1 -0
- package/dist/server-reactivity.d.ts +13 -0
- package/dist/server-reactivity.d.ts.map +1 -0
- package/dist/server-reactivity.js +49 -0
- package/dist/server-reactivity.js.map +1 -0
- package/dist/server.d.ts +8 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +55 -16
- package/dist/server.js.map +1 -1
- package/dist/ui/behaviors/accordion.d.ts.map +1 -1
- package/dist/ui/behaviors/accordion.js +4 -3
- package/dist/ui/behaviors/accordion.js.map +1 -1
- package/dist/ui/behaviors/collapsible.d.ts.map +1 -1
- package/dist/ui/behaviors/collapsible.js +2 -1
- package/dist/ui/behaviors/collapsible.js.map +1 -1
- package/dist/ui/behaviors/dialog.d.ts.map +1 -1
- package/dist/ui/behaviors/dialog.js +5 -4
- package/dist/ui/behaviors/dialog.js.map +1 -1
- package/dist/ui/behaviors/menu.d.ts.map +1 -1
- package/dist/ui/behaviors/menu.js +4 -3
- package/dist/ui/behaviors/menu.js.map +1 -1
- package/dist/ui/behaviors/select.d.ts +23 -1
- package/dist/ui/behaviors/select.d.ts.map +1 -1
- package/dist/ui/behaviors/select.js +81 -11
- package/dist/ui/behaviors/select.js.map +1 -1
- package/dist/ui/behaviors/switch.d.ts.map +1 -1
- package/dist/ui/behaviors/switch.js +2 -1
- package/dist/ui/behaviors/switch.js.map +1 -1
- package/dist/ui/behaviors/toolbar.d.ts.map +1 -1
- package/dist/ui/behaviors/toolbar.js +2 -1
- package/dist/ui/behaviors/toolbar.js.map +1 -1
- package/dist/ui/catalogue.d.ts +1 -1
- package/dist/ui/components/accordion/accordion.d.ts.map +1 -1
- package/dist/ui/components/accordion/accordion.js +3 -2
- package/dist/ui/components/accordion/accordion.js.map +1 -1
- package/dist/ui/components/collapsible/collapsible.d.ts.map +1 -1
- package/dist/ui/components/collapsible/collapsible.js +2 -1
- package/dist/ui/components/collapsible/collapsible.js.map +1 -1
- package/dist/ui/components/dialog/dialog.d.ts.map +1 -1
- package/dist/ui/components/dialog/dialog.js +4 -3
- package/dist/ui/components/dialog/dialog.js.map +1 -1
- package/dist/ui/components/navigation/breadcrumbs.d.ts.map +1 -1
- package/dist/ui/components/navigation/breadcrumbs.js +2 -1
- package/dist/ui/components/navigation/breadcrumbs.js.map +1 -1
- package/dist/ui/components/select/select.d.ts +20 -0
- package/dist/ui/components/select/select.d.ts.map +1 -1
- package/dist/ui/components/select/select.js +20 -0
- package/dist/ui/components/select/select.js.map +1 -1
- package/dist/ui/components/tabs/tabs.d.ts.map +1 -1
- package/dist/ui/components/tabs/tabs.js +6 -5
- package/dist/ui/components/tabs/tabs.js.map +1 -1
- package/dist/ui/core/brand.d.ts +33 -0
- package/dist/ui/core/brand.d.ts.map +1 -0
- package/dist/ui/core/brand.js +96 -0
- package/dist/ui/core/brand.js.map +1 -0
- package/dist/ui/core/component-styles.d.ts.map +1 -1
- package/dist/ui/core/component-styles.js +14 -8
- package/dist/ui/core/component-styles.js.map +1 -1
- package/dist/ui/core/element.d.ts.map +1 -1
- package/dist/ui/core/element.js +2 -3
- package/dist/ui/core/element.js.map +1 -1
- package/dist/ui/core/index.d.ts +1 -0
- package/dist/ui/core/index.d.ts.map +1 -1
- package/dist/ui/core/index.js +1 -0
- package/dist/ui/core/index.js.map +1 -1
- package/dist/ui/core/layout-styles.d.ts.map +1 -1
- package/dist/ui/core/layout-styles.js +12 -6
- package/dist/ui/core/layout-styles.js.map +1 -1
- package/dist/ui/core/parts.d.ts.map +1 -1
- package/dist/ui/core/parts.js +5 -4
- package/dist/ui/core/parts.js.map +1 -1
- package/dist/ui/core/portal.d.ts.map +1 -1
- package/dist/ui/core/portal.js +17 -1
- package/dist/ui/core/portal.js.map +1 -1
- package/dist/ui/machine-state.d.ts +11 -11
- package/dist/ui/state.d.ts +1 -1
- package/dist/vite.d.ts +5 -10
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +112 -114
- package/dist/vite.js.map +1 -1
- package/package.json +2 -12
- package/dist/server-components/browser.d.ts +0 -75
- package/dist/server-components/browser.d.ts.map +0 -1
- package/dist/server-components/browser.js +0 -558
- package/dist/server-components/browser.js.map +0 -1
- package/dist/server-components/compiler.d.ts +0 -6
- package/dist/server-components/compiler.d.ts.map +0 -1
- package/dist/server-components/compiler.js +0 -118
- package/dist/server-components/compiler.js.map +0 -1
- package/dist/server-components/navigation.d.ts +0 -10
- package/dist/server-components/navigation.d.ts.map +0 -1
- package/dist/server-components/navigation.js +0 -105
- package/dist/server-components/navigation.js.map +0 -1
- package/dist/server-components/references.d.ts +0 -22
- package/dist/server-components/references.d.ts.map +0 -1
- package/dist/server-components/references.js +0 -94
- package/dist/server-components/references.js.map +0 -1
package/dist/vite.js
CHANGED
|
@@ -3,12 +3,13 @@ import { compileDataDocument } from './compiler/data-document.js';
|
|
|
3
3
|
import { parseRoutePolicy } from './compiler/route-policy.js';
|
|
4
4
|
import { extractPageScripts, compilePageScript } from "./compiler/page-scripts.js";
|
|
5
5
|
import { compileNativeScript } from "./compiler/native-scripts.js";
|
|
6
|
+
import { renderNativeBinding } from "./native-ranges.js";
|
|
7
|
+
import { compileAuthoredNativeScript, prepareNativeScripts, htmlServerPlugin, assertNoServerScripts, resolveHtmlServer, loadHtmlServer } from "./compiler/native-authoring.js";
|
|
8
|
+
import { emitNativeManifests } from "./compiler/native-manifest.js";
|
|
6
9
|
import { compileWorkflows, workflowPlugin } from "./workflow/compiler.js";
|
|
10
|
+
import { resolveInternalSpecifier } from "./internal-specifiers.js";
|
|
7
11
|
import { assertBrowserViewImport } from "./server-components/environment.js";
|
|
8
|
-
import { replaceClosures } from "./server-components/closures.js";
|
|
9
|
-
import { discoverViewReferences, browserReferencesCode } from "./server-components/references.js";
|
|
10
12
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import { compileFragment, compiledFragmentsPlugin } from "./server-components/compiler.js";
|
|
12
13
|
import { makeRouteMatches } from "./runtime/route-matches.js";
|
|
13
14
|
import { makeClientRouteMatcher } from "./runtime/client-routes.js";
|
|
14
15
|
import { errorHtml, notFoundHtml } from "./boundaries.js";
|
|
@@ -35,6 +36,7 @@ import { toWebRequest, writeWebResponse } from "./server/node.js";
|
|
|
35
36
|
import { routeTypes } from "./runtime/href.js";
|
|
36
37
|
import { writeElectronScaffold } from "./electron.js";
|
|
37
38
|
import { createEnvironmentModuleRuntime, detectViteEnvironmentApi, invalidateEnvironmentModule, normalizeEnvironmentConfiguration } from "./vite-environments.js";
|
|
39
|
+
import { validateBrandPrefix } from "./ui/core/brand.js";
|
|
38
40
|
const virtualId = "virtual:fuzor/client";
|
|
39
41
|
const rendererModules = {
|
|
40
42
|
"virtual:fuzor/compiler/react": 'export { createElement } from "react"; export { renderToString, renderToStaticMarkup } from "react-dom/server";',
|
|
@@ -287,13 +289,6 @@ function replaceOutlet(document, content) {
|
|
|
287
289
|
throw new Error(`index.html must contain ${outletStart} and ${outletEnd}.`);
|
|
288
290
|
return document.slice(0, start + outletStart.length) + content + document.slice(end);
|
|
289
291
|
}
|
|
290
|
-
function serverViewDocument(html) {
|
|
291
|
-
let found = false;
|
|
292
|
-
const document = html.replace(/(<fuzor-router\b[^>]*>)[\s\S]*?<\/fuzor-router>/, (_match, open) => { found = true; return open + "<!--fuzor-view--></fuzor-router>"; });
|
|
293
|
-
if (!found || !document.includes("</body>"))
|
|
294
|
-
throw new Error("Server views require a compiled outlet and a document body.");
|
|
295
|
-
return document.replace("</body>", "<!--fuzor-snapshot--></body>");
|
|
296
|
-
}
|
|
297
292
|
/**
|
|
298
293
|
* The plugin creates private Vite servers to load island and `paths` modules. Their config file loads
|
|
299
294
|
* this plugin again; those nested instances only provide virtual modules and never compile the project.
|
|
@@ -304,17 +299,19 @@ const nestedServer = new AsyncLocalStorage();
|
|
|
304
299
|
* open an HMR WebSocket (two would compete for its port, and a failed one never closes) and must not share the
|
|
305
300
|
* dev server's dependency cache (each would re-optimize and invalidate the other's pre-bundled files).
|
|
306
301
|
*/
|
|
307
|
-
export function nestedServerConfig(config) {
|
|
302
|
+
export function nestedServerConfig(config, nativeModules) {
|
|
308
303
|
return {
|
|
309
304
|
root: config.root,
|
|
310
305
|
configFile: config.configFile ?? false,
|
|
311
|
-
plugins: [workflowPlugin(), {
|
|
306
|
+
plugins: [{ name: "fuzor:internal-specifiers", enforce: "pre", resolveId: id => resolveInternalSpecifier(id) }, htmlServerPlugin(nativeModules), workflowPlugin(), {
|
|
312
307
|
name: "fuzor:nested-renderers",
|
|
313
308
|
resolveId(id) { if (Object.hasOwn(rendererModules, id))
|
|
314
309
|
return `\0${id}`; },
|
|
315
310
|
load(id) { if (id.startsWith("\0") && Object.hasOwn(rendererModules, id.slice(1)))
|
|
316
311
|
return rendererModules[id.slice(1)]; }
|
|
317
312
|
}],
|
|
313
|
+
// Fuzor's server runtime is one module graph: compiler-owned modules and `fuzor/server` must not load twice.
|
|
314
|
+
ssr: { noExternal: ["fuzor"] },
|
|
318
315
|
server: { middlewareMode: true, hmr: false, ws: false },
|
|
319
316
|
cacheDir: path.join(config.cacheDir, "fuzor-nested"),
|
|
320
317
|
optimizeDeps: { noDiscovery: true, include: [] },
|
|
@@ -380,20 +377,6 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
|
|
|
380
377
|
const electron = options.electron ?? envElectron ?? requestedTarget === "electron";
|
|
381
378
|
const target = (requestedTarget === "electron" ? "spa" : requestedTarget);
|
|
382
379
|
const serverReasons = serverModules.map(file => file.startsWith("api/") ? `${file} defines an API endpoint` : file.endsWith(".html") ? `${file} declares request rendering` : `${file} defines server data`);
|
|
383
|
-
if (options.serverComponents) {
|
|
384
|
-
makeClientRouteMatcher(Object.fromEntries(options.serverComponents.routes.map(route => [route, "/"])));
|
|
385
|
-
for (const entry of [options.serverComponents.server, options.serverComponents.client, options.serverComponents.references].filter((value) => value !== undefined)) {
|
|
386
|
-
if (!entry || path.isAbsolute(entry) || entry.split(/[\\/]/).includes(".."))
|
|
387
|
-
throw new Error("Server component entries must be paths inside the project.");
|
|
388
|
-
}
|
|
389
|
-
if (!options.serverComponents.client && !options.serverComponents.references)
|
|
390
|
-
throw new Error("Server components require a client entry or generated reference directory.");
|
|
391
|
-
if (!options.serverComponents.routes.length)
|
|
392
|
-
throw new Error("Server components need at least one route.");
|
|
393
|
-
if (options.navigation === "embedded")
|
|
394
|
-
throw new Error("Server components require HTTP navigation.");
|
|
395
|
-
serverReasons.push(`${options.serverComponents.server} defines server components`);
|
|
396
|
-
}
|
|
397
380
|
const inline = electron || packaged ? true : options.inline ?? envInline ?? false;
|
|
398
381
|
if (inline && target !== "spa")
|
|
399
382
|
throw new Error("Inline output is only supported for the SPA target.");
|
|
@@ -418,27 +401,11 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
|
|
|
418
401
|
return { target, mixed, backendOutDir: ".fuzor/dist-backend", platform, server, inline, outDir: electron ? ".fuzor/dist-desktop" : platform !== "web" ? `.fuzor/dist-${platform}` : mixed ? ".fuzor/web/dist" : `.fuzor/web/dist-${target}`, serverReasons, adapter, ...(electron ? { electron: true } : {}) };
|
|
419
402
|
}
|
|
420
403
|
export function fuzorPlugin(options = {}) {
|
|
404
|
+
if (options.ui?.brand !== undefined)
|
|
405
|
+
validateBrandPrefix(options.ui.brand);
|
|
421
406
|
const nested = nestedServer.getStore() === true;
|
|
407
|
+
const nativeServerModules = new Map();
|
|
422
408
|
let viewBuildId = randomUUID();
|
|
423
|
-
let referencesPromise;
|
|
424
|
-
const references = () => referencesPromise ??= discoverViewReferences(vite.root, options.serverComponents.references);
|
|
425
|
-
const referenceStub = (file, refs) => {
|
|
426
|
-
const relative = path.relative(vite.root, file).split(path.sep).join("/");
|
|
427
|
-
const client = refs.clients.find(ref => ref.file === relative);
|
|
428
|
-
if (client)
|
|
429
|
-
return `export default ${JSON.stringify({ id: client.id })};`;
|
|
430
|
-
const closures = refs.closures.filter(ref => ref.file === relative);
|
|
431
|
-
if (closures.length)
|
|
432
|
-
return readFile(file, "utf8").then(code => replaceClosures(code, closures));
|
|
433
|
-
const handlers = refs.handlers.filter(ref => ref.file === relative);
|
|
434
|
-
return handlers.length ? handlers.map(ref => `export const ${ref.name}=${JSON.stringify({ id: ref.id })};`).join("\n") : undefined;
|
|
435
|
-
};
|
|
436
|
-
const generatedViews = async (views) => {
|
|
437
|
-
if (!options.serverComponents?.references)
|
|
438
|
-
return views;
|
|
439
|
-
const refs = await references();
|
|
440
|
-
return { ...views, manifest: { buildId: viewBuildId, templates: refs.templates }, components: refs.clients.map(ref => ref.id), handlers: [...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)] };
|
|
441
|
-
};
|
|
442
409
|
const configuredEnvironments = normalizeEnvironmentConfiguration(options.experimental?.environments);
|
|
443
410
|
const environmentNames = {
|
|
444
411
|
client: configuredEnvironments.client ?? "client",
|
|
@@ -481,6 +448,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
481
448
|
let project;
|
|
482
449
|
let pageScripts = [];
|
|
483
450
|
const nativeHandlers = new Map();
|
|
451
|
+
const authoredNative = new Map();
|
|
452
|
+
let nativeAdmissions = new Map();
|
|
484
453
|
let inlineModuleFiles = [];
|
|
485
454
|
let scriptRoutes = [];
|
|
486
455
|
const routeScriptId = (route) => `virtual:fuzor/route-client/${encodeURIComponent(route.path)}/${route.path === "/" ? "index" : route.path.split("/").filter(Boolean).join("-").replace(/[^a-zA-Z0-9_-]/g, "_")}-client.js`;
|
|
@@ -503,7 +472,7 @@ export function fuzorPlugin(options = {}) {
|
|
|
503
472
|
let solidHydrationScript = "";
|
|
504
473
|
const pending = (async () => {
|
|
505
474
|
let owned;
|
|
506
|
-
const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite)));
|
|
475
|
+
const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite, nativeServerModules)));
|
|
507
476
|
const moduleServer = () => devServer ? Promise.resolve(devServer) : owned ??= createModuleServer();
|
|
508
477
|
// client:none analysis reads client transforms without HMR code, which the dev server would add. It has its
|
|
509
478
|
// own server: client transforms wait for pending requests to settle, and preview renders on the module
|
|
@@ -672,6 +641,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
672
641
|
const compileRaw = async (signal, loadModule) => {
|
|
673
642
|
let compiled = await compileProject(vite.root, { ...(options.markdown ? { markdown: options.markdown } : {}), ...(options.redirects ? { redirects: options.redirects } : {}), platform: profile.platform, signal, loadModule, cache: compileCache })
|
|
674
643
|
.catch(async (error) => { throw await withViteLocation(error); });
|
|
644
|
+
nativeServerModules.clear();
|
|
645
|
+
compiled = await prepareNativeScripts(compiled, vite.root, loadModule, nativeServerModules);
|
|
675
646
|
const entry = options.entry ?? process.env.FUZOR_ENTRY;
|
|
676
647
|
if (entry) {
|
|
677
648
|
if (!inline || target !== "spa")
|
|
@@ -688,15 +659,20 @@ export function fuzorPlugin(options = {}) {
|
|
|
688
659
|
if (!compiled.routes.some(route => route.path === "/"))
|
|
689
660
|
throw new Error("Application entry must have a static root template; request-rendered routes require an HTTP rendering deployment.");
|
|
690
661
|
}
|
|
662
|
+
const inferredServer = compiled.routes.filter(route => route.serverData || route.policy?.rendering === "request");
|
|
663
|
+
if (inferredServer.length) {
|
|
664
|
+
profile = resolveFuzorProfile(options, process.env, [...new Set([...serverModuleFiles, ...inferredServer.map(route => path.relative(vite.root, route.source).split(path.sep).join("/"))])]);
|
|
665
|
+
withServer = profile.server;
|
|
666
|
+
}
|
|
691
667
|
const deployment = options.deployment ?? process.env.FUZOR_DEPLOYMENT;
|
|
692
668
|
const requestRoutes = compiled.routes.filter(route => route.policy?.rendering === "request");
|
|
693
669
|
if (deployment && !["static", "github-pages", "node", "cloudflare"].includes(deployment))
|
|
694
670
|
throw new Error("Unknown Fuzor deployment target: " + deployment);
|
|
695
|
-
if ((deployment === "static" || deployment === "github-pages") &&
|
|
696
|
-
throw new Error(`Route ${
|
|
671
|
+
if ((deployment === "static" || deployment === "github-pages") && requestRoutes.length)
|
|
672
|
+
throw new Error(`Route ${requestRoutes[0].path} requires request-time server rendering. ${deployment} supports static files only. Pre-render this route, regenerate and republish its files, or choose node/cloudflare rendering. Remote API calls from static SPAs are allowed.`);
|
|
697
673
|
for (const route of requestRoutes)
|
|
698
|
-
if (route.requestPattern && route.policy?.navigation === "spa" && !
|
|
699
|
-
throw new Error(`${route.source}: unenumerated request routes inside an SPA
|
|
674
|
+
if (route.requestPattern && route.policy?.navigation === "spa" && !route.serverData?.startsWith("virtual:fuzor/native-data/"))
|
|
675
|
+
throw new Error(`${route.source}: unenumerated request routes inside an SPA need an HTML server component. Add one or use navigation=document.`);
|
|
700
676
|
if (options.routeTree) {
|
|
701
677
|
if (target !== "spa" && !profile.mixed)
|
|
702
678
|
throw new Error("routeTree requires a SPA target.");
|
|
@@ -716,8 +692,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
716
692
|
check(options.routeTree);
|
|
717
693
|
}
|
|
718
694
|
if (options.clientRoutes && Object.keys(options.clientRoutes).length) {
|
|
719
|
-
if (target !== "spa"
|
|
720
|
-
throw new Error("clientRoutes requires
|
|
695
|
+
if (target !== "spa")
|
|
696
|
+
throw new Error("clientRoutes requires an SPA target.");
|
|
721
697
|
makeClientRouteMatcher(options.clientRoutes);
|
|
722
698
|
for (const target of Object.values(options.clientRoutes)) {
|
|
723
699
|
const route = compiled.routes.find(route => route.path === routeKey(target));
|
|
@@ -735,7 +711,18 @@ export function fuzorPlugin(options = {}) {
|
|
|
735
711
|
const extracted = extractPageScripts(compiled, vite.root);
|
|
736
712
|
pageScripts = extracted.scripts;
|
|
737
713
|
scriptRoutes = extracted.routes;
|
|
738
|
-
|
|
714
|
+
authoredNative.clear();
|
|
715
|
+
nativeHandlers.clear();
|
|
716
|
+
for (const script of pageScripts)
|
|
717
|
+
if (script.native) {
|
|
718
|
+
const compiled = compileAuthoredNativeScript(script.code ?? await readFile(script.source, "utf8"), script, viewBuildId);
|
|
719
|
+
authoredNative.set(script.id, compiled);
|
|
720
|
+
for (const handler of compiled.handlers)
|
|
721
|
+
nativeHandlers.set(handler.id, handler);
|
|
722
|
+
}
|
|
723
|
+
const emitted = emitNativeManifests(extracted.project, scriptRoutes, pageScripts, authoredNative, viewBuildId);
|
|
724
|
+
nativeAdmissions = emitted.admissions;
|
|
725
|
+
return emitted.project;
|
|
739
726
|
};
|
|
740
727
|
const scanIslands = async () => {
|
|
741
728
|
return Effect.runPromise(scanIslandModulesEffect(vite.root, Object.keys(options.runtimes ?? {})));
|
|
@@ -743,7 +730,6 @@ export function fuzorPlugin(options = {}) {
|
|
|
743
730
|
const refresh = async (server) => {
|
|
744
731
|
const version = ++revision;
|
|
745
732
|
viewBuildId = randomUUID();
|
|
746
|
-
referencesPromise = undefined;
|
|
747
733
|
compilation?.abort();
|
|
748
734
|
project = undefined;
|
|
749
735
|
projectPromise = undefined;
|
|
@@ -810,8 +796,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
810
796
|
profile = resolveFuzorProfile(options, process.env, serverModuleFiles);
|
|
811
797
|
({ target, inline } = profile);
|
|
812
798
|
withServer = profile.server;
|
|
813
|
-
if (options.clientRoutes && Object.keys(options.clientRoutes).length &&
|
|
814
|
-
throw new Error("clientRoutes requires
|
|
799
|
+
if (options.clientRoutes && Object.keys(options.clientRoutes).length && target !== "spa")
|
|
800
|
+
throw new Error("clientRoutes requires an SPA target.");
|
|
815
801
|
const packageJson = await readFile(path.join(path.resolve(config.root ?? process.cwd()), "package.json"), "utf8").then(text => JSON.parse(text), () => ({}));
|
|
816
802
|
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
817
803
|
const include = ["Cause", "Context", "Deferred", "Effect", "Exit", "Fiber", "Layer", "Schema", "Scope", "reactivity/Atom", "reactivity/AtomRegistry"].map(module => `fuzor > effect/${module}`);
|
|
@@ -850,11 +836,14 @@ export function fuzorPlugin(options = {}) {
|
|
|
850
836
|
}
|
|
851
837
|
},
|
|
852
838
|
async resolveId(id, importer, resolveOptions) {
|
|
839
|
+
const internalFile = resolveInternalSpecifier(id);
|
|
840
|
+
if (internalFile)
|
|
841
|
+
return internalFile;
|
|
842
|
+
const serverModule = resolveHtmlServer(id, resolveOptions.ssr, nativeServerModules);
|
|
843
|
+
if (serverModule)
|
|
844
|
+
return serverModule;
|
|
853
845
|
if (!resolveOptions.ssr)
|
|
854
846
|
assertBrowserViewImport(id, importer);
|
|
855
|
-
if (id === "fuzor/server-components/server" && !resolveOptions.ssr) {
|
|
856
|
-
throw new Error("The server-component evaluator is server-only. Import browser runtime and protocol separately.");
|
|
857
|
-
}
|
|
858
847
|
if (id.startsWith("virtual:fuzor/route-client/"))
|
|
859
848
|
return `\0${id}`;
|
|
860
849
|
if (id.startsWith("virtual:fuzor/page-script/"))
|
|
@@ -945,6 +934,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
945
934
|
}
|
|
946
935
|
},
|
|
947
936
|
async load(id, loadOptions) {
|
|
937
|
+
if (id.includes("?fuzor-server-script=") || nativeServerModules.has(id))
|
|
938
|
+
return loadHtmlServer(id, nativeServerModules, file => this.addWatchFile(file));
|
|
948
939
|
if (id.startsWith("\0virtual:fuzor/route-client/")) {
|
|
949
940
|
await loadProject();
|
|
950
941
|
const route = scriptRoutes.find(route => `\0${routeScriptId(route)}` === id);
|
|
@@ -959,6 +950,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
959
950
|
throw new Error("Unknown Fuzor page script");
|
|
960
951
|
this.addWatchFile(script.source);
|
|
961
952
|
const source = script.code ?? await readFile(script.source, "utf8");
|
|
953
|
+
if (script.native)
|
|
954
|
+
throw new Error("Authored native declarations are adopted from HTML, not executable descriptor modules");
|
|
962
955
|
const native = compileNativeScript(source, script);
|
|
963
956
|
if (native) {
|
|
964
957
|
for (const handler of native.handlers)
|
|
@@ -974,31 +967,6 @@ export function fuzorPlugin(options = {}) {
|
|
|
974
967
|
throw new Error("Unknown native handler");
|
|
975
968
|
return (await import("vite")).transformWithEsbuild(handler.code, handler.source, { loader: handler.language, target: "es2022" });
|
|
976
969
|
}
|
|
977
|
-
if (id.startsWith("\0fuzor:closure:")) {
|
|
978
|
-
const refs = await references();
|
|
979
|
-
const closure = refs.closures.find(ref => id === `\0fuzor:closure:${ref.id}.ts`);
|
|
980
|
-
if (!closure)
|
|
981
|
-
throw new Error("Unknown resumable closure reference");
|
|
982
|
-
return (await import("vite")).transformWithEsbuild(closure.code, id, { loader: "ts", target: "es2022" }).then(result => result.code);
|
|
983
|
-
}
|
|
984
|
-
if (options.serverComponents?.references) {
|
|
985
|
-
const refs = await references();
|
|
986
|
-
if (id === "\0virtual:fuzor/views") {
|
|
987
|
-
if (loadOptions?.ssr)
|
|
988
|
-
return `export const manifest=${JSON.stringify({ buildId: viewBuildId, templates: refs.templates })}; export const components=${JSON.stringify(refs.clients.map(ref => ref.id))}; export const handlers=${JSON.stringify([...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)])};`;
|
|
989
|
-
return browserReferencesCode(refs, viewBuildId, file => "/" + file);
|
|
990
|
-
}
|
|
991
|
-
if (loadOptions?.ssr) {
|
|
992
|
-
const stub = referenceStub(id.split("?")[0], refs);
|
|
993
|
-
if (stub)
|
|
994
|
-
return stub;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
if (id.startsWith("\0fuzor:fragment:")) {
|
|
998
|
-
const file = id.slice("\0fuzor:fragment:".length, -3);
|
|
999
|
-
this.addWatchFile(file);
|
|
1000
|
-
return readFile(file, "utf8").then(html => `export default ${JSON.stringify(compileFragment(html))};`);
|
|
1001
|
-
}
|
|
1002
970
|
if (Object.hasOwn(rendererModules, id.slice(1)))
|
|
1003
971
|
return rendererModules[id.slice(1)];
|
|
1004
972
|
const runtimeModule = runtimeModules.get(id);
|
|
@@ -1007,7 +975,7 @@ export function fuzorPlugin(options = {}) {
|
|
|
1007
975
|
if (!loadOptions?.ssr && !nested && vite) {
|
|
1008
976
|
const file = id.split("?")[0];
|
|
1009
977
|
const relative = path.relative(vite.root, file).split(path.sep).join("/");
|
|
1010
|
-
if (
|
|
978
|
+
if (serverModuleFiles.includes(relative) || relative.startsWith("api/") || /\.server\.[cm]?[jt]sx?$/.test(relative)) {
|
|
1011
979
|
throw new Error(`${relative} is server-only and cannot be imported by browser code. Read its data with <fuzor-data> or routeData() instead.`);
|
|
1012
980
|
}
|
|
1013
981
|
}
|
|
@@ -1015,7 +983,32 @@ export function fuzorPlugin(options = {}) {
|
|
|
1015
983
|
return undefined;
|
|
1016
984
|
await loadProject();
|
|
1017
985
|
const mixedClient = profile.mixed && project?.routes.some(route => route.policy?.navigation === "spa");
|
|
1018
|
-
const scriptRegistry = scriptRoutes.map(route =>
|
|
986
|
+
const scriptRegistry = scriptRoutes.map(route => nativeAdmissions.has(route.path)
|
|
987
|
+
? `${JSON.stringify(route.path)}: async () => readNativeManifest(outlet, ${JSON.stringify(route.path)}, ${JSON.stringify(viewBuildId)}, ${JSON.stringify(nativeAdmissions.get(route.path))}, nativeLoaders)`
|
|
988
|
+
: `${JSON.stringify(route.path)}: () => import(${JSON.stringify(routeScriptId(route))}).catch(reloadOnStaleDeployment)`).join(",\n");
|
|
989
|
+
const handlerLoaders = new Map();
|
|
990
|
+
const graphLoaders = new Map();
|
|
991
|
+
const cssImports = new Set();
|
|
992
|
+
for (const script of pageScripts) {
|
|
993
|
+
const compiled = authoredNative.get(script.id);
|
|
994
|
+
if (!compiled)
|
|
995
|
+
continue;
|
|
996
|
+
this.addWatchFile(script.source);
|
|
997
|
+
for (const css of compiled.css) {
|
|
998
|
+
const resolved = await this.resolve(css, script.source);
|
|
999
|
+
if (!resolved)
|
|
1000
|
+
throw new Error(`${script.source}: cannot resolve native stylesheet ${css}`);
|
|
1001
|
+
cssImports.add(`import ${JSON.stringify(resolved.id)};`);
|
|
1002
|
+
}
|
|
1003
|
+
for (const [name, id] of compiled.actionIds)
|
|
1004
|
+
if ((script.native.events.some(event => event.action === name) || script.native.effects?.some(effect => effect.action === name)))
|
|
1005
|
+
handlerLoaders.set(id, `${JSON.stringify(id)}: {server: ${Object.hasOwn(script.native.serverActions ?? {}, name)}, load: () => import(${JSON.stringify(`virtual:fuzor/native-handler/${id}.js`)}).catch(reloadOnStaleDeployment)}`);
|
|
1006
|
+
if (compiled.graphId)
|
|
1007
|
+
graphLoaders.set(compiled.graphId, `${JSON.stringify(compiled.graphId)}: (definition, data) => import("fuzor/runtime/native-graph").then(module => module.nativeGraphModule(definition, data, ${JSON.stringify(script.native.computed ?? {})}, ${JSON.stringify(script.native.ranges ?? [])}, [${(script.native.effects ?? []).map(effect => `{dependencies:${JSON.stringify(effect.dependencies)},load:()=>import(${JSON.stringify(`virtual:fuzor/native-handler/${compiled.actionIds.get(effect.action)}.js`)})}`).join(",")}]))`);
|
|
1008
|
+
}
|
|
1009
|
+
const manifestScripts = new Set(scriptRoutes.filter(route => nativeAdmissions.has(route.path)).flatMap(route => route.bindings.filter(binding => !authoredNative.has(binding.id)).map(binding => binding.id)));
|
|
1010
|
+
const nativeData = pageScripts.some(script => script.native?.request);
|
|
1011
|
+
const nativeLoaders = nativeAdmissions.size ? `const nativeLoaders = { ${nativeData ? "data: routeData," : ""} handlers: {${[...handlerLoaders.values()].join(",")}}, graphs: {${[...graphLoaders.values()].join(",")}}, scripts: {${[...manifestScripts].map(id => `${JSON.stringify(id)}: () => import(${JSON.stringify(pageScriptId(pageScripts.find(script => script.id === id)))}).catch(reloadOnStaleDeployment)`).join(",")}} };` : "";
|
|
1019
1012
|
const registry = islands.map((island) => {
|
|
1020
1013
|
const load = island.file.endsWith(".vue")
|
|
1021
1014
|
? `Promise.all([import(${JSON.stringify(island.file)}), import("fuzor/islands/vue")]).then(([component, adapter]) => adapter.vueIsland(component.default)).catch(reloadOnStaleDeployment)`
|
|
@@ -1023,16 +1016,24 @@ export function fuzorPlugin(options = {}) {
|
|
|
1023
1016
|
return `${JSON.stringify(island.name)}: () => ${load}`;
|
|
1024
1017
|
}).join(",\n");
|
|
1025
1018
|
const withData = target === "mpa" && !!project?.routes.some(route => route.serverData);
|
|
1019
|
+
// Keep every discovered module, including ones introduced by page scripts. An empty static
|
|
1020
|
+
// registry cannot activate an island; retain diagnostics for authored hosts and open server/dev paths.
|
|
1021
|
+
const withIslands = islands.length > 0 || withServer || vite.command === "serve"
|
|
1022
|
+
|| [...(project?.routes ?? []), ...(project?.boundaries ?? [])].some(route => /<fuzor-island\b/i.test(route.html));
|
|
1023
|
+
const mountIslandsCode = withIslands ? "mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal })" : "Promise.resolve(() => {})";
|
|
1026
1024
|
const hasDocs = (project?.routes ?? []).some(route => route.path === "/docs/" || route.path.startsWith("/docs/"));
|
|
1027
1025
|
const routerModule = profile.electron || options.navigation === "embedded" ? "fuzor/runtime/router/embedded" : options.navigation === "navigation-api" ? "fuzor/runtime/router/navigation-api" : "fuzor/runtime";
|
|
1028
1026
|
const imports = [
|
|
1027
|
+
...cssImports,
|
|
1028
|
+
...(nativeAdmissions.size ? ['import { readNativeManifest } from "fuzor/runtime/native-manifest";'] : []),
|
|
1029
|
+
...(options.ui?.brand === undefined ? [] : ['import { defineBrand } from "fuzor/ui/core";']),
|
|
1029
1030
|
(target === "spa" || mixedClient)
|
|
1030
1031
|
? `import { createRouter } from "${routerModule}"; import { reloadOnStaleDeployment } from "fuzor/runtime/islands";`
|
|
1031
|
-
:
|
|
1032
|
+
: `import { ${withIslands ? "mountIslands, islandErrorHandler, " : ""}reloadOnStaleDeployment } from "fuzor/runtime/islands";`,
|
|
1032
1033
|
...(target === "mpa" ? ['import { mountPageScripts } from "fuzor/runtime/page-scripts"; import { stripBase } from "fuzor/runtime";'] : []),
|
|
1033
|
-
...(
|
|
1034
|
-
...(
|
|
1035
|
-
...(
|
|
1034
|
+
...(mixedClient && withIslands ? ['import { mountIslands, islandErrorHandler } from "fuzor/runtime/islands";'] : []),
|
|
1035
|
+
...(withData || nativeData ? [`import { ${[withData ? "loadRouteData" : "", nativeData ? "routeData" : ""].filter(Boolean).join(",")} } from "fuzor/runtime/data";`] : []),
|
|
1036
|
+
...(nativeData ? ['import { enableNativeStreams } from "fuzor/runtime/native-stream";'] : []),
|
|
1036
1037
|
...(hasDocs ? ['import { mountDocsUi } from "fuzor/runtime/docs-ui";'] : [])
|
|
1037
1038
|
];
|
|
1038
1039
|
const docsInit = hasDocs ? `let stopDocsUi;
|
|
@@ -1042,17 +1043,16 @@ const disposeDocs = () => {
|
|
|
1042
1043
|
if (docsDisposed) return;
|
|
1043
1044
|
docsDisposed = true;
|
|
1044
1045
|
window.removeEventListener("pagehide", hideDocs);
|
|
1045
|
-
stopDocsUi?.();
|
|
1046
|
+
void stopDocsUi?.().catch(error => console.error(error));
|
|
1046
1047
|
};
|
|
1047
1048
|
void mountDocsUi({ document, base: import.meta.env.BASE_URL }).then(stop => {
|
|
1048
1049
|
stopDocsUi = stop;
|
|
1049
|
-
if (docsDisposed) stop();
|
|
1050
|
+
if (docsDisposed) void stop().catch(error => console.error(error));
|
|
1050
1051
|
}, error => console.error(error));
|
|
1051
1052
|
window.addEventListener("pagehide", hideDocs);
|
|
1052
1053
|
if (import.meta.hot) import.meta.hot.dispose(disposeDocs);
|
|
1053
1054
|
` : "";
|
|
1054
|
-
const
|
|
1055
|
-
const spaStart = `export const router = (${mixedClient ? 'outlet.getAttribute("data-fuzor-navigation") === "spa" && ' : ""}!outlet.hasAttribute("data-fuzor-not-found") && !outlet.hasAttribute("data-fuzor-error")) ? createRouter(outlet, { islands, scripts, base: import.meta.env.BASE_URL${options.serverComponents ? `, renderer: serverViewRoutes({ ${effectiveViewOptions}, routes: ${JSON.stringify(options.serverComponents.routes)}, acceptsRoute: path => !${mixedClient} || [...document.querySelectorAll("template[data-fuzor-route]")].some(t => t.getAttribute("data-fuzor-route") === path || t.hasAttribute("data-fuzor-request-pattern") && makeClientRouteMatcher({[t.getAttribute("data-fuzor-request-pattern")]: "/"})(path)), base: import.meta.env.BASE_URL })` : ""}${options.clientRoutes ? `, clientRoutes: ${JSON.stringify(options.clientRoutes)}` : ""}${options.routeTree ? `, routeTree: ${JSON.stringify(options.routeTree)}` : ""}${options.viewTransitions ? ", viewTransitions: true" : ""}${options.prefetch === "intent" ? ', prefetch: "intent"' : ""} }) : undefined;
|
|
1055
|
+
const spaStart = `export const router = (${mixedClient ? 'outlet.getAttribute("data-fuzor-navigation") === "spa" && ' : ""}!outlet.hasAttribute("data-fuzor-not-found") && !outlet.hasAttribute("data-fuzor-error")) ? createRouter(outlet, { islands, scripts, base: import.meta.env.BASE_URL${options.clientRoutes ? `, clientRoutes: ${JSON.stringify(options.clientRoutes)}` : ""}${options.routeTree ? `, routeTree: ${JSON.stringify(options.routeTree)}` : ""}${options.viewTransitions ? ", viewTransitions: true" : ""}${options.prefetch === "intent" ? ', prefetch: "intent"' : ""} }) : undefined;
|
|
1056
1056
|
export const ready = router ? router.start() : Promise.resolve(undefined);
|
|
1057
1057
|
void ready.catch(error => console.error(error));
|
|
1058
1058
|
if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });`;
|
|
@@ -1061,13 +1061,11 @@ if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });
|
|
|
1061
1061
|
const lifetime = new AbortController();
|
|
1062
1062
|
let cleanup;
|
|
1063
1063
|
const dispose = () => { lifetime.abort(); void cleanup?.(); };
|
|
1064
|
-
|
|
1065
|
-
const mountComponents = viewRenderer ? viewRenderer.resume().then(() => () => viewRenderer.dispose()) : mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });` : `const mountComponents = mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });`}
|
|
1066
|
-
${options.serverComponents ? "const viewRendererActive = !!viewRenderer;" : "const viewRendererActive = false;"}
|
|
1064
|
+
const mountComponents = ${mountIslandsCode};
|
|
1067
1065
|
const pathname = stripBase(location.pathname, import.meta.env.BASE_URL) ?? location.pathname;
|
|
1068
1066
|
const routePath = pathname.endsWith("/") ? pathname : pathname + "/";
|
|
1069
1067
|
const scriptRoute = Object.hasOwn(scripts, routePath) ? routePath : outlet.getAttribute("data-fuzor-pattern") ?? "";
|
|
1070
|
-
const mount = (async () => { const stopComponents = await mountComponents; try { const stopScripts = await mountPageScripts(outlet,
|
|
1068
|
+
const mount = (async () => { const stopComponents = await mountComponents; try { const stopScripts = await mountPageScripts(outlet, scripts, { signal: lifetime.signal, route: scriptRoute }); return async () => { await stopScripts(); await stopComponents(); }; } catch (error) { await stopComponents(); throw error; } })();
|
|
1071
1069
|
void mount.then(stop => {
|
|
1072
1070
|
cleanup = stop;
|
|
1073
1071
|
if (lifetime.signal.aborted) void stop();
|
|
@@ -1075,7 +1073,10 @@ void mount.then(stop => {
|
|
|
1075
1073
|
window.addEventListener("pagehide", event => { if (!event.persisted) dispose(); });
|
|
1076
1074
|
if (import.meta.hot) import.meta.hot.dispose(dispose);`;
|
|
1077
1075
|
return `${imports.join("\n")}
|
|
1076
|
+
${options.ui?.brand === undefined ? "" : `defineBrand(${JSON.stringify(options.ui.brand)});`}
|
|
1078
1077
|
const islands = { ${registry} };
|
|
1078
|
+
${nativeLoaders}
|
|
1079
|
+
${nativeData ? `enableNativeStreams(${JSON.stringify(viewBuildId)});` : ""}
|
|
1079
1080
|
const scripts = { ${scriptRegistry} };
|
|
1080
1081
|
const outlet = document.querySelector("fuzor-router");
|
|
1081
1082
|
if (!outlet) throw new Error("Fuzor outlet is missing");
|
|
@@ -1092,6 +1093,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1092
1093
|
const pathname = stripBase(requested, pathBase) ?? requested;
|
|
1093
1094
|
const currentPath = findRoute(compiled, pathname) ? pathname : "/";
|
|
1094
1095
|
parseRoutePolicy(raw, path.join(vite.root, "index.html"), false);
|
|
1096
|
+
assertNoServerScripts(raw, path.join(vite.root, "index.html"));
|
|
1095
1097
|
const shell = compilePlatformBlocks(raw, profile.platform, path.join(vite.root, "index.html"));
|
|
1096
1098
|
shellHead = readDocumentHead(shell);
|
|
1097
1099
|
let html = applyDocumentMetadata(replaceOutlet(shell, outletFor(compiled, target, currentPath, fallbacks(), profile.mixed)), pageHead(shellHead, findRoute(compiled, currentPath)?.metadata ?? {}));
|
|
@@ -1163,10 +1165,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1163
1165
|
const compiled = await loadProject();
|
|
1164
1166
|
const manifest = serverManifest(compiled, target, template, pathBase, fallbacks(), withStaticModuleImports, options.clientRoutes, `/${vite.build.assetsDir}/`, profile.mixed);
|
|
1165
1167
|
const data = dataRequest || compiled.routes.some(route => route.policy?.rendering === "request") ? await loadRouteDataModules(compiled, file => createEnvironmentModuleRuntime(server, moduleEnvironment, environmentApiEnabled).loadModule(file), vite.root) : undefined;
|
|
1166
|
-
|
|
1167
|
-
if (views && JSON.stringify(Object.keys(views.routes).sort()) !== JSON.stringify([...options.serverComponents.routes].sort()))
|
|
1168
|
-
throw new Error("Server view routes must match serverComponents.routes.");
|
|
1169
|
-
result = await createServerHandler(manifest, { ...(data ? { data } : {}), ...(views ? { views: { ...views, manifest: { ...views.manifest, buildId: viewBuildId }, documentPatterns: manifest.requestPatterns ?? {}, document: serverViewDocument(template), documents: Object.fromEntries(Object.entries("documents" in manifest ? manifest.documents : manifest.routeDocuments ?? {}).map(([route, html]) => [route, serverViewDocument(html)])) } } : {}), onError })(toWebRequest(request, { url, signal: controller.signal }));
|
|
1168
|
+
result = await createServerHandler({ ...manifest, buildId: viewBuildId }, { renderBinding: renderNativeBinding, ...(data ? { data } : {}), onError })(toWebRequest(request, { url, signal: controller.signal }));
|
|
1170
1169
|
}
|
|
1171
1170
|
await writeWebResponse(response, result, { method: request.method ?? "GET", signal: controller.signal });
|
|
1172
1171
|
}
|
|
@@ -1207,7 +1206,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1207
1206
|
}
|
|
1208
1207
|
});
|
|
1209
1208
|
server.watcher.on("add", (file) => {
|
|
1210
|
-
if (pageScripts.some(script => script.source === file) ||
|
|
1209
|
+
if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.replaceAll("\\", "/").includes(part))) {
|
|
1211
1210
|
void refresh(server).catch((error) => server.config.logger.error(String(error)));
|
|
1212
1211
|
}
|
|
1213
1212
|
});
|
|
@@ -1320,8 +1319,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1320
1319
|
await mkdir(serverDir, { recursive: true });
|
|
1321
1320
|
await writeFile(path.join(serverDir, "fuzor.manifest.json"), JSON.stringify({ ...artifact, clientDirectory: path.relative(serverDir, outDir).split(path.sep).join("/") }, null, 2) + "\n");
|
|
1322
1321
|
const dataRoutes = compiled.routes.filter(route => route.serverData);
|
|
1323
|
-
|
|
1324
|
-
if (!dataRoutes.length && !apiFiles.length && !options.serverComponents && !options.serverExternal) {
|
|
1322
|
+
if (!dataRoutes.length && !apiFiles.length && !options.serverExternal) {
|
|
1325
1323
|
await writeFile(path.join(serverDir, "entry.mjs"), `import { createServerHandler } from "fuzor/server";\n` +
|
|
1326
1324
|
`export const manifest = ${JSON.stringify(manifest)};\n` +
|
|
1327
1325
|
`export const handler = createServerHandler(manifest);\n`, "utf8");
|
|
@@ -1331,8 +1329,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1331
1329
|
const files = [...new Set(dataRoutes.map(route => route.serverData))];
|
|
1332
1330
|
const entryId = "virtual:fuzor/server-entry";
|
|
1333
1331
|
const code = [
|
|
1334
|
-
`import { createServerHandler, isServerData } from "fuzor/server";`,
|
|
1335
|
-
...(options.serverComponents ? [`import suppliedViews from ${JSON.stringify(path.resolve(vite.root, options.serverComponents.server))};`, `const views = { ...suppliedViews${refs ? `, manifest: ${JSON.stringify({ buildId: viewBuildId, templates: refs.templates })}, components: ${JSON.stringify(refs.clients.map(ref => ref.id))}, handlers: ${JSON.stringify([...refs.handlers.map(ref => ref.id), ...refs.closures.map(ref => ref.id)])}` : ""} };`, `if (JSON.stringify(Object.keys(views.routes).sort()) !== ${JSON.stringify(JSON.stringify([...options.serverComponents.routes].sort()))}) throw new Error("Server view routes must match serverComponents.routes.");`] : []),
|
|
1332
|
+
`import { createServerHandler, isServerData${Object.values(manifest.dataBindings ?? {}).some(bindings => bindings.some(binding => binding.kind === "range")) ? ", renderNativeBinding" : ""} } from "fuzor/server";`,
|
|
1336
1333
|
...files.map((file, index) => `import data${index} from ${JSON.stringify(file)};`),
|
|
1337
1334
|
...apiFiles.map((route, index) => `import * as api${index} from ${JSON.stringify(route.file)};`),
|
|
1338
1335
|
`const modules = [${files.map((_, index) => `data${index}`).join(", ")}];`,
|
|
@@ -1341,14 +1338,15 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1341
1338
|
`export const manifest = ${JSON.stringify(manifest)};`,
|
|
1342
1339
|
`const routes = ${JSON.stringify(dataRoutes.map(route => [route.path, files.indexOf(route.serverData), route.params ?? null]))};`,
|
|
1343
1340
|
`const api = [${apiFiles.map((route, index) => `{ pattern: ${JSON.stringify(route.pattern)}, segments: ${JSON.stringify(route.segments)}, source: ${JSON.stringify(route.relative)}, module: api${index} }`).join(", ")}];`,
|
|
1344
|
-
`export const handler = createServerHandler(manifest, { data: Object.fromEntries(routes.map(([route, index, params]) => [route, params ? { data: modules[index], params } : { data: modules[index] }])), api${
|
|
1341
|
+
`export const handler = createServerHandler(manifest, { data: Object.fromEntries(routes.map(([route, index, params]) => [route, params ? { data: modules[index], params } : { data: modules[index] }])), api${Object.values(manifest.dataBindings ?? {}).some(bindings => bindings.some(binding => binding.kind === "range")) ? ", renderBinding: renderNativeBinding" : ""} });`
|
|
1345
1342
|
].join("\n");
|
|
1346
1343
|
await nestedServer.run(true, () => viteBuild({
|
|
1347
1344
|
root: vite.root, mode: vite.mode, configFile: vite.configFile ?? false, logLevel: "error", publicDir: false,
|
|
1348
|
-
// One runtime copy:
|
|
1349
|
-
//
|
|
1350
|
-
|
|
1351
|
-
|
|
1345
|
+
// One runtime copy: Fuzor's own server runtime is always bundled into the entry (generated code reaches modules
|
|
1346
|
+
// that are not package entries), so the entry and server modules share one copy of it. `effect` is imported from
|
|
1347
|
+
// node_modules by default; with `serverExternal`, everything it does not name is bundled once.
|
|
1348
|
+
ssr: options.serverExternal ? { external: options.serverExternal.filter(name => name !== "fuzor"), noExternal: true } : { external: ["effect"], noExternal: ["fuzor"] },
|
|
1349
|
+
plugins: [{ name: "fuzor:internal-specifiers", enforce: "pre", resolveId: id => resolveInternalSpecifier(id) }, htmlServerPlugin(nativeServerModules), workflowPlugin(), { name: "fuzor:server-entry", resolveId: id => id === entryId ? `\0${entryId}` : undefined, load: id => id === `\0${entryId}` ? code : undefined }],
|
|
1352
1350
|
build: { ssr: true, outDir: serverDir, emptyOutDir: false, copyPublicDir: false, minify: false,
|
|
1353
1351
|
rollupOptions: { input: entryId, output: { entryFileNames: "entry.mjs", chunkFileNames: "chunks/[name]-[hash].mjs", format: "es" } } }
|
|
1354
1352
|
}));
|
|
@@ -1356,7 +1354,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1356
1354
|
// The same finished documents are also written to client/ below, so it can be served by a CDN or
|
|
1357
1355
|
// static host with only /api and server data requests reaching the server.
|
|
1358
1356
|
if (profile.adapter === "cloudflare")
|
|
1359
|
-
await writeCloudflareOutput(serverDir, vite.root, target, pathBase, outDir, [...compiled.routes.filter(route => route.policy?.rendering === "request").map(route => route.requestPattern ? route.requestPattern.slice(0, route.requestPattern.indexOf(":")) + "*" : route.path)
|
|
1357
|
+
await writeCloudflareOutput(serverDir, vite.root, target, pathBase, outDir, [...compiled.routes.filter(route => route.policy?.rendering === "request").map(route => route.requestPattern ? route.requestPattern.slice(0, route.requestPattern.indexOf(":")) + "*" : route.path)], path.relative(path.resolve(vite.root, profile.backendOutDir), process.env.FUZOR_STAGE_DIR && outDir.startsWith(path.resolve(process.env.FUZOR_STAGE_DIR) + path.sep) ? path.resolve(vite.root, path.relative(process.env.FUZOR_STAGE_DIR, outDir)) : outDir).split(path.sep).join("/"));
|
|
1360
1358
|
}
|
|
1361
1359
|
const index = searchIndex(compiled, options.searchIndex);
|
|
1362
1360
|
if (index)
|
|
@@ -1406,7 +1404,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1406
1404
|
if (nested)
|
|
1407
1405
|
return;
|
|
1408
1406
|
const file = context.file.replaceAll("\\", "/");
|
|
1409
|
-
if (pageScripts.some(script => script.source === file) ||
|
|
1407
|
+
if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.includes(part))) {
|
|
1410
1408
|
const environment = context.environment?.name;
|
|
1411
1409
|
if (environment)
|
|
1412
1410
|
invalidateEnvironmentModule(context.server, resolvedVirtualId, environment, environmentApiEnabled);
|