Skip to content

Commit 900d4fc

Browse files
committed
update readme and vitest
1 parent c5f4ede commit 900d4fc

File tree

9 files changed

+128
-71
lines changed

9 files changed

+128
-71
lines changed

README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Svelte-Command-Form allows you to have easy to use forms with commands instead o
55
## Features
66

77
- **Schema-agnostic validation** – Works with any library that implements the Standard Schema v1 interface (Zod, Valibot, TypeBox, custom validators, …).
8-
- **Command-first workflow** – Wire forms directly to your remote command (e.g. [`command` from `$app/server`](https://kit.svelte.dev/docs/load#command-functions)), and let the helper manage submission, success, and error hooks.
8+
- **Command-first workflow** – Wire forms directly to your remote command ([`command` from `$app/server`](https://kit.svelte.dev/docs/load#command-functions)), and let the helper manage submission, success, and error hooks.
99
- **Typed form state**`form`, `errors`, and `issues` are all strongly typed from your schema, so your component code stays in sync with validation rules.
1010
- **Friendly + raw errors** – Surface user-friendly `errors` for rendering, while also exposing the untouched validator `issues` array for logging/analytics.
1111
- **Helpers for remote inputs** – Includes `normalizeFiles` for bundling file uploads and `standardValidate` for reusing schema validation outside the form class.
@@ -182,7 +182,7 @@ Both the Zod and Valibot schemas above can be adapted to accept either `File[]`
182182

183183
## Initial values and schema defaults
184184

185-
Standard Schema v1 intentionally does **not** provide a cross-library location for default values. A Zod or Valibot schema may specify defaults internally, but those defaults are not discoverable through the shared `~standard` interface. Because of that, `CommandForm` cannot pull defaults from your schema automatically. Instead, pass defaults via `options.initial`:
185+
Standard Schema v1 does **not** provide a cross-library location for default values. A Zod or Valibot schema may specify defaults internally, but those defaults are not discoverable through the shared `~standard` interface. If there is an easy way to do this feel free to submit a PR. Because of that, `CommandForm` cannot pull defaults from your schema automatically. Instead, pass defaults via `options.initial`:
186186

187187
```ts
188188
const form = new CommandForm(userSchema, {
@@ -201,21 +201,38 @@ When validation fails, `CommandForm`:
201201
2. Converts issues into `errors` (per field) via `transformIssues`.
202202
3. Stores the raw issue array in `issues` for programmatic access.
203203

204-
If the command throws an `HttpError` from SvelteKit, the helper looks for `err.body.issues` and merges them into the same structures. Any other error is forwarded to `onError` after clearing submission state.
204+
If the command throws an `HttpError` from SvelteKit, the helper looks for `err.body.issues` and merges them into the same structures. Any other error is forwarded to `onError` after clearing submission state. You can handle validation errors to populate this in your `hooks.server.ts`
205205

206-
## Development
206+
## Manual Errors
207207

208-
- `pnpm dev` – Play with the demo app in `src/routes`.
209-
- `pnpm check` – Run `svelte-check` for type and accessibility diagnostics.
210-
- `pnpm test` – Execute the Vitest suite (if present).
211-
- `pnpm prepack` – Builds the library with `svelte-package` + `publint` (also run as part of `pnpm build`).
208+
You can add errors manually by using the `addErrors` method on the client or by throwing a `new SchemaValidationError` inside of the remote function.
212209

213-
## Publishing
210+
```typescript
211+
// server add error
212+
const someFunc = command(schema, async (data) => {
213+
const user = await db.find({where: email: data.email})
214+
if(!user) throw new SchemaValidationError([{ message: "User does with this email does not exist!", path: ['email'] }])
215+
})
216+
```
217+
218+
```html
219+
<!-- +page.svelte -->
220+
<script lang="ts">
221+
const form = new CommandForm(schema, {
222+
command: someCommand
223+
});
224+
225+
function addError() {
226+
form.addError({ path: 'name', message: 'Test Error' });
227+
}
228+
</script>
229+
230+
<button onclick="{addError}">Add an Error</button>
231+
```
232+
233+
## Contributing
214234

215-
1. Confirm `package.json` metadata (name, version, description, license, repository, etc.).
216-
2. Run `pnpm build` to emit `dist/`.
217-
3. Inspect the output (`npm pack --dry-run`) if desired.
218-
4. Publish: `npm publish` (or `pnpm publish`).
235+
Feel free to contribute by opening a PR with a detailed description of why you are wanting to change what you are changing. If it can be tested with Vitest, that is preferred.
219236

220237
## License
221238

package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
1313
"format": "prettier --write .",
1414
"lint": "prettier --check . && eslint .",
15-
"test:unit": "vitest",
16-
"test": "npm run test:unit -- --run"
15+
"test": "vitest"
1716
},
1817
"homepage": "https://github.com/akcodeworks/svelte-command-form#readme",
1918
"bugs": {
@@ -28,7 +27,8 @@
2827
"files": [
2928
"dist",
3029
"!dist/**/*.test.*",
31-
"!dist/**/*.spec.*"
30+
"!dist/**/*.spec.*",
31+
"!dist/**/tests/**"
3232
],
3333
"sideEffects": [
3434
"**/*.css"
@@ -43,8 +43,8 @@
4343
}
4444
},
4545
"peerDependencies": {
46-
"svelte": "^5.0.0",
47-
"@sveltejs/kit": "^2.47.1"
46+
"@sveltejs/kit": "^2.47.1",
47+
"svelte": "^5.0.0"
4848
},
4949
"devDependencies": {
5050
"@eslint/compat": "^1.4.0",
@@ -59,7 +59,6 @@
5959
"eslint-config-prettier": "^10.1.8",
6060
"eslint-plugin-svelte": "^3.12.4",
6161
"globals": "^16.4.0",
62-
"playwright": "^1.56.1",
6362
"prettier": "^3.6.2",
6463
"prettier-plugin-svelte": "^3.4.0",
6564
"publint": "^0.3.14",

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/demo.spec.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/lib/tests/cf.svelte.spec.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { CommandForm, SchemaValidationError } from '$lib/index.ts';
2+
import { describe, it, expect } from 'vitest';
3+
import { testRemoteCommand } from './test.remote.ts';
4+
import { testSchema } from './test.schema.ts';
5+
// there is no way to actually test the other parts of remote functions currently without a hacky mockup...for now just test client side parts
6+
// https://github.com/sveltejs/kit/issues/14796
7+
8+
describe('initial values', async () => {
9+
it('test client side transforms', async () => {
10+
let someNum = $state(1)
11+
const f = new CommandForm(testSchema, {
12+
command: testRemoteCommand,
13+
onSubmit: async () => {
14+
throw new SchemaValidationError([{ message: "Test Error", path: ['name'] }])
15+
}
16+
})
17+
18+
expect(f.errors).toStrictEqual({})
19+
20+
f.set({ name: "Vitest" })
21+
22+
expect(f.form.name).toBe("Vitest")
23+
24+
f.set({ name: "TestVitest", age: 5 })
25+
26+
expect(f.form).toStrictEqual({ name: "TestVitest", age: 5 })
27+
28+
f.set({ age: 10 }, true)
29+
30+
expect(f.form).toStrictEqual({ age: 10 })
31+
32+
f.form.name = "DirectSet"
33+
34+
expect(f.form).toStrictEqual({ name: "DirectSet", age: 10 })
35+
36+
f.addError({ path: 'name', message: "Test Error" })
37+
38+
expect(f.errors).toStrictEqual({ name: { message: "Test Error" } })
39+
40+
// test initial values without a getter func so reset should go back to inital value not the derived updated value
41+
const ff = new CommandForm(testSchema, {
42+
command: testRemoteCommand,
43+
initial: { name: "Initial Name", age: someNum + 1 }
44+
})
45+
46+
expect(ff.form).toStrictEqual({ name: "Initial Name", age: 2 })
47+
48+
someNum = 2
49+
50+
51+
expect(ff.form).toStrictEqual({ name: "Initial Name", age: 2 })
52+
53+
ff.reset()
54+
55+
expect(ff.form).toStrictEqual({ name: "Initial Name", age: 2 })
56+
57+
someNum = 1
58+
59+
// do same test but with a getter function to see if it reacts
60+
61+
const fff = new CommandForm(testSchema, {
62+
command: testRemoteCommand,
63+
initial: () => ({ name: "Initial Name", age: someNum + 1 })
64+
})
65+
66+
expect(fff.form).toStrictEqual({ name: "Initial Name", age: 2 })
67+
68+
someNum++
69+
70+
expect(fff.form).toStrictEqual({ name: "Initial Name", age: 2 })
71+
72+
fff.reset()
73+
74+
expect(fff.form).toStrictEqual({ name: "Initial Name", age: 3 })
75+
});
76+
});

src/lib/tests/test.remote.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { command } from "$app/server";
2+
import { testSchema } from "./test.schema.ts";
3+
4+
const testRemoteCommand = command(testSchema, async (data) => {
5+
return data
6+
})
7+
8+
export { testRemoteCommand };

src/lib/tests/test.schema.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import z from "zod";
2+
3+
const testSchema = z.object({
4+
name: z.string().min(2, "Name is required"),
5+
age: z.number().min(0)
6+
})
7+
8+
export { testSchema };

src/routes/page.svelte.spec.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

vite.config.ts

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,6 @@
11
import { defineConfig } from 'vitest/config';
2-
import { playwright } from '@vitest/browser-playwright';
32
import { sveltekit } from '@sveltejs/kit/vite';
43

54
export default defineConfig({
6-
plugins: [sveltekit()],
7-
test: {
8-
expect: { requireAssertions: true },
9-
projects: [
10-
{
11-
extends: './vite.config.ts',
12-
test: {
13-
name: 'client',
14-
browser: {
15-
enabled: true,
16-
provider: playwright(),
17-
instances: [{ browser: 'chromium', headless: true }]
18-
},
19-
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
20-
exclude: ['src/lib/server/**']
21-
}
22-
},
23-
{
24-
extends: './vite.config.ts',
25-
test: {
26-
name: 'server',
27-
environment: 'node',
28-
include: ['src/**/*.{test,spec}.{js,ts}'],
29-
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}']
30-
}
31-
}
32-
]
33-
}
5+
plugins: [sveltekit()]
346
});

0 commit comments

Comments
 (0)