Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cute-knives-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/form-core': minor
---

Removes UUID from package.json for native environments. Reverts formId to a getter function.
3 changes: 1 addition & 2 deletions packages/form-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@
],
"dependencies": {
"@tanstack/devtools-event-client": "^0.3.2",
"@tanstack/store": "^0.7.7",
"uuid": "^13.0.0"
"@tanstack/store": "^0.7.7"
},
"devDependencies": {
"arktype": "^2.1.22",
Expand Down
7 changes: 4 additions & 3 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Derived, Store, batch } from '@tanstack/store'
import { v4 as uuidv4 } from 'uuid'

import {
deleteBy,
determineFormLevelErrorSourceAndValue,
Expand All @@ -12,6 +12,7 @@ import {
isNonEmptyArray,
mergeOpts,
setBy,
uuid,
} from './utils'
import { defaultValidationLogic } from './ValidationLogic'

Expand Down Expand Up @@ -1000,7 +1001,7 @@ export class FormApi<
formListeners: {} as Record<ListenerCause, never>,
}

this._formId = opts?.formId ?? uuidv4()
this._formId = opts?.formId ?? uuid()

this._devtoolsSubmissionOverride = false

Expand Down Expand Up @@ -1329,7 +1330,7 @@ export class FormApi<
})
}

formId(): string | undefined {
get formId(): string {
return this._formId
}

Expand Down
42 changes: 42 additions & 0 deletions packages/form-core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,45 @@ export function mergeOpts<T>(

return { ...originalOpts, ...overrides }
}

/*
/ credit is due to https://github.com/lukeed/uuid for this code, with current npm
/ attacks we didn't feel comfortable installing directly from npm. But big appreciation
/ from the TanStack Form team <3.
*/

let IDX = 256
const HEX: string[] = []
let BUFFER: number[] | undefined

while (IDX--) {
HEX[IDX] = (IDX + 256).toString(16).substring(1)
}

export function uuid(): string {
let i = 0
let num: number
let out = ''

if (!BUFFER || IDX + 16 > 256) {
BUFFER = new Array<number>(256)
i = 256
while (i--) {
BUFFER[i] = (256 * Math.random()) | 0
}
i = 0
IDX = 0
}

for (; i < 16; i++) {
num = BUFFER[IDX + i] as number
if (i === 6) out += HEX[(num & 15) | 64]
else if (i === 8) out += HEX[(num & 63) | 128]
else out += HEX[num]

if (i & 1 && i > 1 && i < 11) out += '-'
}

IDX++
return out
}
11 changes: 10 additions & 1 deletion packages/form-core/tests/FormApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3952,5 +3952,14 @@ it('should accept formId and return it', () => {
})
form.mount()

expect(form.formId()).toEqual('age')
expect(form.formId).toEqual('age')
})

it('should generate a formId if not provided', () => {
const form = new FormApi({
defaultValues: { age: 0 },
})
form.mount()

expect(form.formId.length).toBeGreaterThan(1)
})
8 changes: 4 additions & 4 deletions packages/form-core/tests/formOptions.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ describe('formOptions', () => {
} as FormData,
validators: {
onSubmit: ({ formApi }) => {
if (formApi.formId() === undefined) {
return 'needs formId'
if (formApi.formId) {
return 'I just need an error'
}
return undefined
},
Expand All @@ -287,7 +287,7 @@ describe('formOptions', () => {
const form = new FormApi(formOpts)

expectTypeOf(form.state.errors).toEqualTypeOf<
('needs formId' | undefined)[]
('I just need an error' | undefined)[]
>()

const form2 = new FormApi({
Expand Down Expand Up @@ -320,7 +320,7 @@ describe('formOptions', () => {
})

expectTypeOf(form3.state.errors).toEqualTypeOf<
(undefined | 'Too short!' | 'needs formId')[]
(undefined | 'Too short!' | 'I just need an error')[]
>()
})
})
32 changes: 32 additions & 0 deletions packages/form-core/tests/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
makePathArray,
mergeOpts,
setBy,
uuid,
} from '../src/index'

describe('getBy', () => {
Expand Down Expand Up @@ -769,3 +770,34 @@ describe('mergeOpts', () => {
expect(mergeOpts(original, {})).toEqual({ foo: 'test' })
})
})

describe('uuid', () => {
it('should return a string', () => {
const id = uuid()
expect(typeof id).toBe('string')
})

it('should match UUID v4 format', () => {
const id = uuid()
const uuidV4Regex =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
expect(id).toMatch(uuidV4Regex)
})

it('should generate different values on multiple calls', () => {
const ids = new Set(Array.from({ length: 100 }, () => uuid()))
expect(ids.size).toBe(100)
})

it('should always produce 36 characters', () => {
const id = uuid()
expect(id.length).toBe(36)
})

it('should set correct version (4) and variant bits', () => {
const id = uuid()
const parts = id.split('-')
expect(parts[2]?.[0]).toBe('4')
expect(['8', '9', 'a', 'b']).toContain(parts[3]?.[0])
})
})
6 changes: 3 additions & 3 deletions packages/react-form/tests/createFormHook.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,8 @@ describe('createFormHook', () => {
const form = useFormContext()

return (
<button type="submit" form={form.formId()} data-testid="formId-target">
{form.formId()}
<button type="submit" form={form.formId} data-testid="formId-target">
{form.formId}
</button>
)
}
Expand All @@ -554,7 +554,7 @@ describe('createFormHook', () => {
return (
<form.AppForm>
<form
id={form.formId()}
id={form.formId}
onSubmit={(e) => {
e.preventDefault()
form.handleSubmit()
Expand Down
10 changes: 3 additions & 7 deletions packages/react-form/tests/useForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,7 @@ describe('useForm', () => {
return (
<>
<form
id={form.formId()}
id={form.formId}
onSubmit={(e) => {
e.preventDefault()
form.handleSubmit()
Expand All @@ -878,12 +878,8 @@ describe('useForm', () => {
)}
/>

<button
type="submit"
form={form.formId()}
data-testid="formId-target"
>
{form.formId()}
<button type="submit" form={form.formId} data-testid="formId-target">
{form.formId}
</button>
</>
)
Expand Down
9 changes: 0 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading