UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

133 lines (117 loc) 4.28 kB
import {existsSync, mkdirSync} from 'node:fs' import {writeFile} from 'node:fs/promises' import path from 'node:path' import {type CliCommandDefinition} from '@sanity/cli' import deburr from 'lodash/deburr' import {MIGRATIONS_DIRECTORY} from './constants' import {minimalAdvanced} from './templates/minimalAdvanced' import {minimalSimple} from './templates/minimalSimple' import {renameField} from './templates/renameField' import {renameType} from './templates/renameType' import {stringToPTE} from './templates/stringToPTE' const helpText = ` Examples: # Create a new migration, prompting for title and options sanity migration create # Create a new migration with the provided title, prompting for options sanity migration create "Rename field from location to address" ` // eslint-disable-next-line @typescript-eslint/no-empty-interface interface CreateMigrationFlags {} const TEMPLATES = [ {name: 'Minimalistic migration to get you started', template: minimalSimple}, {name: 'Rename an object type', template: renameType}, {name: 'Rename a field', template: renameField}, {name: 'Convert string field to Portable Text', template: stringToPTE}, { name: 'Advanced template using async iterators providing more fine grained control', template: minimalAdvanced, }, ] const createMigrationCommand: CliCommandDefinition<CreateMigrationFlags> = { name: 'create', group: 'migration', signature: '[TITLE]', helpText, description: 'Create a new migration within your project', action: async (args, context) => { const {output, prompt, workDir, chalk} = context let [title] = args.argsWithoutOptions while (!title?.trim()) { title = await prompt.single({ type: 'input', suffix: ' (e.g. "Rename field from location to address")', message: 'Title of migration', }) if (!title.trim()) { output.error(chalk.red('Name cannot be empty')) } } const types = await prompt.single({ type: 'input', suffix: ' (optional)', message: 'Type of documents to migrate. You can add multiple types separated by comma', }) const templatesByName = Object.fromEntries(TEMPLATES.map((t) => [t.name, t])) const template = await prompt.single({ type: 'list', message: 'Select a template', choices: TEMPLATES.map((definedTemplate) => ({ name: definedTemplate.name, value: definedTemplate.name, })), }) const sluggedName = deburr(title.toLowerCase()) .replace(/\s+/g, '-') .replace(/[^a-z0-9-]/g, '') const destDir = path.join(workDir, MIGRATIONS_DIRECTORY, sluggedName) if (existsSync(destDir)) { if ( !(await prompt.single({ type: 'confirm', message: `Migration directory ${chalk.cyan(destDir)} already exists. Overwrite?`, default: false, })) ) { return } } mkdirSync(destDir, {recursive: true}) const renderedTemplate = (templatesByName[template].template || minimalSimple)({ migrationName: title, documentTypes: types .split(',') .map((t) => t.trim()) .filter(Boolean), }) const definitionFile = path.join(destDir, 'index.ts') await writeFile(definitionFile, renderedTemplate) // To dry run it, run \`sanity migration run ${sluggedName}\``) output.print() output.print(`${chalk.green('✓')} Migration created!`) output.print() output.print('Next steps:') output.print( `Open ${chalk.bold( definitionFile, )} in your code editor and write the code for your migration.`, ) output.print( `Dry run the migration with:\n\`${chalk.bold( `sanity migration run ${sluggedName} --project=<projectId> --dataset <dataset> `, )}\``, ) output.print( `Run the migration against a dataset with:\n \`${chalk.bold( `sanity migration run ${sluggedName} --project=<projectId> --dataset <dataset> --no-dry-run`, )}\``, ) output.print() output.print( `👉 Learn more about schema and content migrations at ${chalk.bold( 'https://www.sanity.io/docs/schema-and-content-migrations', )}`, ) }, } export default createMigrationCommand