fuzor 0.5.0 → 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 +12 -10
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +115 -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}`);
|
|
@@ -821,7 +807,9 @@ export function fuzorPlugin(options = {}) {
|
|
|
821
807
|
include.push("vue");
|
|
822
808
|
return {
|
|
823
809
|
resolve: { dedupe: ["react", "react-dom", "solid-js", "vue"] },
|
|
824
|
-
|
|
810
|
+
// A `*.fragment.html` is a compiled fragment, not a page: Vite's dependency scan would load it as an entry and
|
|
811
|
+
// fail on the virtual module it resolves to. Pages keep Vite's own default of every `.html` file.
|
|
812
|
+
optimizeDeps: { include, ...(config.optimizeDeps?.entries === undefined ? { entries: ["**/*.html", "!**/*.fragment.html"] } : {}), ...runtimeOptimizer },
|
|
825
813
|
// Vite 6.4 accepts named environments in the config hook. This is
|
|
826
814
|
// emitted only for explicit opt-in; Vite 6/7 legacy users are untouched.
|
|
827
815
|
...(options.experimental?.environments ? {
|
|
@@ -848,11 +836,14 @@ export function fuzorPlugin(options = {}) {
|
|
|
848
836
|
}
|
|
849
837
|
},
|
|
850
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;
|
|
851
845
|
if (!resolveOptions.ssr)
|
|
852
846
|
assertBrowserViewImport(id, importer);
|
|
853
|
-
if (id === "fuzor/server-components/server" && !resolveOptions.ssr) {
|
|
854
|
-
throw new Error("The server-component evaluator is server-only. Import browser runtime and protocol separately.");
|
|
855
|
-
}
|
|
856
847
|
if (id.startsWith("virtual:fuzor/route-client/"))
|
|
857
848
|
return `\0${id}`;
|
|
858
849
|
if (id.startsWith("virtual:fuzor/page-script/"))
|
|
@@ -943,6 +934,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
943
934
|
}
|
|
944
935
|
},
|
|
945
936
|
async load(id, loadOptions) {
|
|
937
|
+
if (id.includes("?fuzor-server-script=") || nativeServerModules.has(id))
|
|
938
|
+
return loadHtmlServer(id, nativeServerModules, file => this.addWatchFile(file));
|
|
946
939
|
if (id.startsWith("\0virtual:fuzor/route-client/")) {
|
|
947
940
|
await loadProject();
|
|
948
941
|
const route = scriptRoutes.find(route => `\0${routeScriptId(route)}` === id);
|
|
@@ -957,6 +950,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
957
950
|
throw new Error("Unknown Fuzor page script");
|
|
958
951
|
this.addWatchFile(script.source);
|
|
959
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");
|
|
960
955
|
const native = compileNativeScript(source, script);
|
|
961
956
|
if (native) {
|
|
962
957
|
for (const handler of native.handlers)
|
|
@@ -972,31 +967,6 @@ export function fuzorPlugin(options = {}) {
|
|
|
972
967
|
throw new Error("Unknown native handler");
|
|
973
968
|
return (await import("vite")).transformWithEsbuild(handler.code, handler.source, { loader: handler.language, target: "es2022" });
|
|
974
969
|
}
|
|
975
|
-
if (id.startsWith("\0fuzor:closure:")) {
|
|
976
|
-
const refs = await references();
|
|
977
|
-
const closure = refs.closures.find(ref => id === `\0fuzor:closure:${ref.id}.ts`);
|
|
978
|
-
if (!closure)
|
|
979
|
-
throw new Error("Unknown resumable closure reference");
|
|
980
|
-
return (await import("vite")).transformWithEsbuild(closure.code, id, { loader: "ts", target: "es2022" }).then(result => result.code);
|
|
981
|
-
}
|
|
982
|
-
if (options.serverComponents?.references) {
|
|
983
|
-
const refs = await references();
|
|
984
|
-
if (id === "\0virtual:fuzor/views") {
|
|
985
|
-
if (loadOptions?.ssr)
|
|
986
|
-
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)])};`;
|
|
987
|
-
return browserReferencesCode(refs, viewBuildId, file => "/" + file);
|
|
988
|
-
}
|
|
989
|
-
if (loadOptions?.ssr) {
|
|
990
|
-
const stub = referenceStub(id.split("?")[0], refs);
|
|
991
|
-
if (stub)
|
|
992
|
-
return stub;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
if (id.startsWith("\0fuzor:fragment:")) {
|
|
996
|
-
const file = id.slice("\0fuzor:fragment:".length, -3);
|
|
997
|
-
this.addWatchFile(file);
|
|
998
|
-
return readFile(file, "utf8").then(html => `export default ${JSON.stringify(compileFragment(html))};`);
|
|
999
|
-
}
|
|
1000
970
|
if (Object.hasOwn(rendererModules, id.slice(1)))
|
|
1001
971
|
return rendererModules[id.slice(1)];
|
|
1002
972
|
const runtimeModule = runtimeModules.get(id);
|
|
@@ -1005,7 +975,7 @@ export function fuzorPlugin(options = {}) {
|
|
|
1005
975
|
if (!loadOptions?.ssr && !nested && vite) {
|
|
1006
976
|
const file = id.split("?")[0];
|
|
1007
977
|
const relative = path.relative(vite.root, file).split(path.sep).join("/");
|
|
1008
|
-
if (
|
|
978
|
+
if (serverModuleFiles.includes(relative) || relative.startsWith("api/") || /\.server\.[cm]?[jt]sx?$/.test(relative)) {
|
|
1009
979
|
throw new Error(`${relative} is server-only and cannot be imported by browser code. Read its data with <fuzor-data> or routeData() instead.`);
|
|
1010
980
|
}
|
|
1011
981
|
}
|
|
@@ -1013,7 +983,32 @@ export function fuzorPlugin(options = {}) {
|
|
|
1013
983
|
return undefined;
|
|
1014
984
|
await loadProject();
|
|
1015
985
|
const mixedClient = profile.mixed && project?.routes.some(route => route.policy?.navigation === "spa");
|
|
1016
|
-
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(",")}} };` : "";
|
|
1017
1012
|
const registry = islands.map((island) => {
|
|
1018
1013
|
const load = island.file.endsWith(".vue")
|
|
1019
1014
|
? `Promise.all([import(${JSON.stringify(island.file)}), import("fuzor/islands/vue")]).then(([component, adapter]) => adapter.vueIsland(component.default)).catch(reloadOnStaleDeployment)`
|
|
@@ -1021,16 +1016,24 @@ export function fuzorPlugin(options = {}) {
|
|
|
1021
1016
|
return `${JSON.stringify(island.name)}: () => ${load}`;
|
|
1022
1017
|
}).join(",\n");
|
|
1023
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(() => {})";
|
|
1024
1024
|
const hasDocs = (project?.routes ?? []).some(route => route.path === "/docs/" || route.path.startsWith("/docs/"));
|
|
1025
1025
|
const routerModule = profile.electron || options.navigation === "embedded" ? "fuzor/runtime/router/embedded" : options.navigation === "navigation-api" ? "fuzor/runtime/router/navigation-api" : "fuzor/runtime";
|
|
1026
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";']),
|
|
1027
1030
|
(target === "spa" || mixedClient)
|
|
1028
1031
|
? `import { createRouter } from "${routerModule}"; import { reloadOnStaleDeployment } from "fuzor/runtime/islands";`
|
|
1029
|
-
:
|
|
1032
|
+
: `import { ${withIslands ? "mountIslands, islandErrorHandler, " : ""}reloadOnStaleDeployment } from "fuzor/runtime/islands";`,
|
|
1030
1033
|
...(target === "mpa" ? ['import { mountPageScripts } from "fuzor/runtime/page-scripts"; import { stripBase } from "fuzor/runtime";'] : []),
|
|
1031
|
-
...(
|
|
1032
|
-
...(
|
|
1033
|
-
...(
|
|
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";'] : []),
|
|
1034
1037
|
...(hasDocs ? ['import { mountDocsUi } from "fuzor/runtime/docs-ui";'] : [])
|
|
1035
1038
|
];
|
|
1036
1039
|
const docsInit = hasDocs ? `let stopDocsUi;
|
|
@@ -1040,17 +1043,16 @@ const disposeDocs = () => {
|
|
|
1040
1043
|
if (docsDisposed) return;
|
|
1041
1044
|
docsDisposed = true;
|
|
1042
1045
|
window.removeEventListener("pagehide", hideDocs);
|
|
1043
|
-
stopDocsUi?.();
|
|
1046
|
+
void stopDocsUi?.().catch(error => console.error(error));
|
|
1044
1047
|
};
|
|
1045
1048
|
void mountDocsUi({ document, base: import.meta.env.BASE_URL }).then(stop => {
|
|
1046
1049
|
stopDocsUi = stop;
|
|
1047
|
-
if (docsDisposed) stop();
|
|
1050
|
+
if (docsDisposed) void stop().catch(error => console.error(error));
|
|
1048
1051
|
}, error => console.error(error));
|
|
1049
1052
|
window.addEventListener("pagehide", hideDocs);
|
|
1050
1053
|
if (import.meta.hot) import.meta.hot.dispose(disposeDocs);
|
|
1051
1054
|
` : "";
|
|
1052
|
-
const
|
|
1053
|
-
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;
|
|
1054
1056
|
export const ready = router ? router.start() : Promise.resolve(undefined);
|
|
1055
1057
|
void ready.catch(error => console.error(error));
|
|
1056
1058
|
if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });`;
|
|
@@ -1059,13 +1061,11 @@ if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });
|
|
|
1059
1061
|
const lifetime = new AbortController();
|
|
1060
1062
|
let cleanup;
|
|
1061
1063
|
const dispose = () => { lifetime.abort(); void cleanup?.(); };
|
|
1062
|
-
|
|
1063
|
-
const mountComponents = viewRenderer ? viewRenderer.resume().then(() => () => viewRenderer.dispose()) : mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });` : `const mountComponents = mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal });`}
|
|
1064
|
-
${options.serverComponents ? "const viewRendererActive = !!viewRenderer;" : "const viewRendererActive = false;"}
|
|
1064
|
+
const mountComponents = ${mountIslandsCode};
|
|
1065
1065
|
const pathname = stripBase(location.pathname, import.meta.env.BASE_URL) ?? location.pathname;
|
|
1066
1066
|
const routePath = pathname.endsWith("/") ? pathname : pathname + "/";
|
|
1067
1067
|
const scriptRoute = Object.hasOwn(scripts, routePath) ? routePath : outlet.getAttribute("data-fuzor-pattern") ?? "";
|
|
1068
|
-
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; } })();
|
|
1069
1069
|
void mount.then(stop => {
|
|
1070
1070
|
cleanup = stop;
|
|
1071
1071
|
if (lifetime.signal.aborted) void stop();
|
|
@@ -1073,7 +1073,10 @@ void mount.then(stop => {
|
|
|
1073
1073
|
window.addEventListener("pagehide", event => { if (!event.persisted) dispose(); });
|
|
1074
1074
|
if (import.meta.hot) import.meta.hot.dispose(dispose);`;
|
|
1075
1075
|
return `${imports.join("\n")}
|
|
1076
|
+
${options.ui?.brand === undefined ? "" : `defineBrand(${JSON.stringify(options.ui.brand)});`}
|
|
1076
1077
|
const islands = { ${registry} };
|
|
1078
|
+
${nativeLoaders}
|
|
1079
|
+
${nativeData ? `enableNativeStreams(${JSON.stringify(viewBuildId)});` : ""}
|
|
1077
1080
|
const scripts = { ${scriptRegistry} };
|
|
1078
1081
|
const outlet = document.querySelector("fuzor-router");
|
|
1079
1082
|
if (!outlet) throw new Error("Fuzor outlet is missing");
|
|
@@ -1090,6 +1093,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1090
1093
|
const pathname = stripBase(requested, pathBase) ?? requested;
|
|
1091
1094
|
const currentPath = findRoute(compiled, pathname) ? pathname : "/";
|
|
1092
1095
|
parseRoutePolicy(raw, path.join(vite.root, "index.html"), false);
|
|
1096
|
+
assertNoServerScripts(raw, path.join(vite.root, "index.html"));
|
|
1093
1097
|
const shell = compilePlatformBlocks(raw, profile.platform, path.join(vite.root, "index.html"));
|
|
1094
1098
|
shellHead = readDocumentHead(shell);
|
|
1095
1099
|
let html = applyDocumentMetadata(replaceOutlet(shell, outletFor(compiled, target, currentPath, fallbacks(), profile.mixed)), pageHead(shellHead, findRoute(compiled, currentPath)?.metadata ?? {}));
|
|
@@ -1161,10 +1165,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1161
1165
|
const compiled = await loadProject();
|
|
1162
1166
|
const manifest = serverManifest(compiled, target, template, pathBase, fallbacks(), withStaticModuleImports, options.clientRoutes, `/${vite.build.assetsDir}/`, profile.mixed);
|
|
1163
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;
|
|
1164
|
-
|
|
1165
|
-
if (views && JSON.stringify(Object.keys(views.routes).sort()) !== JSON.stringify([...options.serverComponents.routes].sort()))
|
|
1166
|
-
throw new Error("Server view routes must match serverComponents.routes.");
|
|
1167
|
-
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 }));
|
|
1168
1169
|
}
|
|
1169
1170
|
await writeWebResponse(response, result, { method: request.method ?? "GET", signal: controller.signal });
|
|
1170
1171
|
}
|
|
@@ -1205,7 +1206,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1205
1206
|
}
|
|
1206
1207
|
});
|
|
1207
1208
|
server.watcher.on("add", (file) => {
|
|
1208
|
-
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))) {
|
|
1209
1210
|
void refresh(server).catch((error) => server.config.logger.error(String(error)));
|
|
1210
1211
|
}
|
|
1211
1212
|
});
|
|
@@ -1318,8 +1319,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1318
1319
|
await mkdir(serverDir, { recursive: true });
|
|
1319
1320
|
await writeFile(path.join(serverDir, "fuzor.manifest.json"), JSON.stringify({ ...artifact, clientDirectory: path.relative(serverDir, outDir).split(path.sep).join("/") }, null, 2) + "\n");
|
|
1320
1321
|
const dataRoutes = compiled.routes.filter(route => route.serverData);
|
|
1321
|
-
|
|
1322
|
-
if (!dataRoutes.length && !apiFiles.length && !options.serverComponents) {
|
|
1322
|
+
if (!dataRoutes.length && !apiFiles.length && !options.serverExternal) {
|
|
1323
1323
|
await writeFile(path.join(serverDir, "entry.mjs"), `import { createServerHandler } from "fuzor/server";\n` +
|
|
1324
1324
|
`export const manifest = ${JSON.stringify(manifest)};\n` +
|
|
1325
1325
|
`export const handler = createServerHandler(manifest);\n`, "utf8");
|
|
@@ -1329,8 +1329,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1329
1329
|
const files = [...new Set(dataRoutes.map(route => route.serverData))];
|
|
1330
1330
|
const entryId = "virtual:fuzor/server-entry";
|
|
1331
1331
|
const code = [
|
|
1332
|
-
`import { createServerHandler, isServerData } from "fuzor/server";`,
|
|
1333
|
-
...(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";`,
|
|
1334
1333
|
...files.map((file, index) => `import data${index} from ${JSON.stringify(file)};`),
|
|
1335
1334
|
...apiFiles.map((route, index) => `import * as api${index} from ${JSON.stringify(route.file)};`),
|
|
1336
1335
|
`const modules = [${files.map((_, index) => `data${index}`).join(", ")}];`,
|
|
@@ -1339,13 +1338,15 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1339
1338
|
`export const manifest = ${JSON.stringify(manifest)};`,
|
|
1340
1339
|
`const routes = ${JSON.stringify(dataRoutes.map(route => [route.path, files.indexOf(route.serverData), route.params ?? null]))};`,
|
|
1341
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(", ")}];`,
|
|
1342
|
-
`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" : ""} });`
|
|
1343
1342
|
].join("\n");
|
|
1344
1343
|
await nestedServer.run(true, () => viteBuild({
|
|
1345
1344
|
root: vite.root, mode: vite.mode, configFile: vite.configFile ?? false, logLevel: "error", publicDir: false,
|
|
1346
|
-
// One runtime copy:
|
|
1347
|
-
|
|
1348
|
-
|
|
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 }],
|
|
1349
1350
|
build: { ssr: true, outDir: serverDir, emptyOutDir: false, copyPublicDir: false, minify: false,
|
|
1350
1351
|
rollupOptions: { input: entryId, output: { entryFileNames: "entry.mjs", chunkFileNames: "chunks/[name]-[hash].mjs", format: "es" } } }
|
|
1351
1352
|
}));
|
|
@@ -1353,7 +1354,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1353
1354
|
// The same finished documents are also written to client/ below, so it can be served by a CDN or
|
|
1354
1355
|
// static host with only /api and server data requests reaching the server.
|
|
1355
1356
|
if (profile.adapter === "cloudflare")
|
|
1356
|
-
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("/"));
|
|
1357
1358
|
}
|
|
1358
1359
|
const index = searchIndex(compiled, options.searchIndex);
|
|
1359
1360
|
if (index)
|
|
@@ -1403,7 +1404,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1403
1404
|
if (nested)
|
|
1404
1405
|
return;
|
|
1405
1406
|
const file = context.file.replaceAll("\\", "/");
|
|
1406
|
-
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))) {
|
|
1407
1408
|
const environment = context.environment?.name;
|
|
1408
1409
|
if (environment)
|
|
1409
1410
|
invalidateEnvironmentModule(context.server, resolvedVirtualId, environment, environmentApiEnabled);
|