fuzor 0.5.1 → 0.5.3
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 +20 -1
- package/dist/compiler/data-document.d.ts.map +1 -1
- package/dist/compiler/data-document.js +80 -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 +569 -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/select-options.d.ts +7 -0
- package/dist/compiler/select-options.d.ts.map +1 -0
- package/dist/compiler/select-options.js +22 -0
- package/dist/compiler/select-options.js.map +1 -0
- 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 +18 -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/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.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/route-views.d.ts +15 -0
- package/dist/runtime/route-views.d.ts.map +1 -0
- package/dist/runtime/route-views.js +133 -0
- package/dist/runtime/route-views.js.map +1 -0
- package/dist/runtime/router-embedded.d.ts +1 -0
- package/dist/runtime/router-embedded.d.ts.map +1 -1
- package/dist/runtime/router-navigation-api.d.ts +1 -0
- package/dist/runtime/router-navigation-api.d.ts.map +1 -1
- package/dist/runtime/router.d.ts +1 -0
- package/dist/runtime/router.d.ts.map +1 -1
- package/dist/runtime/router.js +11 -2
- 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-document.d.ts +21 -0
- package/dist/server-document.d.ts.map +1 -0
- package/dist/server-document.js +3 -0
- package/dist/server-document.js.map +1 -0
- package/dist/server-native-document.d.ts +13 -0
- package/dist/server-native-document.d.ts.map +1 -0
- package/dist/server-native-document.js +154 -0
- package/dist/server-native-document.js.map +1 -0
- 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 +70 -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 +11 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +68 -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 +119 -116
- 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
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { inlineDocumentAssets } from "./compiler/inline-assets.js";
|
|
2
|
-
import { compileDataDocument } from './compiler/data-document.js';
|
|
2
|
+
import { compileDataDocument, compileInitialDocument } 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";',
|
|
@@ -152,7 +154,12 @@ function serverManifest(project, target, template, base, fallbacks, finish = htm
|
|
|
152
154
|
? { document: finish(template), routeDocuments: Object.fromEntries(project.routes.map(route => [route.path, finish(applyDocumentMetadata(replaceOutlet(template, outletFor(project, "spa", route.path, fallbacks)), pageHead(fallbacks.head, route.metadata ?? {})))])), routes: Object.fromEntries(project.routes.map((route) => [route.path, true])), ...(clientRoutes ? { clientRoutes, ...(assetDirectory ? { assetDirectory } : {}) } : {}), ...shared }
|
|
153
155
|
: { documents: Object.fromEntries(project.routes.map((route) => [route.path, finish(applyDocumentMetadata(replaceOutlet(template, outletFor(project, "mpa", route.path, fallbacks, mixed)), pageHead(fallbacks.head, route.metadata ?? {})))])), ...shared };
|
|
154
156
|
const documents = "documents" in manifest ? manifest.documents : manifest.routeDocuments ?? {};
|
|
155
|
-
|
|
157
|
+
const requestDocuments = project.routes.filter(route => route.policy?.rendering === "request");
|
|
158
|
+
return { ...manifest, dataBindings: Object.fromEntries(requestDocuments.map(route => [route.path, compileDataDocument(documents[route.path])])),
|
|
159
|
+
initialDocuments: Object.fromEntries(requestDocuments.flatMap(route => {
|
|
160
|
+
const program = compileInitialDocument(documents[route.path]);
|
|
161
|
+
return program ? [[route.path, program]] : [];
|
|
162
|
+
})) };
|
|
156
163
|
}
|
|
157
164
|
/** Loads each page's `server.ts` default export and checks it was declared with `defineServerData`. */
|
|
158
165
|
async function loadRouteDataModules(project, load, root) {
|
|
@@ -287,13 +294,6 @@ function replaceOutlet(document, content) {
|
|
|
287
294
|
throw new Error(`index.html must contain ${outletStart} and ${outletEnd}.`);
|
|
288
295
|
return document.slice(0, start + outletStart.length) + content + document.slice(end);
|
|
289
296
|
}
|
|
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
297
|
/**
|
|
298
298
|
* The plugin creates private Vite servers to load island and `paths` modules. Their config file loads
|
|
299
299
|
* this plugin again; those nested instances only provide virtual modules and never compile the project.
|
|
@@ -304,17 +304,19 @@ const nestedServer = new AsyncLocalStorage();
|
|
|
304
304
|
* open an HMR WebSocket (two would compete for its port, and a failed one never closes) and must not share the
|
|
305
305
|
* dev server's dependency cache (each would re-optimize and invalidate the other's pre-bundled files).
|
|
306
306
|
*/
|
|
307
|
-
export function nestedServerConfig(config) {
|
|
307
|
+
export function nestedServerConfig(config, nativeModules) {
|
|
308
308
|
return {
|
|
309
309
|
root: config.root,
|
|
310
310
|
configFile: config.configFile ?? false,
|
|
311
|
-
plugins: [workflowPlugin(), {
|
|
311
|
+
plugins: [{ name: "fuzor:internal-specifiers", enforce: "pre", resolveId: id => resolveInternalSpecifier(id) }, htmlServerPlugin(nativeModules), workflowPlugin(), {
|
|
312
312
|
name: "fuzor:nested-renderers",
|
|
313
313
|
resolveId(id) { if (Object.hasOwn(rendererModules, id))
|
|
314
314
|
return `\0${id}`; },
|
|
315
315
|
load(id) { if (id.startsWith("\0") && Object.hasOwn(rendererModules, id.slice(1)))
|
|
316
316
|
return rendererModules[id.slice(1)]; }
|
|
317
317
|
}],
|
|
318
|
+
// Fuzor's server runtime is one module graph: compiler-owned modules and `fuzor/server` must not load twice.
|
|
319
|
+
ssr: { noExternal: ["fuzor"] },
|
|
318
320
|
server: { middlewareMode: true, hmr: false, ws: false },
|
|
319
321
|
cacheDir: path.join(config.cacheDir, "fuzor-nested"),
|
|
320
322
|
optimizeDeps: { noDiscovery: true, include: [] },
|
|
@@ -380,20 +382,6 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
|
|
|
380
382
|
const electron = options.electron ?? envElectron ?? requestedTarget === "electron";
|
|
381
383
|
const target = (requestedTarget === "electron" ? "spa" : requestedTarget);
|
|
382
384
|
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
385
|
const inline = electron || packaged ? true : options.inline ?? envInline ?? false;
|
|
398
386
|
if (inline && target !== "spa")
|
|
399
387
|
throw new Error("Inline output is only supported for the SPA target.");
|
|
@@ -418,27 +406,11 @@ export function resolveFuzorProfile(options = {}, env = process.env, serverModul
|
|
|
418
406
|
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
407
|
}
|
|
420
408
|
export function fuzorPlugin(options = {}) {
|
|
409
|
+
if (options.ui?.brand !== undefined)
|
|
410
|
+
validateBrandPrefix(options.ui.brand);
|
|
421
411
|
const nested = nestedServer.getStore() === true;
|
|
412
|
+
const nativeServerModules = new Map();
|
|
422
413
|
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
414
|
const configuredEnvironments = normalizeEnvironmentConfiguration(options.experimental?.environments);
|
|
443
415
|
const environmentNames = {
|
|
444
416
|
client: configuredEnvironments.client ?? "client",
|
|
@@ -481,6 +453,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
481
453
|
let project;
|
|
482
454
|
let pageScripts = [];
|
|
483
455
|
const nativeHandlers = new Map();
|
|
456
|
+
const authoredNative = new Map();
|
|
457
|
+
let nativeAdmissions = new Map();
|
|
484
458
|
let inlineModuleFiles = [];
|
|
485
459
|
let scriptRoutes = [];
|
|
486
460
|
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 +477,7 @@ export function fuzorPlugin(options = {}) {
|
|
|
503
477
|
let solidHydrationScript = "";
|
|
504
478
|
const pending = (async () => {
|
|
505
479
|
let owned;
|
|
506
|
-
const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite)));
|
|
480
|
+
const createModuleServer = () => nestedServer.run(true, () => createServer(nestedServerConfig(vite, nativeServerModules)));
|
|
507
481
|
const moduleServer = () => devServer ? Promise.resolve(devServer) : owned ??= createModuleServer();
|
|
508
482
|
// client:none analysis reads client transforms without HMR code, which the dev server would add. It has its
|
|
509
483
|
// own server: client transforms wait for pending requests to settle, and preview renders on the module
|
|
@@ -672,6 +646,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
672
646
|
const compileRaw = async (signal, loadModule) => {
|
|
673
647
|
let compiled = await compileProject(vite.root, { ...(options.markdown ? { markdown: options.markdown } : {}), ...(options.redirects ? { redirects: options.redirects } : {}), platform: profile.platform, signal, loadModule, cache: compileCache })
|
|
674
648
|
.catch(async (error) => { throw await withViteLocation(error); });
|
|
649
|
+
nativeServerModules.clear();
|
|
650
|
+
compiled = await prepareNativeScripts(compiled, vite.root, loadModule, nativeServerModules);
|
|
675
651
|
const entry = options.entry ?? process.env.FUZOR_ENTRY;
|
|
676
652
|
if (entry) {
|
|
677
653
|
if (!inline || target !== "spa")
|
|
@@ -688,15 +664,20 @@ export function fuzorPlugin(options = {}) {
|
|
|
688
664
|
if (!compiled.routes.some(route => route.path === "/"))
|
|
689
665
|
throw new Error("Application entry must have a static root template; request-rendered routes require an HTTP rendering deployment.");
|
|
690
666
|
}
|
|
667
|
+
const inferredServer = compiled.routes.filter(route => route.serverData || route.policy?.rendering === "request");
|
|
668
|
+
if (inferredServer.length) {
|
|
669
|
+
profile = resolveFuzorProfile(options, process.env, [...new Set([...serverModuleFiles, ...inferredServer.map(route => path.relative(vite.root, route.source).split(path.sep).join("/"))])]);
|
|
670
|
+
withServer = profile.server;
|
|
671
|
+
}
|
|
691
672
|
const deployment = options.deployment ?? process.env.FUZOR_DEPLOYMENT;
|
|
692
673
|
const requestRoutes = compiled.routes.filter(route => route.policy?.rendering === "request");
|
|
693
674
|
if (deployment && !["static", "github-pages", "node", "cloudflare"].includes(deployment))
|
|
694
675
|
throw new Error("Unknown Fuzor deployment target: " + deployment);
|
|
695
|
-
if ((deployment === "static" || deployment === "github-pages") &&
|
|
696
|
-
throw new Error(`Route ${
|
|
676
|
+
if ((deployment === "static" || deployment === "github-pages") && requestRoutes.length)
|
|
677
|
+
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
678
|
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
|
|
679
|
+
if (route.requestPattern && route.policy?.navigation === "spa" && !route.serverData?.startsWith("virtual:fuzor/native-data/"))
|
|
680
|
+
throw new Error(`${route.source}: unenumerated request routes inside an SPA need an HTML server component. Add one or use navigation=document.`);
|
|
700
681
|
if (options.routeTree) {
|
|
701
682
|
if (target !== "spa" && !profile.mixed)
|
|
702
683
|
throw new Error("routeTree requires a SPA target.");
|
|
@@ -716,8 +697,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
716
697
|
check(options.routeTree);
|
|
717
698
|
}
|
|
718
699
|
if (options.clientRoutes && Object.keys(options.clientRoutes).length) {
|
|
719
|
-
if (target !== "spa"
|
|
720
|
-
throw new Error("clientRoutes requires
|
|
700
|
+
if (target !== "spa")
|
|
701
|
+
throw new Error("clientRoutes requires an SPA target.");
|
|
721
702
|
makeClientRouteMatcher(options.clientRoutes);
|
|
722
703
|
for (const target of Object.values(options.clientRoutes)) {
|
|
723
704
|
const route = compiled.routes.find(route => route.path === routeKey(target));
|
|
@@ -735,7 +716,18 @@ export function fuzorPlugin(options = {}) {
|
|
|
735
716
|
const extracted = extractPageScripts(compiled, vite.root);
|
|
736
717
|
pageScripts = extracted.scripts;
|
|
737
718
|
scriptRoutes = extracted.routes;
|
|
738
|
-
|
|
719
|
+
authoredNative.clear();
|
|
720
|
+
nativeHandlers.clear();
|
|
721
|
+
for (const script of pageScripts)
|
|
722
|
+
if (script.native) {
|
|
723
|
+
const compiled = compileAuthoredNativeScript(script.code ?? await readFile(script.source, "utf8"), script, viewBuildId);
|
|
724
|
+
authoredNative.set(script.id, compiled);
|
|
725
|
+
for (const handler of compiled.handlers)
|
|
726
|
+
nativeHandlers.set(handler.id, handler);
|
|
727
|
+
}
|
|
728
|
+
const emitted = emitNativeManifests(extracted.project, scriptRoutes, pageScripts, authoredNative, viewBuildId);
|
|
729
|
+
nativeAdmissions = emitted.admissions;
|
|
730
|
+
return emitted.project;
|
|
739
731
|
};
|
|
740
732
|
const scanIslands = async () => {
|
|
741
733
|
return Effect.runPromise(scanIslandModulesEffect(vite.root, Object.keys(options.runtimes ?? {})));
|
|
@@ -743,7 +735,6 @@ export function fuzorPlugin(options = {}) {
|
|
|
743
735
|
const refresh = async (server) => {
|
|
744
736
|
const version = ++revision;
|
|
745
737
|
viewBuildId = randomUUID();
|
|
746
|
-
referencesPromise = undefined;
|
|
747
738
|
compilation?.abort();
|
|
748
739
|
project = undefined;
|
|
749
740
|
projectPromise = undefined;
|
|
@@ -810,8 +801,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
810
801
|
profile = resolveFuzorProfile(options, process.env, serverModuleFiles);
|
|
811
802
|
({ target, inline } = profile);
|
|
812
803
|
withServer = profile.server;
|
|
813
|
-
if (options.clientRoutes && Object.keys(options.clientRoutes).length &&
|
|
814
|
-
throw new Error("clientRoutes requires
|
|
804
|
+
if (options.clientRoutes && Object.keys(options.clientRoutes).length && target !== "spa")
|
|
805
|
+
throw new Error("clientRoutes requires an SPA target.");
|
|
815
806
|
const packageJson = await readFile(path.join(path.resolve(config.root ?? process.cwd()), "package.json"), "utf8").then(text => JSON.parse(text), () => ({}));
|
|
816
807
|
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
817
808
|
const include = ["Cause", "Context", "Deferred", "Effect", "Exit", "Fiber", "Layer", "Schema", "Scope", "reactivity/Atom", "reactivity/AtomRegistry"].map(module => `fuzor > effect/${module}`);
|
|
@@ -850,11 +841,14 @@ export function fuzorPlugin(options = {}) {
|
|
|
850
841
|
}
|
|
851
842
|
},
|
|
852
843
|
async resolveId(id, importer, resolveOptions) {
|
|
844
|
+
const internalFile = resolveInternalSpecifier(id);
|
|
845
|
+
if (internalFile)
|
|
846
|
+
return internalFile;
|
|
847
|
+
const serverModule = resolveHtmlServer(id, resolveOptions.ssr, nativeServerModules);
|
|
848
|
+
if (serverModule)
|
|
849
|
+
return serverModule;
|
|
853
850
|
if (!resolveOptions.ssr)
|
|
854
851
|
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
852
|
if (id.startsWith("virtual:fuzor/route-client/"))
|
|
859
853
|
return `\0${id}`;
|
|
860
854
|
if (id.startsWith("virtual:fuzor/page-script/"))
|
|
@@ -945,6 +939,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
945
939
|
}
|
|
946
940
|
},
|
|
947
941
|
async load(id, loadOptions) {
|
|
942
|
+
if (id.includes("?fuzor-server-script=") || nativeServerModules.has(id))
|
|
943
|
+
return loadHtmlServer(id, nativeServerModules, file => this.addWatchFile(file));
|
|
948
944
|
if (id.startsWith("\0virtual:fuzor/route-client/")) {
|
|
949
945
|
await loadProject();
|
|
950
946
|
const route = scriptRoutes.find(route => `\0${routeScriptId(route)}` === id);
|
|
@@ -959,6 +955,8 @@ export function fuzorPlugin(options = {}) {
|
|
|
959
955
|
throw new Error("Unknown Fuzor page script");
|
|
960
956
|
this.addWatchFile(script.source);
|
|
961
957
|
const source = script.code ?? await readFile(script.source, "utf8");
|
|
958
|
+
if (script.native)
|
|
959
|
+
throw new Error("Authored native declarations are adopted from HTML, not executable descriptor modules");
|
|
962
960
|
const native = compileNativeScript(source, script);
|
|
963
961
|
if (native) {
|
|
964
962
|
for (const handler of native.handlers)
|
|
@@ -974,31 +972,6 @@ export function fuzorPlugin(options = {}) {
|
|
|
974
972
|
throw new Error("Unknown native handler");
|
|
975
973
|
return (await import("vite")).transformWithEsbuild(handler.code, handler.source, { loader: handler.language, target: "es2022" });
|
|
976
974
|
}
|
|
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
975
|
if (Object.hasOwn(rendererModules, id.slice(1)))
|
|
1003
976
|
return rendererModules[id.slice(1)];
|
|
1004
977
|
const runtimeModule = runtimeModules.get(id);
|
|
@@ -1007,7 +980,7 @@ export function fuzorPlugin(options = {}) {
|
|
|
1007
980
|
if (!loadOptions?.ssr && !nested && vite) {
|
|
1008
981
|
const file = id.split("?")[0];
|
|
1009
982
|
const relative = path.relative(vite.root, file).split(path.sep).join("/");
|
|
1010
|
-
if (
|
|
983
|
+
if (serverModuleFiles.includes(relative) || relative.startsWith("api/") || /\.server\.[cm]?[jt]sx?$/.test(relative)) {
|
|
1011
984
|
throw new Error(`${relative} is server-only and cannot be imported by browser code. Read its data with <fuzor-data> or routeData() instead.`);
|
|
1012
985
|
}
|
|
1013
986
|
}
|
|
@@ -1015,7 +988,32 @@ export function fuzorPlugin(options = {}) {
|
|
|
1015
988
|
return undefined;
|
|
1016
989
|
await loadProject();
|
|
1017
990
|
const mixedClient = profile.mixed && project?.routes.some(route => route.policy?.navigation === "spa");
|
|
1018
|
-
const scriptRegistry = scriptRoutes.map(route =>
|
|
991
|
+
const scriptRegistry = scriptRoutes.map(route => nativeAdmissions.has(route.path)
|
|
992
|
+
? `${JSON.stringify(route.path)}: async () => readNativeManifest(outlet, ${JSON.stringify(route.path)}, ${JSON.stringify(viewBuildId)}, ${JSON.stringify(nativeAdmissions.get(route.path))}, nativeLoaders)`
|
|
993
|
+
: `${JSON.stringify(route.path)}: () => import(${JSON.stringify(routeScriptId(route))}).catch(reloadOnStaleDeployment)`).join(",\n");
|
|
994
|
+
const handlerLoaders = new Map();
|
|
995
|
+
const graphLoaders = new Map();
|
|
996
|
+
const cssImports = new Set();
|
|
997
|
+
for (const script of pageScripts) {
|
|
998
|
+
const compiled = authoredNative.get(script.id);
|
|
999
|
+
if (!compiled)
|
|
1000
|
+
continue;
|
|
1001
|
+
this.addWatchFile(script.source);
|
|
1002
|
+
for (const css of compiled.css) {
|
|
1003
|
+
const resolved = await this.resolve(css, script.source);
|
|
1004
|
+
if (!resolved)
|
|
1005
|
+
throw new Error(`${script.source}: cannot resolve native stylesheet ${css}`);
|
|
1006
|
+
cssImports.add(`import ${JSON.stringify(resolved.id)};`);
|
|
1007
|
+
}
|
|
1008
|
+
for (const [name, id] of compiled.actionIds)
|
|
1009
|
+
if ((script.native.events.some(event => event.action === name) || script.native.effects?.some(effect => effect.action === name)))
|
|
1010
|
+
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)}`);
|
|
1011
|
+
if (compiled.graphId)
|
|
1012
|
+
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(",")}]))`);
|
|
1013
|
+
}
|
|
1014
|
+
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)));
|
|
1015
|
+
const nativeData = pageScripts.some(script => script.native?.request);
|
|
1016
|
+
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
1017
|
const registry = islands.map((island) => {
|
|
1020
1018
|
const load = island.file.endsWith(".vue")
|
|
1021
1019
|
? `Promise.all([import(${JSON.stringify(island.file)}), import("fuzor/islands/vue")]).then(([component, adapter]) => adapter.vueIsland(component.default)).catch(reloadOnStaleDeployment)`
|
|
@@ -1023,16 +1021,24 @@ export function fuzorPlugin(options = {}) {
|
|
|
1023
1021
|
return `${JSON.stringify(island.name)}: () => ${load}`;
|
|
1024
1022
|
}).join(",\n");
|
|
1025
1023
|
const withData = target === "mpa" && !!project?.routes.some(route => route.serverData);
|
|
1024
|
+
// Keep every discovered module, including ones introduced by page scripts. An empty static
|
|
1025
|
+
// registry cannot activate an island; retain diagnostics for authored hosts and open server/dev paths.
|
|
1026
|
+
const withIslands = islands.length > 0 || withServer || vite.command === "serve"
|
|
1027
|
+
|| [...(project?.routes ?? []), ...(project?.boundaries ?? [])].some(route => /<fuzor-island\b/i.test(route.html));
|
|
1028
|
+
const mountIslandsCode = withIslands ? "mountIslands(outlet, islands, islandErrorHandler, { signal: lifetime.signal })" : "Promise.resolve(() => {})";
|
|
1026
1029
|
const hasDocs = (project?.routes ?? []).some(route => route.path === "/docs/" || route.path.startsWith("/docs/"));
|
|
1027
1030
|
const routerModule = profile.electron || options.navigation === "embedded" ? "fuzor/runtime/router/embedded" : options.navigation === "navigation-api" ? "fuzor/runtime/router/navigation-api" : "fuzor/runtime";
|
|
1028
1031
|
const imports = [
|
|
1032
|
+
...cssImports,
|
|
1033
|
+
...(nativeAdmissions.size ? ['import { readNativeManifest } from "fuzor/runtime/native-manifest";'] : []),
|
|
1034
|
+
...(options.ui?.brand === undefined ? [] : ['import { defineBrand } from "fuzor/ui/core";']),
|
|
1029
1035
|
(target === "spa" || mixedClient)
|
|
1030
1036
|
? `import { createRouter } from "${routerModule}"; import { reloadOnStaleDeployment } from "fuzor/runtime/islands";`
|
|
1031
|
-
:
|
|
1037
|
+
: `import { ${withIslands ? "mountIslands, islandErrorHandler, " : ""}reloadOnStaleDeployment } from "fuzor/runtime/islands";`,
|
|
1032
1038
|
...(target === "mpa" ? ['import { mountPageScripts } from "fuzor/runtime/page-scripts"; import { stripBase } from "fuzor/runtime";'] : []),
|
|
1033
|
-
...(
|
|
1034
|
-
...(
|
|
1035
|
-
...(
|
|
1039
|
+
...(mixedClient && withIslands ? ['import { mountIslands, islandErrorHandler } from "fuzor/runtime/islands";'] : []),
|
|
1040
|
+
...(withData || nativeData ? [`import { ${[withData ? "loadRouteData" : "", nativeData ? "routeData" : ""].filter(Boolean).join(",")} } from "fuzor/runtime/data";`] : []),
|
|
1041
|
+
...(nativeData ? ['import { enableNativeStreams } from "fuzor/runtime/native-stream";'] : []),
|
|
1036
1042
|
...(hasDocs ? ['import { mountDocsUi } from "fuzor/runtime/docs-ui";'] : [])
|
|
1037
1043
|
];
|
|
1038
1044
|
const docsInit = hasDocs ? `let stopDocsUi;
|
|
@@ -1042,17 +1048,16 @@ const disposeDocs = () => {
|
|
|
1042
1048
|
if (docsDisposed) return;
|
|
1043
1049
|
docsDisposed = true;
|
|
1044
1050
|
window.removeEventListener("pagehide", hideDocs);
|
|
1045
|
-
stopDocsUi?.();
|
|
1051
|
+
void stopDocsUi?.().catch(error => console.error(error));
|
|
1046
1052
|
};
|
|
1047
1053
|
void mountDocsUi({ document, base: import.meta.env.BASE_URL }).then(stop => {
|
|
1048
1054
|
stopDocsUi = stop;
|
|
1049
|
-
if (docsDisposed) stop();
|
|
1055
|
+
if (docsDisposed) void stop().catch(error => console.error(error));
|
|
1050
1056
|
}, error => console.error(error));
|
|
1051
1057
|
window.addEventListener("pagehide", hideDocs);
|
|
1052
1058
|
if (import.meta.hot) import.meta.hot.dispose(disposeDocs);
|
|
1053
1059
|
` : "";
|
|
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;
|
|
1060
|
+
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
1061
|
export const ready = router ? router.start() : Promise.resolve(undefined);
|
|
1057
1062
|
void ready.catch(error => console.error(error));
|
|
1058
1063
|
if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });`;
|
|
@@ -1061,13 +1066,11 @@ if (import.meta.hot) import.meta.hot.dispose(() => { void router?.dispose(); });
|
|
|
1061
1066
|
const lifetime = new AbortController();
|
|
1062
1067
|
let cleanup;
|
|
1063
1068
|
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;"}
|
|
1069
|
+
const mountComponents = ${mountIslandsCode};
|
|
1067
1070
|
const pathname = stripBase(location.pathname, import.meta.env.BASE_URL) ?? location.pathname;
|
|
1068
1071
|
const routePath = pathname.endsWith("/") ? pathname : pathname + "/";
|
|
1069
1072
|
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,
|
|
1073
|
+
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
1074
|
void mount.then(stop => {
|
|
1072
1075
|
cleanup = stop;
|
|
1073
1076
|
if (lifetime.signal.aborted) void stop();
|
|
@@ -1075,7 +1078,10 @@ void mount.then(stop => {
|
|
|
1075
1078
|
window.addEventListener("pagehide", event => { if (!event.persisted) dispose(); });
|
|
1076
1079
|
if (import.meta.hot) import.meta.hot.dispose(dispose);`;
|
|
1077
1080
|
return `${imports.join("\n")}
|
|
1081
|
+
${options.ui?.brand === undefined ? "" : `defineBrand(${JSON.stringify(options.ui.brand)});`}
|
|
1078
1082
|
const islands = { ${registry} };
|
|
1083
|
+
${nativeLoaders}
|
|
1084
|
+
${nativeData ? `enableNativeStreams(${JSON.stringify(viewBuildId)});` : ""}
|
|
1079
1085
|
const scripts = { ${scriptRegistry} };
|
|
1080
1086
|
const outlet = document.querySelector("fuzor-router");
|
|
1081
1087
|
if (!outlet) throw new Error("Fuzor outlet is missing");
|
|
@@ -1092,6 +1098,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1092
1098
|
const pathname = stripBase(requested, pathBase) ?? requested;
|
|
1093
1099
|
const currentPath = findRoute(compiled, pathname) ? pathname : "/";
|
|
1094
1100
|
parseRoutePolicy(raw, path.join(vite.root, "index.html"), false);
|
|
1101
|
+
assertNoServerScripts(raw, path.join(vite.root, "index.html"));
|
|
1095
1102
|
const shell = compilePlatformBlocks(raw, profile.platform, path.join(vite.root, "index.html"));
|
|
1096
1103
|
shellHead = readDocumentHead(shell);
|
|
1097
1104
|
let html = applyDocumentMetadata(replaceOutlet(shell, outletFor(compiled, target, currentPath, fallbacks(), profile.mixed)), pageHead(shellHead, findRoute(compiled, currentPath)?.metadata ?? {}));
|
|
@@ -1163,10 +1170,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1163
1170
|
const compiled = await loadProject();
|
|
1164
1171
|
const manifest = serverManifest(compiled, target, template, pathBase, fallbacks(), withStaticModuleImports, options.clientRoutes, `/${vite.build.assetsDir}/`, profile.mixed);
|
|
1165
1172
|
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 }));
|
|
1173
|
+
result = await createServerHandler({ ...manifest, buildId: viewBuildId }, { renderBinding: renderNativeBinding, ...(data ? { data } : {}), onError })(toWebRequest(request, { url, signal: controller.signal }));
|
|
1170
1174
|
}
|
|
1171
1175
|
await writeWebResponse(response, result, { method: request.method ?? "GET", signal: controller.signal });
|
|
1172
1176
|
}
|
|
@@ -1207,7 +1211,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1207
1211
|
}
|
|
1208
1212
|
});
|
|
1209
1213
|
server.watcher.on("add", (file) => {
|
|
1210
|
-
if (pageScripts.some(script => script.source === file) ||
|
|
1214
|
+
if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.replaceAll("\\", "/").includes(part))) {
|
|
1211
1215
|
void refresh(server).catch((error) => server.config.logger.error(String(error)));
|
|
1212
1216
|
}
|
|
1213
1217
|
});
|
|
@@ -1320,8 +1324,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1320
1324
|
await mkdir(serverDir, { recursive: true });
|
|
1321
1325
|
await writeFile(path.join(serverDir, "fuzor.manifest.json"), JSON.stringify({ ...artifact, clientDirectory: path.relative(serverDir, outDir).split(path.sep).join("/") }, null, 2) + "\n");
|
|
1322
1326
|
const dataRoutes = compiled.routes.filter(route => route.serverData);
|
|
1323
|
-
|
|
1324
|
-
if (!dataRoutes.length && !apiFiles.length && !options.serverComponents && !options.serverExternal) {
|
|
1327
|
+
if (!dataRoutes.length && !apiFiles.length && !options.serverExternal) {
|
|
1325
1328
|
await writeFile(path.join(serverDir, "entry.mjs"), `import { createServerHandler } from "fuzor/server";\n` +
|
|
1326
1329
|
`export const manifest = ${JSON.stringify(manifest)};\n` +
|
|
1327
1330
|
`export const handler = createServerHandler(manifest);\n`, "utf8");
|
|
@@ -1331,8 +1334,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1331
1334
|
const files = [...new Set(dataRoutes.map(route => route.serverData))];
|
|
1332
1335
|
const entryId = "virtual:fuzor/server-entry";
|
|
1333
1336
|
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.");`] : []),
|
|
1337
|
+
`import { createServerHandler, isServerData${Object.values(manifest.dataBindings ?? {}).some(bindings => bindings.some(binding => binding.kind === "range")) ? ", renderNativeBinding" : ""} } from "fuzor/server";`,
|
|
1336
1338
|
...files.map((file, index) => `import data${index} from ${JSON.stringify(file)};`),
|
|
1337
1339
|
...apiFiles.map((route, index) => `import * as api${index} from ${JSON.stringify(route.file)};`),
|
|
1338
1340
|
`const modules = [${files.map((_, index) => `data${index}`).join(", ")}];`,
|
|
@@ -1341,14 +1343,15 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1341
1343
|
`export const manifest = ${JSON.stringify(manifest)};`,
|
|
1342
1344
|
`const routes = ${JSON.stringify(dataRoutes.map(route => [route.path, files.indexOf(route.serverData), route.params ?? null]))};`,
|
|
1343
1345
|
`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${
|
|
1346
|
+
`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
1347
|
].join("\n");
|
|
1346
1348
|
await nestedServer.run(true, () => viteBuild({
|
|
1347
1349
|
root: vite.root, mode: vite.mode, configFile: vite.configFile ?? false, logLevel: "error", publicDir: false,
|
|
1348
|
-
// One runtime copy:
|
|
1349
|
-
//
|
|
1350
|
-
|
|
1351
|
-
|
|
1350
|
+
// One runtime copy: Fuzor's own server runtime is always bundled into the entry (generated code reaches modules
|
|
1351
|
+
// that are not package entries), so the entry and server modules share one copy of it. `effect` is imported from
|
|
1352
|
+
// node_modules by default; with `serverExternal`, everything it does not name is bundled once.
|
|
1353
|
+
ssr: options.serverExternal ? { external: options.serverExternal.filter(name => name !== "fuzor"), noExternal: true } : { external: ["effect"], noExternal: ["fuzor"] },
|
|
1354
|
+
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
1355
|
build: { ssr: true, outDir: serverDir, emptyOutDir: false, copyPublicDir: false, minify: false,
|
|
1353
1356
|
rollupOptions: { input: entryId, output: { entryFileNames: "entry.mjs", chunkFileNames: "chunks/[name]-[hash].mjs", format: "es" } } }
|
|
1354
1357
|
}));
|
|
@@ -1356,7 +1359,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1356
1359
|
// The same finished documents are also written to client/ below, so it can be served by a CDN or
|
|
1357
1360
|
// static host with only /api and server data requests reaching the server.
|
|
1358
1361
|
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)
|
|
1362
|
+
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
1363
|
}
|
|
1361
1364
|
const index = searchIndex(compiled, options.searchIndex);
|
|
1362
1365
|
if (index)
|
|
@@ -1406,7 +1409,7 @@ ${docsInit}${mixedClient ? spaStart + "\nif (!router) {\n" + mpaStart + "\n}" :
|
|
|
1406
1409
|
if (nested)
|
|
1407
1410
|
return;
|
|
1408
1411
|
const file = context.file.replaceAll("\\", "/");
|
|
1409
|
-
if (pageScripts.some(script => script.source === file) ||
|
|
1412
|
+
if (pageScripts.some(script => script.source === file) || ["/pages/", "/components/", "/layouts/", "/islands/", "/api/"].some((part) => file.includes(part))) {
|
|
1410
1413
|
const environment = context.environment?.name;
|
|
1411
1414
|
if (environment)
|
|
1412
1415
|
invalidateEnvironmentModule(context.server, resolvedVirtualId, environment, environmentApiEnabled);
|