Skip to content

Commit

Permalink
fix(migration): make sanity migration list also include file-only m…
Browse files Browse the repository at this point in the history
…igrations
  • Loading branch information
rexxars committed Feb 3, 2024
1 parent cc9e473 commit c6745ab
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {Migration} from '@sanity/migrate'
import chalk from 'chalk'
import {Table} from 'console-table-printer'
import {register} from 'esbuild-register/dist/node'
import {MIGRATIONS_DIRECTORY} from './constants'
import {MIGRATIONS_DIRECTORY, MIGRATION_SCRIPT_EXTENSIONS} from './constants'
import {resolveMigrationScript} from './utils'

const helpText = ``
Expand Down Expand Up @@ -38,7 +38,7 @@ const listMigrationCommand: CliCommandDefinition = {
})

migrations.forEach((definedMigration) => {
table.addRow({id: definedMigration.dirname, title: definedMigration.migration.title})
table.addRow({id: definedMigration.id, title: definedMigration.migration.title})
})
table.printTable()
output.print('\nRun `sanity migration run <ID>` to run a migration')
Expand All @@ -55,46 +55,64 @@ const listMigrationCommand: CliCommandDefinition = {
},
}

/**
* A resolved migration, where you are guaranteed that the migration file exists
*
* @internal
*/
export interface ResolvedMigration {
id: string
migration: Migration
}

/**
* Resolves all migrations in the studio working directory
*
* @param workDir - The studio working directory
* @returns Array of migrations and their respective paths
* @internal
*/
export async function resolveMigrations(
workDir: string,
): Promise<{dirname: string; migration: Migration}[]> {
export async function resolveMigrations(workDir: string): Promise<ResolvedMigration[]> {
let unregister
if (!__DEV__) {
unregister = register({
target: `node${process.version.slice(1)}`,
}).unregister
}

const directories = (
await readdir(path.join(workDir, MIGRATIONS_DIRECTORY), {withFileTypes: true})
).filter((ent) => ent.isDirectory())
const migrationsDir = path.join(workDir, MIGRATIONS_DIRECTORY)
const migrationEntries = await readdir(migrationsDir, {withFileTypes: true})

const entries = directories
.map((ent) => {
const candidates = resolveMigrationScript(workDir, ent.name)
const found = candidates.find((candidate) => candidate.mod?.default)
if (!found) {
return null
}
return {
dirname: ent.name,
migration: found.mod.default as Migration,
const migrations: ResolvedMigration[] = []
for (const entry of migrationEntries) {
const entryName = entry.isDirectory() ? entry.name : removeMigrationScriptExtension(entry.name)
const candidates = resolveMigrationScript(workDir, entryName)

for (const candidate of candidates) {
if (!candidate.mod?.default) {
continue
}
})
.filter(Boolean) as {dirname: string; migration: Migration}[]

migrations.push({
id: entryName,
migration: candidate.mod.default,
})
}
}

if (unregister) {
unregister()
}

return entries
return migrations
}

function removeMigrationScriptExtension(fileName: string) {
// Remove `.ts`, `.js` etc from the end of a filename
return MIGRATION_SCRIPT_EXTENSIONS.reduce(
(name, ext) => (name.endsWith(`.${ext}`) ? path.basename(name, `.${ext}`) : name),
fileName,
)
}

export default listMigrationCommand
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const runMigrationCommand: CliCommandDefinition<CreateFlags> = {
})

migrations.forEach((definedMigration) => {
table.addRow({id: definedMigration.dirname, title: definedMigration.migration.title})
table.addRow({id: definedMigration.id, title: definedMigration.migration.title})
})
table.printTable()
output.print('\nRun `sanity migration run <ID>` to run a migration')
Expand Down
43 changes: 40 additions & 3 deletions packages/sanity/src/_internal/cli/commands/migration/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import path from 'path'

import type {Migration} from '@sanity/migrate'
import {MIGRATION_SCRIPT_EXTENSIONS, MIGRATIONS_DIRECTORY} from './constants'

export function resolveMigrationScript(workDir: string, migrationName: string) {
interface ResolvedMigrationScript {
/**
* Relative path from the working directory to the migration script
*/
relativePath: string

/**
* Absolute path to the migration script
*/
absolutePath: string

/**
* The migration module, if it could be resolved - otherwise `undefined`
*/
mod?: {default: Migration}
}

/**
* Resolves the potential paths to a migration script.
* Considers the following paths (where `<ext>` is 'mjs', 'js', 'ts' or 'cjs'):
*
* - `<migrationsDir>/<migrationName>.<ext>`
* - `<migrationsDir>/<migrationName>/index.<ext>`
*
* Note that all possible paths are returned, even if the files do not exist.
* Check the `mod` property to see if a module could actually be loaded.
*
* @param workDir - Working directory of the studio
* @param migrationName - The name of the migration directory to resolve
* @returns An array of potential migration scripts
* @internal
*/
export function resolveMigrationScript(
workDir: string,
migrationName: string,
): ResolvedMigrationScript[] {
return [migrationName, path.join(migrationName, 'index')].flatMap((location) =>
MIGRATION_SCRIPT_EXTENSIONS.map((ext) => {
const relativePath = path.join(MIGRATIONS_DIRECTORY, `${location}.${ext}`)
Expand All @@ -12,7 +47,9 @@ export function resolveMigrationScript(workDir: string, migrationName: string) {
// eslint-disable-next-line import/no-dynamic-require
mod = require(absolutePath)
} catch (err) {
// console.error(err)
if (err.code !== 'MODULE_NOT_FOUND') {
throw new Error(`Error: ${err.message}"`)
}
}
return {relativePath, absolutePath, mod}
}),
Expand Down

0 comments on commit c6745ab

Please sign in to comment.