Add asset pruning when importing files (#3689)

Adds pruning of unused assets when importing files.

Pulled out the pruning logic from the exporting of tldraw files and we
now use the same logic for both cases.

### 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

### Release Notes

- Prunes unused assets when loading a tldraw document.
This commit is contained in:
Mitja Bezenšek 2024-05-07 16:48:01 +02:00 committed by GitHub
parent b5caa53cee
commit ec5eded41b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -10,7 +10,6 @@ import {
SerializedSchemaV2, SerializedSchemaV2,
SerializedStore, SerializedStore,
T, T,
TLAsset,
TLAssetId, TLAssetId,
TLRecord, TLRecord,
TLSchema, TLSchema,
@ -135,7 +134,8 @@ export function parseTldrawJsonFile({
// latest version // latest version
let migrationResult: MigrationResult<SerializedStore<TLRecord>> let migrationResult: MigrationResult<SerializedStore<TLRecord>>
try { try {
const storeSnapshot = Object.fromEntries(data.records.map((r) => [r.id, r as TLRecord])) const records = pruneUnusedAssets(data.records as TLRecord[])
const storeSnapshot = Object.fromEntries(records.map((r) => [r.id, r]))
migrationResult = schema.migrateStoreSnapshot({ store: storeSnapshot, schema: data.schema }) migrationResult = schema.migrateStoreSnapshot({ store: storeSnapshot, schema: data.schema })
} catch (e) { } catch (e) {
// junk data in the migration // junk data in the migration
@ -164,11 +164,19 @@ export function parseTldrawJsonFile({
} }
} }
function pruneUnusedAssets(records: TLRecord[]) {
const usedAssets = new Set<TLAssetId>()
for (const record of records) {
if (record.typeName === 'shape' && 'assetId' in record.props && record.props.assetId) {
usedAssets.add(record.props.assetId)
}
}
return records.filter((r) => r.typeName !== 'asset' || usedAssets.has(r.id))
}
/** @public */ /** @public */
export async function serializeTldrawJson(store: TLStore): Promise<string> { export async function serializeTldrawJson(store: TLStore): Promise<string> {
const records: TLRecord[] = [] const records: TLRecord[] = []
const usedAssets = new Set<TLAssetId | null>()
const assets: TLAsset[] = []
for (const record of store.allRecords()) { for (const record of store.allRecords()) {
switch (record.typeName) { switch (record.typeName) {
case 'asset': case 'asset':
@ -188,7 +196,7 @@ export async function serializeTldrawJson(store: TLStore): Promise<string> {
assetSrcToSave = record.props.src assetSrcToSave = record.props.src
} }
assets.push({ records.push({
...record, ...record,
props: { props: {
...record.props, ...record.props,
@ -196,26 +204,19 @@ export async function serializeTldrawJson(store: TLStore): Promise<string> {
}, },
}) })
} else { } else {
assets.push(record) records.push(record)
} }
break break
case 'shape':
if ('assetId' in record.props) {
usedAssets.add(record.props.assetId)
}
records.push(record)
break
default: default:
records.push(record) records.push(record)
break break
} }
} }
const recordsToSave = records.concat(assets.filter((a) => usedAssets.has(a.id)))
return JSON.stringify({ return JSON.stringify({
tldrawFileFormatVersion: LATEST_TLDRAW_FILE_FORMAT_VERSION, tldrawFileFormatVersion: LATEST_TLDRAW_FILE_FORMAT_VERSION,
schema: store.schema.serialize(), schema: store.schema.serialize(),
records: recordsToSave, records: pruneUnusedAssets(records),
}) })
} }