@sanity/import
Version:
Import documents to a Sanity dataset
94 lines (76 loc) • 2.73 kB
text/typescript
import {pathToFileURL} from 'node:url'
import {extractWithPath} from '@sanity/mutator'
import {serializePath} from './serializePath.js'
import {type SanityDocument} from './types.js'
import {deepGet, deepSet, deepUnset} from './util/deepGet.js'
const assetKey = '_sanityAsset'
const assetMatcher = /^(file|image)@([a-z]+:\/\/.*)/
interface AssetRef {
documentId: string
path: string
type: string
url: string
}
// Note: mutates in-place
export function unsetAssetRefs(doc: SanityDocument): SanityDocument {
for (const path of findAssetRefs(doc)) {
const parentPath = path.slice(0, -1)
const parent = deepGet(doc, parentPath) as Record<string, unknown>
// If the only key in the object is `_sanityAsset`, unset the whole thing,
// as we will be using a `setIfMissing({[path]: {}})` patch to enforce it.
// Prevents empty objects from appearing while import is running
const isOnlyKey = parent && Object.keys(parent).length === 1 && parent[assetKey]
const unsetPath = isOnlyKey ? parentPath : path
deepUnset(doc, unsetPath)
}
return doc
}
// Note: mutates in-place
export function absolutifyPaths(doc: SanityDocument, absPath?: string): SanityDocument {
if (!absPath) {
return doc
}
const modifier = (value: string): string =>
value
.replace(/file:\/\/\.\//i, `${pathToFileURL(absPath).href}/`)
.replace(/(https?):\/\/\.\//, `$1://${absPath}/`)
for (const path of findAssetRefs(doc)) {
const value = deepGet(doc, path) as string
deepSet(doc, path, modifier(value))
}
return doc
}
export function getAssetRefs(doc: SanityDocument): AssetRef[] {
return findAssetRefs(doc)
.map((path) => validateAssetImportKey(path, doc))
.map((path) => {
const value = deepGet(doc, path) as string
return {
documentId: doc._id,
path: serializePath({path: path.filter((segment) => isNotAssetKey(segment))}),
type: value.replace(assetMatcher, '$1'),
url: value.replace(assetMatcher, '$2'),
}
})
}
function isNotAssetKey(segment: number | string): boolean {
return segment !== assetKey
}
function findAssetRefs(doc: SanityDocument): (number | string)[][] {
return extractWithPath(`..[${assetKey}]`, doc).map((match) => match.path)
}
function validateAssetImportKey(
path: (number | string)[],
doc: SanityDocument,
): (number | string)[] {
if (!assetMatcher.test(deepGet(doc, path) as string)) {
throw new Error(
[
'Asset type is not specified.',
'`_sanityAsset` values must be prefixed with a type, eg image@url or file@url.',
`See document with ID "${doc._id}", path: ${serializePath({path})}`,
].join('\n'),
)
}
return path
}