vorma 0.83.0 → 0.85.0-pre.0
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/internal/framework/_typescript/client/src/client.ts +646 -422
- package/internal/framework/_typescript/client/src/links.ts +55 -21
- package/internal/framework/_typescript/create/package.json +3 -3
- package/internal/framework/_typescript/create/pnpm-lock.yaml +14 -14
- package/internal/framework/_typescript/vite/vite.ts +99 -3
- package/npm_dist/internal/framework/_typescript/client/index.js +496 -314
- package/npm_dist/internal/framework/_typescript/client/index.js.map +3 -3
- package/npm_dist/internal/framework/_typescript/client/src/client.d.ts +22 -13
- package/npm_dist/internal/framework/_typescript/client/src/client.d.ts.map +1 -1
- package/npm_dist/internal/framework/_typescript/client/src/links.d.ts.map +1 -1
- package/npm_dist/internal/framework/_typescript/vite/vite.d.ts +1 -0
- package/npm_dist/internal/framework/_typescript/vite/vite.d.ts.map +1 -1
- package/npm_dist/internal/framework/_typescript/vite/vite.js +60 -2
- package/npm_dist/internal/framework/_typescript/vite/vite.js.map +2 -2
- package/package.json +13 -13
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getAnchorDetailsFromEvent, getHrefDetails } from "vorma/kit/url";
|
|
2
2
|
import { navigationStateManager, vormaNavigate } from "./client.ts";
|
|
3
|
+
import { effectuateRedirectDataResult } from "./redirects/redirects.ts";
|
|
3
4
|
import { saveScrollState } from "./scroll_state_manager.ts";
|
|
4
5
|
|
|
5
6
|
type LinkOnClickCallback<E extends Event> = (event: E) => void | Promise<void>;
|
|
@@ -173,29 +174,62 @@ export function __makeLinkOnClickFn<E extends Event>(
|
|
|
173
174
|
|
|
174
175
|
if (!control.promise) return;
|
|
175
176
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
if (!res) {
|
|
179
|
-
// If not here, loading indicator can get stuck on
|
|
180
|
-
// following redirects
|
|
181
|
-
const targetUrl = new URL(anchor.href, window.location.href)
|
|
182
|
-
.href;
|
|
183
|
-
navigationStateManager.removeNavigation(targetUrl);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
await callbacks.beforeRender?.(e);
|
|
188
|
-
|
|
177
|
+
const outcome = await control.promise;
|
|
189
178
|
const targetUrl = new URL(anchor.href, window.location.href).href;
|
|
190
|
-
const entry = navigationStateManager.getNavigation(targetUrl);
|
|
191
|
-
if (entry) {
|
|
192
|
-
await navigationStateManager["processNavigationResult"](
|
|
193
|
-
res,
|
|
194
|
-
entry,
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
179
|
|
|
198
|
-
|
|
180
|
+
// Handle outcome based on type (discriminated union)
|
|
181
|
+
switch (outcome.type) {
|
|
182
|
+
case "aborted":
|
|
183
|
+
// Navigation was aborted - clean up to prevent stuck loading indicator
|
|
184
|
+
navigationStateManager.removeNavigation(targetUrl);
|
|
185
|
+
return;
|
|
186
|
+
|
|
187
|
+
case "redirect": {
|
|
188
|
+
// Call beforeRender while entry still exists (consistent with success case)
|
|
189
|
+
await callbacks.beforeRender?.(e);
|
|
190
|
+
|
|
191
|
+
// Clean up before redirect to prevent race conditions
|
|
192
|
+
navigationStateManager.removeNavigation(targetUrl);
|
|
193
|
+
|
|
194
|
+
// Effectuate the redirect
|
|
195
|
+
await effectuateRedirectDataResult(
|
|
196
|
+
outcome.redirectData,
|
|
197
|
+
outcome.props.redirectCount || 0,
|
|
198
|
+
outcome.props,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Call afterRender after redirect effectuation
|
|
202
|
+
await callbacks.afterRender?.(e);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
case "success": {
|
|
207
|
+
// Call beforeRender before processing (matches original behavior)
|
|
208
|
+
await callbacks.beforeRender?.(e);
|
|
209
|
+
|
|
210
|
+
// Process the successful navigation if entry still exists
|
|
211
|
+
const entry =
|
|
212
|
+
navigationStateManager.getNavigation(targetUrl);
|
|
213
|
+
if (entry) {
|
|
214
|
+
await navigationStateManager.processSuccessfulNavigation(
|
|
215
|
+
outcome,
|
|
216
|
+
entry,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Call afterRender after processing (matches original behavior)
|
|
221
|
+
await callbacks.afterRender?.(e);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
default: {
|
|
226
|
+
// Exhaustiveness check
|
|
227
|
+
const _exhaustive: never = outcome;
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Unexpected outcome type: ${(_exhaustive as any).type}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
199
233
|
}
|
|
200
234
|
};
|
|
201
235
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-vorma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.0-pre.0",
|
|
4
4
|
"description": "CLI for creating new Vorma applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@clack/prompts": "^0.11.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^24.
|
|
28
|
-
"typescript": "^5.9.
|
|
27
|
+
"@types/node": "^24.10.7",
|
|
28
|
+
"typescript": "^5.9.3"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=22.11.0"
|
|
@@ -13,11 +13,11 @@ importers:
|
|
|
13
13
|
version: 0.11.0
|
|
14
14
|
devDependencies:
|
|
15
15
|
'@types/node':
|
|
16
|
-
specifier: ^24.
|
|
17
|
-
version: 24.
|
|
16
|
+
specifier: ^24.10.7
|
|
17
|
+
version: 24.10.7
|
|
18
18
|
typescript:
|
|
19
|
-
specifier: ^5.9.
|
|
20
|
-
version: 5.9.
|
|
19
|
+
specifier: ^5.9.3
|
|
20
|
+
version: 5.9.3
|
|
21
21
|
|
|
22
22
|
packages:
|
|
23
23
|
|
|
@@ -27,8 +27,8 @@ packages:
|
|
|
27
27
|
'@clack/prompts@0.11.0':
|
|
28
28
|
resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
|
|
29
29
|
|
|
30
|
-
'@types/node@24.
|
|
31
|
-
resolution: {integrity: sha512
|
|
30
|
+
'@types/node@24.10.7':
|
|
31
|
+
resolution: {integrity: sha512-+054pVMzVTmRQV8BhpGv3UyfZ2Llgl8rdpDTon+cUH9+na0ncBVXj3wTUKh14+Kiz18ziM3b4ikpP5/Pc0rQEQ==}
|
|
32
32
|
|
|
33
33
|
picocolors@1.1.1:
|
|
34
34
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
|
@@ -36,13 +36,13 @@ packages:
|
|
|
36
36
|
sisteransi@1.0.5:
|
|
37
37
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
|
38
38
|
|
|
39
|
-
typescript@5.9.
|
|
40
|
-
resolution: {integrity: sha512-
|
|
39
|
+
typescript@5.9.3:
|
|
40
|
+
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
|
41
41
|
engines: {node: '>=14.17'}
|
|
42
42
|
hasBin: true
|
|
43
43
|
|
|
44
|
-
undici-types@7.
|
|
45
|
-
resolution: {integrity: sha512-
|
|
44
|
+
undici-types@7.16.0:
|
|
45
|
+
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
|
46
46
|
|
|
47
47
|
snapshots:
|
|
48
48
|
|
|
@@ -57,14 +57,14 @@ snapshots:
|
|
|
57
57
|
picocolors: 1.1.1
|
|
58
58
|
sisteransi: 1.0.5
|
|
59
59
|
|
|
60
|
-
'@types/node@24.
|
|
60
|
+
'@types/node@24.10.7':
|
|
61
61
|
dependencies:
|
|
62
|
-
undici-types: 7.
|
|
62
|
+
undici-types: 7.16.0
|
|
63
63
|
|
|
64
64
|
picocolors@1.1.1: {}
|
|
65
65
|
|
|
66
66
|
sisteransi@1.0.5: {}
|
|
67
67
|
|
|
68
|
-
typescript@5.9.
|
|
68
|
+
typescript@5.9.3: {}
|
|
69
69
|
|
|
70
|
-
undici-types@7.
|
|
70
|
+
undici-types@7.16.0: {}
|
|
@@ -1,23 +1,74 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
1
4
|
type VormaVitePluginConfig = {
|
|
2
5
|
rollupInput: ReadonlyArray<string>;
|
|
3
6
|
publicPathPrefix: string;
|
|
4
7
|
staticPublicAssetMap: Record<string, string>;
|
|
5
8
|
buildtimePublicURLFuncName: string;
|
|
9
|
+
filemapJSONPath: string;
|
|
6
10
|
ignoredPatterns: ReadonlyArray<string>;
|
|
7
11
|
dedupeList: ReadonlyArray<string>;
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
export default function vormaVitePlugin(config: VormaVitePluginConfig): any {
|
|
15
|
+
// Cache for dev mode filemap reading.
|
|
16
|
+
// In dev mode, we read from the JSON file so we can pick up changes
|
|
17
|
+
// without restarting Vite. The mtime check allows us to avoid re-reading
|
|
18
|
+
// the file on every transform if it hasn't changed.
|
|
19
|
+
let cachedMap: Record<string, string> | null = null;
|
|
20
|
+
let cachedMtime: number = 0;
|
|
21
|
+
let isDev = false;
|
|
22
|
+
let resolvedFilemapPath: string | null = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets the current filemap, reading from disk in dev mode.
|
|
26
|
+
* In production builds, uses the static map passed at plugin creation.
|
|
27
|
+
*/
|
|
28
|
+
function getFilemap(): Record<string, string> {
|
|
29
|
+
if (!isDev) {
|
|
30
|
+
return config.staticPublicAssetMap;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!resolvedFilemapPath) {
|
|
34
|
+
resolvedFilemapPath = resolve(
|
|
35
|
+
process.cwd(),
|
|
36
|
+
config.filemapJSONPath,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const stat = statSync(resolvedFilemapPath);
|
|
42
|
+
const mtime = stat.mtimeMs;
|
|
43
|
+
|
|
44
|
+
// Return cached version if file hasn't changed
|
|
45
|
+
if (cachedMap && mtime === cachedMtime) {
|
|
46
|
+
return cachedMap;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const content = readFileSync(resolvedFilemapPath, "utf-8");
|
|
50
|
+
cachedMap = JSON.parse(content);
|
|
51
|
+
cachedMtime = mtime;
|
|
52
|
+
return cachedMap!;
|
|
53
|
+
} catch {
|
|
54
|
+
// Fallback to initial config if file can't be read.
|
|
55
|
+
// This handles the case where the JSON file doesn't exist yet
|
|
56
|
+
// (e.g., on first build before Wave has written it).
|
|
57
|
+
return config.staticPublicAssetMap;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
11
61
|
return {
|
|
12
62
|
name: "vorma-vite-plugin",
|
|
63
|
+
|
|
13
64
|
config(c: any, { command }: any) {
|
|
65
|
+
isDev = command === "serve";
|
|
66
|
+
|
|
14
67
|
const mp = c.build?.modulePreload;
|
|
15
68
|
const roi = c.build?.rollupOptions?.input;
|
|
16
69
|
const ign = c.server?.watch?.ignored;
|
|
17
70
|
const dedupe = c.resolve?.dedupe;
|
|
18
71
|
|
|
19
|
-
const isDev = command === "serve";
|
|
20
|
-
|
|
21
72
|
return {
|
|
22
73
|
base: isDev ? "/" : config.publicPathPrefix,
|
|
23
74
|
build: {
|
|
@@ -65,6 +116,48 @@ export default function vormaVitePlugin(config: VormaVitePluginConfig): any {
|
|
|
65
116
|
},
|
|
66
117
|
};
|
|
67
118
|
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Configures the dev server with an endpoint for filemap cache invalidation.
|
|
122
|
+
* Wave calls this endpoint after updating public static files, which:
|
|
123
|
+
* 1. Clears the cached filemap so the next transform reads fresh data
|
|
124
|
+
* 2. Invalidates all modules in Vite's module graph
|
|
125
|
+
* 3. Triggers a browser reload via Vite's HMR websocket
|
|
126
|
+
*
|
|
127
|
+
* This is much faster than cycling Vite (stopping and restarting the process).
|
|
128
|
+
*/
|
|
129
|
+
configureServer(server: any) {
|
|
130
|
+
server.middlewares.use((req: any, res: any, next: any) => {
|
|
131
|
+
if (req.url !== "/__vorma_invalidate_filemap") {
|
|
132
|
+
return next();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(
|
|
136
|
+
"[vorma-vite-plugin] Filemap invalidation triggered",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Clear the filemap cache so the next transform reads fresh data
|
|
140
|
+
cachedMap = null;
|
|
141
|
+
cachedMtime = 0;
|
|
142
|
+
|
|
143
|
+
// Invalidate all modules in Vite's module graph.
|
|
144
|
+
// This is simpler than tracking which specific modules use
|
|
145
|
+
// waveBuildtimeURL() and fast enough for typical project sizes
|
|
146
|
+
// (a few ms for hundreds of modules).
|
|
147
|
+
for (const mod of server.moduleGraph.idToModuleMap.values()) {
|
|
148
|
+
server.moduleGraph.invalidateModule(mod);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Trigger a full browser reload via Vite's HMR websocket.
|
|
152
|
+
// The browser will re-request modules, Vite will re-transform them
|
|
153
|
+
// (cache miss due to invalidation), and they'll get the new URLs.
|
|
154
|
+
server.ws.send({ type: "full-reload" });
|
|
155
|
+
|
|
156
|
+
res.statusCode = 200;
|
|
157
|
+
res.end("ok");
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
|
|
68
161
|
transform(code: any, id: any) {
|
|
69
162
|
const isNodeModules = /node_modules/.test(id);
|
|
70
163
|
if (isNodeModules) return null;
|
|
@@ -77,10 +170,13 @@ export default function vormaVitePlugin(config: VormaVitePluginConfig): any {
|
|
|
77
170
|
const needsReplacement = regex.test(code);
|
|
78
171
|
if (!needsReplacement) return null;
|
|
79
172
|
|
|
173
|
+
// Get the current filemap (reads from disk in dev mode)
|
|
174
|
+
const filemap = getFilemap();
|
|
175
|
+
|
|
80
176
|
const replacedCode = code.replace(
|
|
81
177
|
regex,
|
|
82
178
|
(_: any, __: any, assetPath: any) => {
|
|
83
|
-
const hashed =
|
|
179
|
+
const hashed = filemap[assetPath];
|
|
84
180
|
if (!hashed) return `"${assetPath}"`;
|
|
85
181
|
return `"${config.publicPathPrefix}${hashed}"`;
|
|
86
182
|
},
|