From 063f8ebaeb41b8984c2d0da303f16756f2b02b01 Mon Sep 17 00:00:00 2001 From: Espen Hovlandsdal Date: Thu, 1 Feb 2024 16:33:13 -0800 Subject: [PATCH] refactor(migration): reuse resolve migration logic in run command --- .../migration/listMigrationsCommand.ts | 8 ++--- .../commands/migration/runMigrationCommand.ts | 34 ++++--------------- .../_internal/cli/commands/migration/utils.ts | 23 +++++++++++-- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/sanity/src/_internal/cli/commands/migration/listMigrationsCommand.ts b/packages/sanity/src/_internal/cli/commands/migration/listMigrationsCommand.ts index 77f1142bf98..c3c2cbd78b9 100644 --- a/packages/sanity/src/_internal/cli/commands/migration/listMigrationsCommand.ts +++ b/packages/sanity/src/_internal/cli/commands/migration/listMigrationsCommand.ts @@ -5,7 +5,7 @@ import type {Migration} from '@sanity/migrate' import {Table} from 'console-table-printer' import {register} from 'esbuild-register/dist/node' import {MIGRATIONS_DIRECTORY, MIGRATION_SCRIPT_EXTENSIONS} from './constants' -import {resolveMigrationScript} from './utils' +import {isLoadableMigrationScript, resolveMigrationScript} from './utils' const helpText = `` @@ -85,13 +85,9 @@ export async function resolveMigrations(workDir: string): Promise - tryExtensions.map((ext) => { - const relativePath = path.join('migrations', `${location}.${ext}`) - const absolutePath = path.resolve(workDir, relativePath) - let mod - try { - // eslint-disable-next-line import/no-dynamic-require - mod = require(absolutePath) - } catch (err) { - if (err.code !== 'MODULE_NOT_FOUND') { - throw new Error(`Error: ${err.message}"`) - } - } - return {relativePath, absolutePath, mod} - }), - ) -} - function parseCliFlags(args: {argv?: string[]}) { return yargs(hideBin(args.argv || process.argv).slice(2)) .options('dry-run', {type: 'boolean', default: true}) @@ -123,8 +102,7 @@ const runMigrationCommand: CliCommandDefinition = { } const candidates = resolveMigrationScript(workDir, id) - - const resolvedScripts = candidates.filter((candidate) => candidate!.mod?.default) + const resolvedScripts = candidates.filter(isLoadableMigrationScript) if (resolvedScripts.length > 1) { // todo: consider prompt user about which one to run? note: it's likely a mistake if multiple files resolve to the same name @@ -134,7 +112,9 @@ const runMigrationCommand: CliCommandDefinition = { .join(', ')}`, ) } - if (resolvedScripts.length === 0) { + + const script = resolvedScripts[0] + if (!script) { throw new Error( `No migration found for "${id}" in current directory. Make sure that the migration file exists and exports a valid migration as its default export.\n Tried the following files:\n -${candidates @@ -143,9 +123,7 @@ const runMigrationCommand: CliCommandDefinition = { ) } - const script = resolvedScripts[0]! - - const mod = script!.mod + const mod = script.mod if ('up' in mod || 'down' in mod) { // todo: consider adding support for up/down as separate named exports // For now, make sure we reserve the names for future use diff --git a/packages/sanity/src/_internal/cli/commands/migration/utils.ts b/packages/sanity/src/_internal/cli/commands/migration/utils.ts index c9da64cc60c..105c7d4a97f 100644 --- a/packages/sanity/src/_internal/cli/commands/migration/utils.ts +++ b/packages/sanity/src/_internal/cli/commands/migration/utils.ts @@ -1,4 +1,5 @@ -import path from 'path' +import path from 'node:path' +import {isPlainObject} from 'lodash' import type {Migration} from '@sanity/migrate' import {MIGRATION_SCRIPT_EXTENSIONS, MIGRATIONS_DIRECTORY} from './constants' @@ -16,7 +17,7 @@ interface ResolvedMigrationScript { /** * The migration module, if it could be resolved - otherwise `undefined` */ - mod?: {default: Migration} + mod?: {default: Migration; up?: unknown; down?: unknown} } /** @@ -55,3 +56,21 @@ export function resolveMigrationScript( }), ) } + +/** + * Checks whether or not the passed resolved migration script is actually loadable (eg has a default export) + * + * @param script - The resolved migration script to check + * @returns `true` if the script is loadable, `false` otherwise + * @internal + */ +export function isLoadableMigrationScript( + script: ResolvedMigrationScript, +): script is Required { + if (typeof script.mod === 'undefined' || !isPlainObject(script.mod.default)) { + return false + } + + const mod = script.mod.default + return typeof mod.title === 'string' && mod.migrate !== undefined +}