Espen Hovlandsdal
Open-sourceror @ Sanity.io
🚫 MyFile / ✅ MyFile.pdf
/* eslint-disable consistent-return, no-process-env */
import {basename, extname} from 'node:path'
import {type Asset} from 'sanity'
import {at, defineMigration, patch, type SanityDocument, set} from 'sanity/migrate'
const REPLACE_EXTENSION =
typeof process === 'object' &&
typeof process.env === 'object' &&
'REPLACE_EXTENSION' in process.env
// Some formats are considered "equivalent" for our purposes, and we want to maintain them
const aliases = [
['jpeg', 'jpg'],
['tiff', 'tif'],
['mp4', 'mp'],
]
export default defineMigration({
title: 'Add asset extensions',
documentTypes: ['sanity.fileAsset', 'sanity.imageAsset'],
migrate: {
document(doc) {
if (!isAsset(doc)) {
return
}
// Actual extension set in `originalFilename`, normalized to lowercase and without leading dot
const rawExtension = extname(doc.originalFilename)
const actualExtension = extname(doc.originalFilename.toLowerCase()).replace(/^./, '')
// The "wanted" extension, as inferred by Sanity backend
const wantedExtension = doc.extension.toLowerCase()
// Do we already have the wanted extension?
const hasWantedExtension = actualExtension === wantedExtension
if (hasWantedExtension) {
return
}
// Some are acceptable as aliases, eg `tif` instead of `tiff`, `jpg` instead of `jpeg`
const hasAliasExtension = aliases.some(
(group) => group.includes(actualExtension) && group.includes(wantedExtension),
)
if (hasAliasExtension) {
return
}
// Note; this may end up as `filename.some.pdf`, if the original filename was `filename.some`
const withExtension = REPLACE_EXTENSION
? `${basename(doc.originalFilename, rawExtension)}.${wantedExtension}`
: `${doc.originalFilename}.${wantedExtension}`
return patch(doc._id, [at('originalFilename', set(withExtension))])
},
},
})
// Should always be true given the `documentTypes` filter, but for TypeScript safety,
// let's be overly pedantic/defensive.
function isAsset(doc: SanityDocument): doc is Asset & {originalFilename: string} {
return (
(doc._type === 'sanity.fileAsset' || doc._type === 'sanity.imageAsset') &&
'extension' in doc &&
'originalFilename' in doc
)
}
A migration script for the sanity migration command which finds all asset documents (they are what represents your uploaded images and files) which has an "original filename" that is missing a "correct" extension.
This usually shouldn't happen, but it could if you are uploading files/images through the API or using a client and you forget to include an extension when specifying a filename
parameter. Files can also be uploaded with a file type that does not match what the actual content is, such as uploading a .png
file that is actually a .jpg
.
Place the migration in the <studio>/migrations
directory and run it like you normally would:
sanity migration run add-asset-extensions
Note that this migration script by default will add an additional extension to files that already has one - in the above PNG/JPG mismatch scenario, the file name could end up as filename.png.jpg
. If you would rather replace the filename, you can set an environment variable REPLACE_EXTENSION
to force that behavior:
REPLACE_EXTENSION npx sanity migration run add-asset-extensions
Open-sourceror @ Sanity.io
A script to validate that your schema is free of errors and warnings
Go to Validate schema scriptDrop this into your Studio to let editors know when there's a more recent version of your Studio available, making sure they have the latest fields and validations.
Go to Auto-reload Studio when changes are deployedScript to find and delete unused assets in a dataset
Go to Delete unused assetsScript to convert quotation marks in Portable Text blocks
Go to Convert quotation marks for Portable Text