@void/vue 1.2.0 → 1.3.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/dist/index.d.mts +3 -1
- package/dist/index.mjs +25 -13
- package/dist/plugin.mjs +43 -18
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionClient, NavigationState, VisitOptions, VoidActionError, VoidRouter } from "void/pages-client";
|
|
1
|
+
import { ActionClient, FormResetOptions, NavigationState, VisitOptions, VoidActionError, VoidRouter } from "void/pages-client";
|
|
2
2
|
import { Component } from "vue";
|
|
3
3
|
import { CloudContextVariables, DeferredState as Deferred, InferProps } from "void";
|
|
4
4
|
import { ActionFormOptions, ActionUrl, ResolveActionBody } from "void/routes";
|
|
@@ -33,7 +33,9 @@ export declare function useForm<U extends ActionUrl & string>(url: U, defaults:
|
|
|
33
33
|
put(opts?: FormSubmitOptions): Promise<void>;
|
|
34
34
|
patch(opts?: FormSubmitOptions): Promise<void>;
|
|
35
35
|
delete(opts?: FormSubmitOptions): Promise<void>;
|
|
36
|
+
reset(options: FormResetOptions, ...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
36
37
|
reset(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
38
|
+
setError(field: keyof ResolveActionBody<U> & string, message: string): void;
|
|
37
39
|
clearErrors(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
|
|
38
40
|
clearError(): void;
|
|
39
41
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VoidActionError, categorizeActionError, createActionClient, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, idleNavigationState, interpolateRouteParams, isAbortError, isEqualFormValue, isModifiedEvent, mergeDataIntoHref, scheduleFormRetry, ssrProxy, submitAction } from "void/pages-client";
|
|
2
|
-
import { computed, defineComponent, h, inject, onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
|
1
|
+
import { VoidActionError, categorizeActionError, cloneFormValue, createActionClient, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, idleNavigationState, interpolateRouteParams, isAbortError, isEqualFormValue, isModifiedEvent, mergeDataIntoHref, parseFormResetArgs, scheduleFormRetry, ssrProxy, submitAction } from "void/pages-client";
|
|
2
|
+
import { computed, defineComponent, h, inject, onMounted, onUnmounted, reactive, ref, shallowRef, watch } from "vue";
|
|
3
3
|
//#region src/runtime/form.ts
|
|
4
4
|
const Form = defineComponent({
|
|
5
5
|
name: "VoidForm",
|
|
@@ -241,28 +241,36 @@ function useShared() {
|
|
|
241
241
|
//#endregion
|
|
242
242
|
//#region src/runtime/use-form.ts
|
|
243
243
|
function useForm(url, defaults, options) {
|
|
244
|
-
const
|
|
244
|
+
const initialDefaults = cloneFormValue(defaults);
|
|
245
245
|
const router = inject("__void_router", null);
|
|
246
|
-
const data = reactive(
|
|
246
|
+
const data = reactive(cloneFormValue(initialDefaults));
|
|
247
247
|
const errors = reactive({});
|
|
248
248
|
const error = ref(null);
|
|
249
249
|
const pending = ref(false);
|
|
250
250
|
const wasSuccessful = ref(false);
|
|
251
251
|
const recentlySuccessful = ref(false);
|
|
252
|
-
|
|
252
|
+
const defaults_ = shallowRef(initialDefaults);
|
|
253
253
|
let successTimeout = null;
|
|
254
|
-
|
|
255
|
-
watch(() => (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
254
|
+
let dataSnapshot = cloneFormValue(initialDefaults);
|
|
255
|
+
watch(() => cloneFormValue(data), (val) => {
|
|
256
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(dataSnapshot), ...Object.keys(val)])) if (!isEqualFormValue(val[key], dataSnapshot[key])) delete errors[key];
|
|
257
|
+
dataSnapshot = val;
|
|
258
|
+
}, { flush: "sync" });
|
|
259
|
+
const hasChanges = computed(() => Object.keys(defaults_.value).some((key) => !isEqualFormValue(data[key], defaults_.value[key])));
|
|
260
|
+
function reset(...args) {
|
|
261
|
+
const { initial, fields } = parseFormResetArgs(args);
|
|
262
|
+
const source = initial ? initialDefaults : defaults_.value;
|
|
263
|
+
if (fields.length === 0) Object.assign(data, cloneFormValue(source));
|
|
264
|
+
else for (const field of fields) data[field] = cloneFormValue(source[field]);
|
|
265
|
+
dataSnapshot = cloneFormValue(data);
|
|
261
266
|
}
|
|
262
267
|
function clearErrors(...fields) {
|
|
263
268
|
if (fields.length === 0) for (const key of Object.keys(errors)) delete errors[key];
|
|
264
269
|
else for (const field of fields) delete errors[field];
|
|
265
270
|
}
|
|
271
|
+
function setError(field, message) {
|
|
272
|
+
errors[field] = message;
|
|
273
|
+
}
|
|
266
274
|
function clearError() {
|
|
267
275
|
error.value = null;
|
|
268
276
|
}
|
|
@@ -288,7 +296,10 @@ function useForm(url, defaults, options) {
|
|
|
288
296
|
}
|
|
289
297
|
wasSuccessful.value = true;
|
|
290
298
|
recentlySuccessful.value = true;
|
|
291
|
-
|
|
299
|
+
if (options?.resetOnSuccess) {
|
|
300
|
+
Object.assign(data, cloneFormValue(initialDefaults));
|
|
301
|
+
defaults_.value = cloneFormValue(initialDefaults);
|
|
302
|
+
} else defaults_.value = cloneFormValue(data);
|
|
292
303
|
successTimeout = setTimeout(() => {
|
|
293
304
|
recentlySuccessful.value = false;
|
|
294
305
|
}, 2e3);
|
|
@@ -308,6 +319,7 @@ function useForm(url, defaults, options) {
|
|
|
308
319
|
wasSuccessful,
|
|
309
320
|
recentlySuccessful,
|
|
310
321
|
reset,
|
|
322
|
+
setError,
|
|
311
323
|
clearErrors,
|
|
312
324
|
clearError,
|
|
313
325
|
post: (opts) => submit("POST", opts),
|
package/dist/plugin.mjs
CHANGED
|
@@ -89,7 +89,10 @@ function islandsPlugin(pagesDir, options) {
|
|
|
89
89
|
},
|
|
90
90
|
async buildStart() {
|
|
91
91
|
if (existsSync(pagesDir)) {
|
|
92
|
-
pageScan = await scanPages(root, {
|
|
92
|
+
pageScan = await scanPages(root, {
|
|
93
|
+
pagesDir,
|
|
94
|
+
environment: isDev ? "dev" : "prod"
|
|
95
|
+
});
|
|
93
96
|
const filesToScan = [
|
|
94
97
|
...pageScan.pages.map((page) => ({
|
|
95
98
|
filePath: page.componentPath,
|
|
@@ -312,6 +315,35 @@ initIslands(islands, hydrateIsland);
|
|
|
312
315
|
//#region src/plugin-pages.ts
|
|
313
316
|
const RENDERER_ID = "virtual:void-pages-renderer";
|
|
314
317
|
const CLIENT_ENTRY_ID = "virtual:void-pages-client";
|
|
318
|
+
/** Runtime helper injected into generated SSR and client page bundles. */
|
|
319
|
+
const PATCH_PROPS_RUNTIME = `
|
|
320
|
+
function hasRuntimePropsDeclaration(comp) {
|
|
321
|
+
const declared = comp.props;
|
|
322
|
+
if (declared == null) return false;
|
|
323
|
+
if (Array.isArray(declared)) return declared.length > 0;
|
|
324
|
+
if (typeof declared === "object") return Object.keys(declared).length > 0;
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function patchProps(comp, props) {
|
|
329
|
+
if (comp.__voidPatched) return;
|
|
330
|
+
const keys = Object.keys(props);
|
|
331
|
+
if (hasRuntimePropsDeclaration(comp)) {
|
|
332
|
+
const declared = comp.props;
|
|
333
|
+
// defineModel() can declare runtime props even after typed loader props are stripped.
|
|
334
|
+
const missing = keys.filter((key) =>
|
|
335
|
+
Array.isArray(declared) ? !declared.includes(key) : !Object.hasOwn(declared, key)
|
|
336
|
+
);
|
|
337
|
+
if (missing.length === 0) return;
|
|
338
|
+
comp.props = Array.isArray(declared)
|
|
339
|
+
? [...declared, ...missing]
|
|
340
|
+
: { ...Object.fromEntries(missing.map((key) => [key, null])), ...declared };
|
|
341
|
+
} else {
|
|
342
|
+
comp.props = keys;
|
|
343
|
+
}
|
|
344
|
+
comp.__voidPatched = true;
|
|
345
|
+
}
|
|
346
|
+
`;
|
|
315
347
|
function isVuePageFile(id, pagesDir) {
|
|
316
348
|
if (!id.startsWith(pagesDir + "/")) return false;
|
|
317
349
|
const rel = id.slice(pagesDir.length + 1);
|
|
@@ -364,7 +396,10 @@ function pagesPlugin(pagesDir, options) {
|
|
|
364
396
|
const normalizedPagesDir = normalizePath(pagesDir);
|
|
365
397
|
let pendingRescan = null;
|
|
366
398
|
const rescanPages = async () => {
|
|
367
|
-
pageScan = existsSync(pagesDir) ? await scanPages(root, {
|
|
399
|
+
pageScan = existsSync(pagesDir) ? await scanPages(root, {
|
|
400
|
+
pagesDir,
|
|
401
|
+
environment: isDev ? "dev" : "prod"
|
|
402
|
+
}) : null;
|
|
368
403
|
};
|
|
369
404
|
const runRescan = () => {
|
|
370
405
|
pendingRescan ??= rescanPages().finally(() => {
|
|
@@ -384,7 +419,7 @@ function pagesPlugin(pagesDir, options) {
|
|
|
384
419
|
transform: {
|
|
385
420
|
filter: { id: /\.vue$/ },
|
|
386
421
|
handler(code, id) {
|
|
387
|
-
if (!isVuePageFile(id,
|
|
422
|
+
if (!isVuePageFile(normalizePath(id), normalizedPagesDir)) return;
|
|
388
423
|
const transformed = stripDefinePropsTypeArgument(code);
|
|
389
424
|
if (!transformed) return;
|
|
390
425
|
return {
|
|
@@ -396,7 +431,7 @@ function pagesPlugin(pagesDir, options) {
|
|
|
396
431
|
handleHotUpdate: {
|
|
397
432
|
order: "pre",
|
|
398
433
|
handler(ctx) {
|
|
399
|
-
if (!isVuePageFile(ctx.file,
|
|
434
|
+
if (!isVuePageFile(normalizePath(ctx.file), normalizedPagesDir)) return;
|
|
400
435
|
const read = ctx.read;
|
|
401
436
|
ctx.read = async () => {
|
|
402
437
|
const code = await read();
|
|
@@ -454,11 +489,9 @@ function pagesPlugin(pagesDir, options) {
|
|
|
454
489
|
viteConfig = config;
|
|
455
490
|
},
|
|
456
491
|
config(_, env) {
|
|
492
|
+
isDev = env.command === "serve";
|
|
457
493
|
if (!existsSync(pagesDir)) return;
|
|
458
|
-
if (
|
|
459
|
-
isDev = true;
|
|
460
|
-
return { optimizeDeps: { include: ["vue"] } };
|
|
461
|
-
}
|
|
494
|
+
if (isDev) return { optimizeDeps: { include: ["vue"] } };
|
|
462
495
|
if (!hasNonIslandPages(pagesDir)) return;
|
|
463
496
|
return { environments: { client: { build: {
|
|
464
497
|
manifest: true,
|
|
@@ -477,11 +510,7 @@ import { serializePageData } from "void/pages-server";
|
|
|
477
510
|
|
|
478
511
|
${generateComponentManifest(scan, pagesDir)}
|
|
479
512
|
|
|
480
|
-
|
|
481
|
-
if (comp.__voidPatched) return;
|
|
482
|
-
comp.props = Object.keys(props);
|
|
483
|
-
comp.__voidPatched = true;
|
|
484
|
-
}
|
|
513
|
+
${PATCH_PROPS_RUNTIME}
|
|
485
514
|
|
|
486
515
|
export async function renderPage(pageObj, assetTags) {
|
|
487
516
|
const PageComponent = (await components[pageObj.component]()).default;
|
|
@@ -545,11 +574,7 @@ const currentPageKey = ref(0);
|
|
|
545
574
|
const routeUrl = ref(window.location.pathname + window.location.search + window.location.hash);
|
|
546
575
|
const navigation = reactive(idleNavigationState());
|
|
547
576
|
|
|
548
|
-
|
|
549
|
-
if (comp.__voidPatched) return;
|
|
550
|
-
comp.props = Object.keys(props);
|
|
551
|
-
comp.__voidPatched = true;
|
|
552
|
-
}
|
|
577
|
+
${PATCH_PROPS_RUNTIME}
|
|
553
578
|
|
|
554
579
|
function replaceRecord(target, source) {
|
|
555
580
|
for (const key of Object.keys(target)) delete target[key];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Vue pages, server rendering, forms, and navigation for Void.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"pathslash": "^0.1.0",
|
|
42
42
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
|
|
43
43
|
"vite-plus": "1.3.0",
|
|
44
|
-
"void": "1.
|
|
44
|
+
"void": "1.3.0",
|
|
45
45
|
"vue": "^3.5.44"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"vite": "^8.0.0",
|
|
49
|
-
"void": "1.
|
|
49
|
+
"void": "1.3.0",
|
|
50
50
|
"vue": "^3.5.0"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|