[DX] sensible defaults for createTLStore (#3886)

`createTLStore` had defaults of empty arrays for shapeUtils and
bindingUtils. this is problematic since people who already are calling
`createTLStore` manually with like `createTLStore({shapeUtils:
defaultShapeUtils})` will miss out on bindings utils when they upgrade
to the latest version, and this will probably only fail at runtime for
them.

To prevent issues we could have made `shapeUtils` and `bindingUtils`
required args but it feels better to me, long term, if we bring
`createTLStore` in line with `createTLSchema` and configure it to use
tldraw's default shapes/bindings if no custom overrides are specified.

i.e. we can do this

```diff
- const store = createTLStore({ shapeUtils: defaultShapeUtils, bindingUtils: defaultBindingUtils })
+ const store = createTLStore()
```

There's still technically potential for breaking changes by people
accidentally including the arrow binding util when they might not have
arrows in the app, but I don't think that's likely to actually cause any
bugs unless they add their own arrow binding type later on.

### Change Type

<!--  Please select a 'Scope' label ️ -->

- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff

<!--  Please select a 'Type' label ️ -->

- [ ] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [x] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know


### Test Plan

1. Add a step-by-step description of how to test your PR here.
2.

- [ ] Unit Tests
- [ ] End to end tests

### Release Notes

- Add a brief release note for your PR here.
This commit is contained in:
David Sheldrick 2024-06-05 15:29:54 +01:00 committed by GitHub
parent 5d58924f74
commit 5d7f368fd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 20 additions and 41 deletions

View file

@ -130,10 +130,7 @@ The `store` property of the `<Tldraw>` / `<TldrawEditor>` components accepts a s
export default function () { export default function () {
const [store] = useState(() => { const [store] = useState(() => {
// Create the store // Create the store
const newStore = createTLStore({ const newStore = createTLStore()
shapeUtils: defaultShapeUtils,
bindingUtils: defaultBindingUtils,
})
// Get the snapshot // Get the snapshot
const stringified = localStorage.getItem('my-editor-snapshot') const stringified = localStorage.getItem('my-editor-snapshot')
@ -165,10 +162,7 @@ export default function () {
if (cancelled) return if (cancelled) return
// Create the store // Create the store
const newStore = createTLStore({ const newStore = createTLStore()
shapeUtils: defaultShapeUtils,
bindingUtils: defaultBindingUtils,
})
// Load the snapshot // Load the snapshot
loadSnapshot(newStore, snapshot) loadSnapshot(newStore, snapshot)

View file

@ -1,13 +1,5 @@
import { useLayoutEffect, useState } from 'react' import { useLayoutEffect, useState } from 'react'
import { import { Tldraw, createTLStore, getSnapshot, loadSnapshot, throttle } from 'tldraw'
Tldraw,
createTLStore,
defaultBindingUtils,
defaultShapeUtils,
getSnapshot,
loadSnapshot,
throttle,
} from 'tldraw'
import 'tldraw/tldraw.css' import 'tldraw/tldraw.css'
// There's a guide at the bottom of this file! // There's a guide at the bottom of this file!
@ -16,9 +8,7 @@ const PERSISTENCE_KEY = 'example-3'
export default function PersistenceExample() { export default function PersistenceExample() {
//[1] //[1]
const [store] = useState(() => const [store] = useState(() => createTLStore())
createTLStore({ shapeUtils: defaultShapeUtils, bindingUtils: defaultBindingUtils })
)
//[2] //[2]
const [loadingState, setLoadingState] = useState< const [loadingState, setLoadingState] = useState<
{ status: 'loading' } | { status: 'ready' } | { status: 'error'; error: string } { status: 'loading' } | { status: 'ready' } | { status: 'error'; error: string }

View file

@ -1,22 +1,16 @@
import { TldrawFile, createTLStore, defaultBindingUtils, defaultShapeUtils } from 'tldraw' import { TldrawFile, createTLSchema } from 'tldraw'
import * as vscode from 'vscode' import * as vscode from 'vscode'
import { nicelog } from './utils' import { nicelog } from './utils'
export const defaultFileContents: TldrawFile = { export const defaultFileContents: TldrawFile = {
tldrawFileFormatVersion: 1, tldrawFileFormatVersion: 1,
schema: createTLStore({ schema: createTLSchema().serialize(),
shapeUtils: defaultShapeUtils,
bindingUtils: defaultBindingUtils,
}).schema.serialize(),
records: [], records: [],
} }
export const fileContentWithErrors: TldrawFile = { export const fileContentWithErrors: TldrawFile = {
tldrawFileFormatVersion: 1, tldrawFileFormatVersion: 1,
schema: createTLStore({ schema: createTLSchema().serialize(),
shapeUtils: defaultShapeUtils,
bindingUtils: defaultBindingUtils,
}).schema.serialize(),
records: [{ typeName: 'shape', id: null } as any], records: [{ typeName: 'shape', id: null } as any],
} }

View file

@ -458,7 +458,7 @@ export function counterClockwiseAngleDist(a0: number, a1: number): number;
export function createSessionStateSnapshotSignal(store: TLStore): Signal<null | TLSessionStateSnapshot>; export function createSessionStateSnapshotSignal(store: TLStore): Signal<null | TLSessionStateSnapshot>;
// @public // @public
export function createTLStore({ initialData, defaultName, id, ...rest }: TLStoreOptions): TLStore; export function createTLStore({ initialData, defaultName, id, ...rest }?: TLStoreOptions): TLStore;
// @public (undocumented) // @public (undocumented)
export function createTLUser(opts?: { export function createTLUser(opts?: {

View file

@ -31,20 +31,22 @@ export function createTLStore({
defaultName = '', defaultName = '',
id, id,
...rest ...rest
}: TLStoreOptions): TLStore { }: TLStoreOptions = {}): TLStore {
const schema = const schema =
'schema' in rest && rest.schema 'schema' in rest && rest.schema
? // we have a schema ? // we have a schema
rest.schema rest.schema
: // we need a schema : // we need a schema
createTLSchema({ createTLSchema({
shapes: utilsToMap( shapes:
checkShapesAndAddCore('shapeUtils' in rest && rest.shapeUtils ? rest.shapeUtils : []) 'shapeUtils' in rest && rest.shapeUtils
), ? utilsToMap(checkShapesAndAddCore(rest.shapeUtils))
bindings: utilsToMap( : undefined,
checkBindings('bindingUtils' in rest && rest.bindingUtils ? rest.bindingUtils : []) bindings:
), 'bindingUtils' in rest && rest.bindingUtils
migrations: 'migrations' in rest ? rest.migrations : [], ? utilsToMap(checkBindings(rest.bindingUtils))
: undefined,
migrations: 'migrations' in rest ? rest.migrations : undefined,
}) })
return new Store({ return new Store({

View file

@ -5,8 +5,7 @@ import {
TLDocument, TLDocument,
TLRecord, TLRecord,
ZERO_INDEX_KEY, ZERO_INDEX_KEY,
createTLStore, createTLSchema,
defaultShapeUtils,
} from 'tldraw' } from 'tldraw'
import { type WebSocket } from 'ws' import { type WebSocket } from 'ws'
import { RoomSessionState } from '../lib/RoomSession' import { RoomSessionState } from '../lib/RoomSession'
@ -95,7 +94,7 @@ class TLServerTestImpl extends TLServer {
} }
type UnpackPromise<T> = T extends Promise<infer U> ? U : T type UnpackPromise<T> = T extends Promise<infer U> ? U : T
const schema = createTLStore({ shapeUtils: defaultShapeUtils }).schema.serialize() const schema = createTLSchema().serialize()
let server: TLServerTestImpl let server: TLServerTestImpl
let sockets: UnpackPromise<ReturnType<typeof server.createSocketPair>> let sockets: UnpackPromise<ReturnType<typeof server.createSocketPair>>