feat: allow loading predefined migrations from proper exports (#9872)

Currently, predefined migrations can only be loaded if they are part of
one of our db adapters.

With this PR, plugins will be able to export their own predefined
migrations that can be created like this:

`pnpm payload migrate:create --file
@payloadcms/plugin-someplugin/someMigration`

with the plugin exporting it in their package.json:

```json
  "exports": {
    "./someMigration": {
      "import": "./someMigration.mjs",
      "types": "./someMigration.mjs",
      "default": "./someMigration.mjs"
    }
  },
```
This commit is contained in:
Alessio Gravili
2024-12-12 09:39:20 -07:00
committed by GitHub
parent 9d324ff207
commit d4d79c1141

View File

@@ -21,11 +21,12 @@ export const getPredefinedMigration = async ({
}): Promise<MigrationTemplateArgs> => {
// Check for predefined migration.
// Either passed in via --file or prefixed with '@payloadcms/db-mongodb/' for example
if (file || migrationNameArg?.startsWith('@payloadcms/')) {
// removes the package name from the migrationName.
const migrationName = (file || migrationNameArg).split('/').slice(2).join('/')
let cleanPath = path.join(dirname, `./predefinedMigrations/${migrationName}`)
const importPath = file ?? migrationNameArg
if (importPath?.startsWith('@payloadcms/db-')) {
// removes the package name from the migrationName.
const migrationName = importPath.split('/').slice(2).join('/')
let cleanPath = path.join(dirname, `./predefinedMigrations/${migrationName}`)
if (fs.existsSync(`${cleanPath}.mjs`)) {
cleanPath = `${cleanPath}.mjs`
} else if (fs.existsSync(`${cleanPath}.js`)) {
@@ -36,12 +37,15 @@ export const getPredefinedMigration = async ({
})
process.exit(1)
}
cleanPath = cleanPath.replaceAll('\\', '/')
const moduleURL = pathToFileURL(cleanPath)
try {
const { downSQL, imports, upSQL } = await eval(`import('${moduleURL.href}')`)
return { downSQL, imports, upSQL }
return {
downSQL,
imports,
upSQL,
}
} catch (err) {
payload.logger.error({
err,
@@ -49,6 +53,22 @@ export const getPredefinedMigration = async ({
})
process.exit(1)
}
} else if (importPath) {
try {
const { downSQL, imports, upSQL } = await eval(`import('${importPath}')`)
return {
downSQL,
imports,
upSQL,
}
} catch (_err) {
if (importPath?.includes('/')) {
// We can assume that the intent was to import a file, thus we throw an error.
throw new Error(`Error importing migration file from ${importPath}`)
}
// Silently fail. If the migration cannot be imported, it will be created as a blank migration and the import path will be used as the migration name.
return {}
}
}
return {}
}