@foldkit/vite-plugin 0.24.0 → 0.25.0-canary.c9c641bf8179
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/README.md +70 -17
- package/dist/build.d.ts +48 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +66 -58
- package/dist/buildToken.d.ts +6 -14
- package/dist/buildToken.d.ts.map +1 -1
- package/dist/buildToken.js +153 -64
- package/dist/foldkitPackages.d.ts +12 -0
- package/dist/foldkitPackages.d.ts.map +1 -0
- package/dist/foldkitPackages.js +37 -0
- package/dist/index.d.ts +22 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -89
- package/dist/relayRegistry.d.ts +17 -0
- package/dist/relayRegistry.d.ts.map +1 -0
- package/dist/relayRegistry.js +101 -0
- package/package.json +12 -9
package/README.md
CHANGED
|
@@ -65,6 +65,52 @@ With this set, the dev server converts HTML page requests to Web `Request` value
|
|
|
65
65
|
|
|
66
66
|
Vite retains ownership of configured proxy routes before Foldkit handles application requests. Vite's `server.cors` option applies to Vite-owned source modules, assets, and HMR. It does not add headers to application responses or answer their preflights. Preflight ownership follows `Access-Control-Request-Method`, so a preflight for an application `POST` reaches `renderPage` even when its path looks like an asset. An `OPTIONS` request without both `Origin` and `Access-Control-Request-Method` is not a preflight and also reaches `renderPage`. Define application CORS in `renderPage`, where development and the deployed host share one policy. Vite's `allowedHosts` check runs before proxy and application handling, including `OPTIONS` and methods the Web `Request` API cannot represent.
|
|
67
67
|
|
|
68
|
+
## Completed build metadata
|
|
69
|
+
|
|
70
|
+
Deployment tools that run Vite in process can read `foldkit:build` through Vite's standard plugin `api` field. Await the full application build before reading:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import type { FoldkitBuildApi } from '@foldkit/vite-plugin'
|
|
74
|
+
import { createBuilder } from 'vite'
|
|
75
|
+
|
|
76
|
+
const builder = await createBuilder()
|
|
77
|
+
await builder.buildApp()
|
|
78
|
+
|
|
79
|
+
const plugin = builder.config.plugins.find(
|
|
80
|
+
plugin => plugin.name === 'foldkit:build',
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (plugin !== undefined) {
|
|
84
|
+
const api: FoldkitBuildApi | undefined = plugin.api
|
|
85
|
+
|
|
86
|
+
if (typeof api?.getBuildMetadata !== 'function') {
|
|
87
|
+
throw new Error('This Foldkit version does not expose build metadata')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const metadata = api.getBuildMetadata()
|
|
91
|
+
console.log(metadata.serverEntry)
|
|
92
|
+
console.log(metadata.manifest.prerendered)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`FoldkitBuildApi` preserves `serverEntry` (the configured source entry) and `fetchModuleId` (the virtual fetch module). Its `getBuildMetadata()` method returns `FoldkitBuildMetadata`, exported as a Schema and inferred type:
|
|
97
|
+
|
|
98
|
+
| Field | Meaning |
|
|
99
|
+
| ----------------- | ------------------------------------------------------- |
|
|
100
|
+
| `root` | Absolute resolved application root |
|
|
101
|
+
| `clientDirectory` | Absolute resolved client output directory |
|
|
102
|
+
| `serverDirectory` | Absolute resolved server output directory |
|
|
103
|
+
| `serverEntry` | Absolute path to the emitted fetch handler |
|
|
104
|
+
| `manifest` | The version-1 data also written to `foldkit.build.json` |
|
|
105
|
+
|
|
106
|
+
The snapshot, manifest, and prerendered route array are frozen. The data can be serialized to another process. The manifest keeps its portable relative POSIX paths; the outer path fields describe the local build machine. Output paths reflect the resolved Vite environments, including overrides made by a host plugin.
|
|
107
|
+
|
|
108
|
+
A client-only build has no `foldkit:build` plugin. A present plugin without the accessor needs a Foldkit upgrade. The accessor throws before Foldkit finalizes, while another client or server build is running, or after its build fails. Always await `builder.buildApp()` successfully: another plugin can fail after Foldkit has finalized. An environment's `writeBundle` and another plugin's post-order `buildApp` hook do not establish this completion boundary.
|
|
109
|
+
|
|
110
|
+
Create a fresh Foldkit plugin set for each independent builder. The build plugin uses Vite's `sharedDuringBuild` to share captures across its environments. Concurrent builders must not reuse the same plugin object. This API does not add watch-mode support.
|
|
111
|
+
|
|
112
|
+
Prerendered pages and `foldkit.build.json` are finalized after the environment bundles. A separate deployment process that consumes an existing build can continue reading the disk manifest. An integration that runs the build in a child process can read the API there and transfer the serialized metadata in its child result.
|
|
113
|
+
|
|
68
114
|
## Build id
|
|
69
115
|
|
|
70
116
|
The build id does not make hydration correct. It makes hydration refuse when it would otherwise be incorrect.
|
|
@@ -73,34 +119,31 @@ Server-rendered HTML carries the deployment id, and the client bundle carries it
|
|
|
73
119
|
|
|
74
120
|
Nothing moves, so no custom element reconnects and no frame reloads. The containment blocks native page interaction; it is not a script or global-event sandbox. A client already running in an open tab is not rechecked when a deployment lands because the comparison happens only when a client boots against a page.
|
|
75
121
|
|
|
76
|
-
|
|
122
|
+
When one Vite app build produces the client and server artifacts, the plugin generates an opaque id and compiles it into Foldkit in both. The entries need no build-id wiring:
|
|
77
123
|
|
|
78
124
|
```typescript
|
|
79
|
-
|
|
125
|
+
// src/entry.server.ts
|
|
126
|
+
Server.renderToString(config, { flags })
|
|
127
|
+
|
|
128
|
+
// src/entry.ts
|
|
129
|
+
Runtime.hydrate(application)
|
|
80
130
|
```
|
|
81
131
|
|
|
82
|
-
|
|
132
|
+
Use the `buildId` option or `FOLDKIT_BUILD_ID` as an explicit override when client and server build in separate jobs, or when the id should name a deployment in another system:
|
|
83
133
|
|
|
84
134
|
```typescript
|
|
85
|
-
|
|
86
|
-
Server.renderToString(config, {
|
|
87
|
-
flags,
|
|
88
|
-
buildId: import.meta.env.FOLDKIT_BUILD_ID,
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// src/entry.ts
|
|
92
|
-
Runtime.hydrate(application, { buildId: import.meta.env.FOLDKIT_BUILD_ID })
|
|
135
|
+
plugins: [foldkit({ buildId: process.env.DEPLOYMENT_ID })]
|
|
93
136
|
```
|
|
94
137
|
|
|
95
|
-
|
|
138
|
+
Three things have to be true:
|
|
96
139
|
|
|
97
140
|
- The id appears in the HTML every visitor receives, so it must never contain a secret.
|
|
98
141
|
- Two deployments must never share an id.
|
|
99
|
-
-
|
|
142
|
+
- Separate build jobs must receive the same explicit override.
|
|
100
143
|
|
|
101
|
-
A hydratable render
|
|
144
|
+
A hydratable render with neither a compiled nor explicit id fails with `MissingBuildId`. The dev server generates an opaque id for its own client and server transforms.
|
|
102
145
|
|
|
103
|
-
The standalone `foldkitSsr({ serverEntry, buildId })` export
|
|
146
|
+
The standalone `foldkitSsr({ serverEntry, buildId })` export retains explicit build-id support for separately orchestrated integrations. The aggregate `foldkit({ ssr })` plugin owns the automatic path.
|
|
104
147
|
|
|
105
148
|
## DevTools overlay
|
|
106
149
|
|
|
@@ -110,13 +153,23 @@ To include the overlay in production, list `@foldkit/devtools` in regular `depen
|
|
|
110
153
|
|
|
111
154
|
## DevTools MCP relay
|
|
112
155
|
|
|
113
|
-
|
|
156
|
+
During development, the plugin starts a WebSocket relay for the [`@foldkit/devtools-mcp`](https://www.npmjs.com/package/@foldkit/devtools-mcp) server. Through the relay, an AI agent can inspect a running Foldkit app and dispatch Messages.
|
|
157
|
+
|
|
158
|
+
By default, the relay uses the dev server's listener at `/__foldkit/devtools-mcp`. The plugin publishes its address to a registry private to your user, and the MCP server finds it by project. You do not need to coordinate a port between them. The registry lives under `XDG_RUNTIME_DIR` when that is set, or under the operating system's temporary directory. `FOLDKIT_DEVTOOLS_RELAY_DIRECTORY` selects another directory.
|
|
159
|
+
|
|
160
|
+
The relay follows Vite's `server.host` setting. If you expose the dev server with `--host`, a client still needs the random token in the published address to inspect a Model or dispatch a Message. The plugin will not publish that token into a registry directory owned by another user or readable by other users. It reports the problem in the console.
|
|
161
|
+
|
|
162
|
+
In middleware mode, the relay uses a free loopback port because there is no HTTP server to share. It also uses a free loopback port for HTTPS dev servers, whose self-signed certificates the MCP server cannot verify. The plugin publishes these addresses for discovery in the same way.
|
|
163
|
+
|
|
164
|
+
To use a fixed port, set `devToolsMcpPort` in your Vite config:
|
|
114
165
|
|
|
115
166
|
```typescript
|
|
116
167
|
plugins: [foldkit({ devToolsMcpPort: 9988 })]
|
|
117
168
|
```
|
|
118
169
|
|
|
119
|
-
|
|
170
|
+
Set `FOLDKIT_DEVTOOLS_MCP_PORT` to the same value for the MCP server. A fixed port opens a separate socket on every interface and does not require a token. Use this setting on platforms where directory ownership cannot be verified, including Windows, because the plugin cannot publish a relay address there.
|
|
171
|
+
|
|
172
|
+
`devToolsMcpPort: false` disables the relay. The relay does not start during Vitest runs or in production builds.
|
|
120
173
|
|
|
121
174
|
See the [DevTools MCP documentation](https://foldkit.dev/ai/mcp) for setup, the available tools, and how dispatch validation works.
|
|
122
175
|
|
package/dist/build.d.ts
CHANGED
|
@@ -76,6 +76,53 @@ export declare const FoldkitBuildManifest: Schema.Struct<{
|
|
|
76
76
|
* unknown versions rather than letting the host read missing fields.
|
|
77
77
|
*/
|
|
78
78
|
export type FoldkitBuildManifest = typeof FoldkitBuildManifest.Type;
|
|
79
|
+
/** Completed application build data for an in-process deployment integration. */
|
|
80
|
+
export declare const FoldkitBuildMetadata: Schema.Struct<{
|
|
81
|
+
/** Absolute resolved Vite application root. */
|
|
82
|
+
readonly root: Schema.String;
|
|
83
|
+
/** Absolute resolved browser output directory. */
|
|
84
|
+
readonly clientDirectory: Schema.String;
|
|
85
|
+
/** Absolute resolved server output directory. */
|
|
86
|
+
readonly serverDirectory: Schema.String;
|
|
87
|
+
/** Absolute path to the emitted fetch handler. */
|
|
88
|
+
readonly serverEntry: Schema.String;
|
|
89
|
+
/** Portable data also written to `foldkit.build.json`. */
|
|
90
|
+
readonly manifest: Schema.Struct<{
|
|
91
|
+
/**
|
|
92
|
+
* The shape of this document. A consumer decodes before reading and refuses
|
|
93
|
+
* a version it does not know, so a manifest written by a newer Foldkit is a
|
|
94
|
+
* clear refusal rather than a field silently read as undefined.
|
|
95
|
+
*/
|
|
96
|
+
readonly schemaVersion: Schema.Literals<readonly [1]>;
|
|
97
|
+
/**
|
|
98
|
+
* Where the browser build was written, as a POSIX path relative to the Vite
|
|
99
|
+
* root. Relative and normalized so a manifest survives being moved with the
|
|
100
|
+
* build it describes.
|
|
101
|
+
*/
|
|
102
|
+
readonly client: Schema.String;
|
|
103
|
+
/** Where the server build was written, on the same terms as {@link client}. */
|
|
104
|
+
readonly server: Schema.String;
|
|
105
|
+
/** The server build's entry file, relative to `server`. */
|
|
106
|
+
readonly serverEntry: Schema.String;
|
|
107
|
+
/** Every path this build generated a page for, in the order it generated. */
|
|
108
|
+
readonly prerendered: Schema.$Array<Schema.String>;
|
|
109
|
+
}>;
|
|
110
|
+
}>;
|
|
111
|
+
/** The serializable, frozen snapshot of a completed Foldkit build. */
|
|
112
|
+
export type FoldkitBuildMetadata = typeof FoldkitBuildMetadata.Type;
|
|
113
|
+
/** The `foldkit:build` plugin's public integration API. */
|
|
114
|
+
export type FoldkitBuildApi = Readonly<{
|
|
115
|
+
/** Configured application source entry. */
|
|
116
|
+
serverEntry: string;
|
|
117
|
+
/** Virtual module used to bundle the fetch handler. */
|
|
118
|
+
fetchModuleId: typeof FOLDKIT_FETCH_MODULE_ID;
|
|
119
|
+
/**
|
|
120
|
+
* Read after a successful `await builder.buildApp()`. Throws before Foldkit
|
|
121
|
+
* finalizes or after another client or server build starts. Later plugin failures
|
|
122
|
+
* still require callers to await the full build successfully.
|
|
123
|
+
*/
|
|
124
|
+
getBuildMetadata: () => FoldkitBuildMetadata;
|
|
125
|
+
}>;
|
|
79
126
|
export declare const manifestPath: (root: string, directory: string, pathApi?: typeof nodePath) => string;
|
|
80
127
|
export declare const renderTargetFor: (clientDirectory: string, path: string, origin: string, pathApi?: typeof nodePath) => Readonly<{
|
|
81
128
|
url: URL;
|
|
@@ -90,5 +137,5 @@ export declare const renderTargetFor: (clientDirectory: string, path: string, or
|
|
|
90
137
|
* use the HTML emitted by the browser build, but the unrendered template is not
|
|
91
138
|
* published with the assets. The server bundle's default export is `{ fetch }`.
|
|
92
139
|
*/
|
|
93
|
-
export declare const foldkitBuild: (serverEntry: string, options?: FoldkitBuildOptions) => Plugin
|
|
140
|
+
export declare const foldkitBuild: (serverEntry: string, options?: FoldkitBuildOptions) => Plugin<FoldkitBuildApi>;
|
|
94
141
|
//# sourceMappingURL=build.d.ts.map
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B,OAAO,QAA8B,MAAM,WAAW,CAAA;AAEtD,OAAO,KAAK,EAGV,MAAM,EAEP,MAAM,MAAM,CAAA;AAEb,mEAAmE;AACnE,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C;;;OAGG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAC,CAAA;AAEF,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAAA;CAC9C,CAAC,CAAA;AAEF,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,0BAA0B,CAAA;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;IAC/B;;;;OAIG;;IAEH;;;;OAIG;;IAEH,+EAA+E;;IAE/E,2DAA2D;;IAE3D,6EAA6E;;EAE7E,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAA;AAEnE,iFAAiF;AACjF,eAAO,MAAM,oBAAoB;IAC/B,+CAA+C;;IAE/C,kDAAkD;;IAElD,iDAAiD;;IAEjD,kDAAkD;;IAElD,0DAA0D;;QAtC1D;;;;WAIG;;QAEH;;;;WAIG;;QAEH,+EAA+E;;QAE/E,2DAA2D;;QAE3D,6EAA6E;;;EAwB7E,CAAA;AAEF,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAA;AAEnE,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,aAAa,EAAE,OAAO,uBAAuB,CAAA;IAC7C;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;CAC7C,CAAC,CAAA;AAcF,eAAO,MAAM,YAAY,SACjB,MAAM,aACD,MAAM,YACR,OAAO,QAAQ,KACvB,MAQF,CAAA;AAyFD,eAAO,MAAM,eAAe,oBACT,MAAM,QACjB,MAAM,UACJ,MAAM,YACL,OAAO,QAAQ,KACvB,QAAQ,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA0CrC,CAAA;AAsFD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,gBACV,MAAM,YACV,mBAAmB,KAC3B,MAAM,CAAC,eAAe,CAqPxB,CAAA"}
|
package/dist/build.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Schema } from 'effect';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
4
|
import nodePath, { dirname, resolve } from 'node:path';
|
|
4
5
|
import { pathToFileURL } from 'node:url';
|
|
@@ -31,6 +32,19 @@ export const FoldkitBuildManifest = Schema.Struct({
|
|
|
31
32
|
/** Every path this build generated a page for, in the order it generated. */
|
|
32
33
|
prerendered: Schema.Array(Schema.String),
|
|
33
34
|
});
|
|
35
|
+
/** Completed application build data for an in-process deployment integration. */
|
|
36
|
+
export const FoldkitBuildMetadata = Schema.Struct({
|
|
37
|
+
/** Absolute resolved Vite application root. */
|
|
38
|
+
root: Schema.String,
|
|
39
|
+
/** Absolute resolved browser output directory. */
|
|
40
|
+
clientDirectory: Schema.String,
|
|
41
|
+
/** Absolute resolved server output directory. */
|
|
42
|
+
serverDirectory: Schema.String,
|
|
43
|
+
/** Absolute path to the emitted fetch handler. */
|
|
44
|
+
serverEntry: Schema.String,
|
|
45
|
+
/** Portable data also written to `foldkit.build.json`. */
|
|
46
|
+
manifest: FoldkitBuildManifest,
|
|
47
|
+
});
|
|
34
48
|
const MANIFEST_SCHEMA_VERSION = 1;
|
|
35
49
|
// Relative and POSIX so the manifest describes a layout rather than this
|
|
36
50
|
// machine: an absolute `clientOutDir` would otherwise be published verbatim and
|
|
@@ -154,26 +168,6 @@ const prerenderOptionsFrom = (prerender) => {
|
|
|
154
168
|
}
|
|
155
169
|
return prerender === true ? {} : prerender;
|
|
156
170
|
};
|
|
157
|
-
// Keyed by Vite root plus the output layout, so concurrent builds of different
|
|
158
|
-
// projects in one process never read each other's output.
|
|
159
|
-
//
|
|
160
|
-
// NOTE: the registry hangs off a global symbol rather than module scope. Vite
|
|
161
|
-
// re-bundles a config file for each environment it resolves, and every bundle
|
|
162
|
-
// is a fresh copy of this module with its own module scope, so what the client
|
|
163
|
-
// build recorded would be invisible to the instance that finalizes. The symbol
|
|
164
|
-
// is one registry for the process no matter how many copies of this module it
|
|
165
|
-
// loads.
|
|
166
|
-
const CAPTURES = Symbol.for('foldkit/vite-plugin:build-captures');
|
|
167
|
-
const captures = (() => {
|
|
168
|
-
const registry = globalThis;
|
|
169
|
-
const existing = registry[CAPTURES];
|
|
170
|
-
if (existing instanceof Map) {
|
|
171
|
-
return existing;
|
|
172
|
-
}
|
|
173
|
-
const fresh = new Map();
|
|
174
|
-
registry[CAPTURES] = fresh;
|
|
175
|
-
return fresh;
|
|
176
|
-
})();
|
|
177
171
|
const fetchModuleSource = (serverEntry, template, containerId) => {
|
|
178
172
|
const containerLiteral = containerId === undefined ? 'undefined' : JSON.stringify(containerId);
|
|
179
173
|
// NOTE: `export *` re-exports whatever the application entry actually names,
|
|
@@ -218,6 +212,8 @@ const templateForFetchModule = (capturedTemplate) => {
|
|
|
218
212
|
* published with the assets. The server bundle's default export is `{ fetch }`.
|
|
219
213
|
*/
|
|
220
214
|
export const foldkitBuild = (serverEntry, options = {}) => {
|
|
215
|
+
const state = {};
|
|
216
|
+
let metadata;
|
|
221
217
|
const clientOutDir = options.clientOutDir ?? DEFAULT_CLIENT_OUT_DIR;
|
|
222
218
|
const serverOutDir = options.serverOutDir ?? DEFAULT_SERVER_OUT_DIR;
|
|
223
219
|
const prerender = prerenderOptionsFrom(options.prerender ?? false);
|
|
@@ -226,18 +222,19 @@ export const foldkitBuild = (serverEntry, options = {}) => {
|
|
|
226
222
|
// with the build's own privileges. That module is the application's own code
|
|
227
223
|
// and its dependencies, built from the configured entry, and is trusted on
|
|
228
224
|
// exactly those terms. Nothing here is imported when prerendering is off.
|
|
229
|
-
const generatePages = async (builder, template, serverDirectory, entryFileName) => {
|
|
225
|
+
const generatePages = async (builder, template, clientDirectory, serverDirectory, entryFileName) => {
|
|
230
226
|
if (prerender === undefined) {
|
|
231
227
|
return [];
|
|
232
228
|
}
|
|
233
229
|
const origin = prerender.origin ?? DEFAULT_PRERENDER_ORIGIN;
|
|
234
|
-
const clientDirectory = resolve(builder.config.root, clientOutDir);
|
|
235
230
|
const entryFile = resolve(serverDirectory, entryFileName);
|
|
236
231
|
const contained = resolve(serverDirectory);
|
|
237
232
|
if (!entryFile.startsWith(`${contained}${nodePath.sep}`)) {
|
|
238
233
|
throw new Error(`[foldkit] the server entry "${entryFileName}" resolves outside the server build at "${contained}".`);
|
|
239
234
|
}
|
|
240
|
-
const
|
|
235
|
+
const entryUrl = pathToFileURL(entryFile);
|
|
236
|
+
entryUrl.searchParams.set('foldkit-build', randomUUID());
|
|
237
|
+
const entry = await import(entryUrl.href);
|
|
241
238
|
if (typeof entry.renderPage !== 'function') {
|
|
242
239
|
throw new Error(`[foldkit] "${entryFileName}" exports no renderPage function, so there is nothing to generate pages with.`);
|
|
243
240
|
}
|
|
@@ -258,38 +255,12 @@ export const foldkitBuild = (serverEntry, options = {}) => {
|
|
|
258
255
|
}
|
|
259
256
|
return paths;
|
|
260
257
|
};
|
|
261
|
-
const writeManifest = async (builder, serverDirectory, entryFileName, prerendered) => {
|
|
262
|
-
const manifest = Schema.encodeSync(FoldkitBuildManifest)({
|
|
263
|
-
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
264
|
-
client: manifestPath(builder.config.root, clientOutDir),
|
|
265
|
-
server: manifestPath(builder.config.root, serverOutDir),
|
|
266
|
-
serverEntry: entryFileName,
|
|
267
|
-
prerendered,
|
|
268
|
-
});
|
|
269
|
-
await writeFile(resolve(serverDirectory, MANIFEST_FILE_NAME), `${JSON.stringify(manifest, undefined, 2)}\n`);
|
|
270
|
-
builder.config.logger.info(` wrote ${MANIFEST_FILE_NAME}`);
|
|
271
|
-
};
|
|
272
|
-
// What each environment emitted, recorded as it is emitted.
|
|
273
|
-
//
|
|
274
|
-
// Vite resolves the config once per environment unless `sharedConfigBuild` is
|
|
275
|
-
// on, so the plugin that finalizes is not necessarily the instance that saw a
|
|
276
|
-
// given environment build. Keying the record by the output layout it
|
|
277
|
-
// describes is what lets the finalizing instance read what the others
|
|
278
|
-
// emitted, and what lets finalization work whether this plugin orchestrates
|
|
279
|
-
// the environments or a host does.
|
|
280
|
-
const key = [clientOutDir, serverOutDir, serverEntry].join('\u0000');
|
|
281
|
-
const captured = (root) => {
|
|
282
|
-
const existing = captures.get(`${root}\u0000${key}`);
|
|
283
|
-
if (existing !== undefined) {
|
|
284
|
-
return existing;
|
|
285
|
-
}
|
|
286
|
-
const fresh = {};
|
|
287
|
-
captures.set(`${root}\u0000${key}`, fresh);
|
|
288
|
-
return fresh;
|
|
289
|
-
};
|
|
290
258
|
const finalize = async (builder) => {
|
|
291
|
-
|
|
292
|
-
const
|
|
259
|
+
metadata = undefined;
|
|
260
|
+
const client = environmentNamed(builder, 'client');
|
|
261
|
+
const server = environmentNamed(builder, 'ssr');
|
|
262
|
+
const clientDirectory = resolve(client.config.root, client.config.build.outDir);
|
|
263
|
+
const serverDirectory = resolve(server.config.root, server.config.build.outDir);
|
|
293
264
|
if (state.serverEntryFile === undefined) {
|
|
294
265
|
throw new Error('[foldkit] the server environment produced no entry chunk, so there is nothing to deploy or generate from.');
|
|
295
266
|
}
|
|
@@ -301,15 +272,54 @@ export const foldkitBuild = (serverEntry, options = {}) => {
|
|
|
301
272
|
}
|
|
302
273
|
return state.template;
|
|
303
274
|
};
|
|
304
|
-
const prerendered = await generatePages(builder, template, serverDirectory, state.serverEntryFile);
|
|
305
|
-
|
|
275
|
+
const prerendered = await generatePages(builder, template, clientDirectory, serverDirectory, state.serverEntryFile);
|
|
276
|
+
const manifest = FoldkitBuildManifest.make({
|
|
277
|
+
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
278
|
+
client: manifestPath(builder.config.root, clientDirectory),
|
|
279
|
+
server: manifestPath(builder.config.root, serverDirectory),
|
|
280
|
+
serverEntry: state.serverEntryFile,
|
|
281
|
+
prerendered: [...prerendered],
|
|
282
|
+
});
|
|
283
|
+
await writeFile(resolve(serverDirectory, MANIFEST_FILE_NAME), `${JSON.stringify(Schema.encodeSync(FoldkitBuildManifest)(manifest), undefined, 2)}\n`);
|
|
284
|
+
builder.config.logger.info(` wrote ${MANIFEST_FILE_NAME}`);
|
|
285
|
+
const completedMetadata = FoldkitBuildMetadata.make({
|
|
286
|
+
root: builder.config.root,
|
|
287
|
+
clientDirectory,
|
|
288
|
+
serverDirectory,
|
|
289
|
+
serverEntry: resolve(serverDirectory, state.serverEntryFile),
|
|
290
|
+
manifest,
|
|
291
|
+
});
|
|
292
|
+
Object.freeze(completedMetadata.manifest.prerendered);
|
|
293
|
+
Object.freeze(completedMetadata.manifest);
|
|
294
|
+
metadata = Object.freeze(completedMetadata);
|
|
306
295
|
};
|
|
307
296
|
return {
|
|
308
297
|
name: 'foldkit:build',
|
|
309
298
|
apply: 'build',
|
|
299
|
+
sharedDuringBuild: true,
|
|
310
300
|
api: {
|
|
311
301
|
serverEntry,
|
|
312
302
|
fetchModuleId: FOLDKIT_FETCH_MODULE_ID,
|
|
303
|
+
getBuildMetadata: () => {
|
|
304
|
+
if (metadata === undefined) {
|
|
305
|
+
throw new Error('[foldkit] build metadata is not available. Read it after a successful builder.buildApp().');
|
|
306
|
+
}
|
|
307
|
+
return metadata;
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
buildStart: {
|
|
311
|
+
order: 'pre',
|
|
312
|
+
handler() {
|
|
313
|
+
if (this.environment.name === 'client') {
|
|
314
|
+
delete state.template;
|
|
315
|
+
delete state.serverEntryFile;
|
|
316
|
+
metadata = undefined;
|
|
317
|
+
}
|
|
318
|
+
else if (this.environment.name === 'ssr') {
|
|
319
|
+
delete state.serverEntryFile;
|
|
320
|
+
metadata = undefined;
|
|
321
|
+
}
|
|
322
|
+
},
|
|
313
323
|
},
|
|
314
324
|
resolveId(id) {
|
|
315
325
|
if (id === FOLDKIT_FETCH_MODULE_ID) {
|
|
@@ -321,7 +331,6 @@ export const foldkitBuild = (serverEntry, options = {}) => {
|
|
|
321
331
|
if (id !== RESOLVED_FETCH_MODULE_ID) {
|
|
322
332
|
return;
|
|
323
333
|
}
|
|
324
|
-
const state = captured(this.environment.config.root);
|
|
325
334
|
const template = templateForFetchModule(state.template);
|
|
326
335
|
return fetchModuleSource(serverEntry, template, containerId);
|
|
327
336
|
},
|
|
@@ -330,7 +339,6 @@ export const foldkitBuild = (serverEntry, options = {}) => {
|
|
|
330
339
|
generateBundle: {
|
|
331
340
|
order: 'post',
|
|
332
341
|
handler(_options, bundle) {
|
|
333
|
-
const state = captured(this.environment.config.root);
|
|
334
342
|
if (this.environment.name === 'ssr') {
|
|
335
343
|
state.serverEntryFile = serverEntryFile(Object.values(bundle), FETCH_CHUNK_NAME);
|
|
336
344
|
return;
|
package/dist/buildToken.d.ts
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
/** The build id
|
|
3
|
-
*
|
|
2
|
+
/** The build id explicitly supplied through plugin configuration or the
|
|
3
|
+
* environment, or `undefined` when neither supplied a nonempty value.
|
|
4
4
|
*
|
|
5
5
|
* @internal Exported for tests.
|
|
6
6
|
*/
|
|
7
7
|
export declare const resolveBuildId: (configured?: string) => string | undefined;
|
|
8
|
-
/** The
|
|
9
|
-
* when a build was given no id and must refuse to render a hydratable page.
|
|
8
|
+
/** The id a standalone plugin compiles for one Vite command.
|
|
10
9
|
*
|
|
11
10
|
* @internal Exported for tests.
|
|
12
11
|
*/
|
|
13
12
|
export declare const buildIdForCommand: (command: 'build' | 'serve', configured?: string) => string | undefined;
|
|
14
|
-
/**
|
|
15
|
-
* Compiles the deployment's build id into application code as
|
|
16
|
-
* `import.meta.env.FOLDKIT_BUILD_ID`, for the client entry and the server entry
|
|
17
|
-
* to hand to `Runtime.hydrate` and `renderToString`.
|
|
13
|
+
/** Compiles one build identity into application entries and Foldkit itself.
|
|
18
14
|
*
|
|
19
|
-
*
|
|
20
|
-
* compiles nothing when it was given neither, so a hydratable render fails with
|
|
21
|
-
* `MissingBuildId` rather than serving a page hydration cannot place.
|
|
22
|
-
* Development serves a fixed id instead because one live source session
|
|
23
|
-
* supplies both transforms and has no deployment identity to derive.
|
|
15
|
+
* @internal
|
|
24
16
|
*/
|
|
25
|
-
export declare const foldkitBuildToken: (buildId?: string) => Plugin
|
|
17
|
+
export declare const foldkitBuildToken: (buildId?: string, verifyFrameworkIdentity?: boolean) => Array<Plugin>;
|
|
26
18
|
//# sourceMappingURL=buildToken.d.ts.map
|
package/dist/buildToken.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildToken.d.ts","sourceRoot":"","sources":["../src/buildToken.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"buildToken.d.ts","sourceRoot":"","sources":["../src/buildToken.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAA+B,MAAM,MAAM,CAAA;AAoC/D;;;;GAIG;AACH,eAAO,MAAM,cAAc,gBAAiB,MAAM,KAAG,MAAM,GAAG,SAQ7D,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,YACnB,OAAO,GAAG,OAAO,eACb,MAAM,KAClB,MAAM,GAAG,SAMX,CAAA;AA0ID;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAClB,MAAM,wCAEf,KAAK,CAAC,MAAM,CA+Dd,CAAA"}
|