9.3.1. Replace Imports Codemod (v2.11.0+)
In this chapter, you'll learn about the codemod that helps you replace imports in your codebase when upgrading to Medusa v2.11.0.
What is the Replace Imports Codemod?#
Medusa v2.11.0 optimized the package structure by consolidating several external packages into the @medusajs/framework
package.
Previously, you had to install and manage packages related to MikroORM, Awilix, OpenTelemetry, and the pg
package separately in your Medusa application. Starting with v2.11.0, these packages are included in the @medusajs/framework
package.
For example, instead of importing @mikro-orm/core
, you now import it from @medusajs/framework/mikro-orm/core
. This applies to all of the following packages:
@mikro-orm/*
packages (for example,@mikro-orm/core
,@mikro-orm/migrations
, etc.) ->@medusajs/framework/mikro-orm/{subpath}
awilix
->@medusajs/framework/awilix
pg
->@medusajs/framework/pg
@opentelemetry/instrumentation-pg
->@medusajs/framework/opentelemetry/instrumentation-pg
@opentelemetry/resources
->@medusajs/framework/opentelemetry/resources
@opentelemetry/sdk-node
->@medusajs/framework/opentelemetry/sdk-node
@opentelemetry/sdk-trace-node
->@medusajs/framework/opentelemetry/sdk-trace-node
To help you update your codebase to reflect these changes, Medusa provides a codemod that automatically replaces imports of these packages throughout your codebase.
Using the Replace Imports Codemod#
To use the replace imports codemod, create the file replace-imports.js
in the root of your Medusa application with the following content:
1#!/usr/bin/env node2 3const fs = require("fs")4const path = require("path")5const { execSync } = require("child_process")6 7/**8 * Script to replace imports and require statements from mikro-orm/{subpath}, awilix, and pg9 * to their @medusajs/framework equivalents10 */11 12// Define the replacement mappings13const replacements = [14 // MikroORM imports - replace mikro-orm/{subpath} with @medusajs/framework/mikro-orm/{subpath}15 {16 pattern: /from\s+['"]@?mikro-orm\/([^'"]+)['"]/g,17 // eslint-disable-next-line quotes18 replacement: 'from "@medusajs/framework/mikro-orm/$1"',19 },20 // Awilix imports - replace awilix with @medusajs/framework/awilix21 {22 pattern: /from\s+['"]awilix['"]/g,23 // eslint-disable-next-line quotes24 replacement: 'from "@medusajs/framework/awilix"',25 },26 // PG imports - replace pg with @medusajs/framework/pg27 {28 pattern: /from\s+['"]pg['"]/g,29 // eslint-disable-next-line quotes30 replacement: 'from "@medusajs/framework/pg"',31 },32 // OpenTelemetry imports - replace @opentelemetry/instrumentation-pg, @opentelemetry/resources, 33 // @opentelemetry/sdk-node, and @opentelemetry/sdk-trace-node with @medusajs/framework/opentelemetry/{subpath}34 {35 pattern: /from\s+['"]@?opentelemetry\/(instrumentation-pg|resources|sdk-node|sdk-trace-node)['"]/g,36 // eslint-disable-next-line quotes37 replacement: 'from "@medusajs/framework/opentelemetry/$1"',38 },39 // MikroORM require statements - replace require('@?mikro-orm/{subpath}') with require('@medusajs/framework/mikro-orm/{subpath}')40 {41 pattern: /require\s*\(\s*['"]@?mikro-orm\/([^'"]+)['"]\s*\)/g,42 // eslint-disable-next-line quotes43 replacement: 'require("@medusajs/framework/mikro-orm/$1")',44 },45 // Awilix require statements - replace require('awilix') with require('@medusajs/framework/awilix')46 {47 pattern: /require\s*\(\s*['"]awilix['"]\s*\)/g,48 // eslint-disable-next-line quotes49 replacement: 'require("@medusajs/framework/awilix")',50 },51 // PG require statements - replace require('pg') with require('@medusajs/framework/pg')52 {53 pattern: /require\s*\(\s*['"]pg['"]\s*\)/g,54 // eslint-disable-next-line quotes55 replacement: 'require("@medusajs/framework/pg")',56 },57 // OpenTelemetry require statements - replace require('@opentelemetry/instrumentation-pg'), 58 // require('@opentelemetry/resources'), require('@opentelemetry/sdk-node'), and 59 // require('@opentelemetry/sdk-trace-node') with require('@medusajs/framework/opentelemetry/{subpath}')60 {61 pattern: /require\s*\(\s*['"]@?opentelemetry\/(instrumentation-pg|resources|sdk-node|sdk-trace-node)['"]\s*\)/g,62 // eslint-disable-next-line quotes63 replacement: 'require("@medusajs/framework/opentelemetry/$1")',64 },65]66 67function processFile(filePath) {68 try {69 const content = fs.readFileSync(filePath, "utf8")70 let modifiedContent = content71 let wasModified = false72 73 replacements.forEach(({ pattern, replacement }) => {74 const newContent = modifiedContent.replace(pattern, replacement)75 if (newContent !== modifiedContent) {76 wasModified = true77 modifiedContent = newContent78 }79 })80 81 if (wasModified) {82 fs.writeFileSync(filePath, modifiedContent)83 console.log(`✓ Updated: ${filePath}`)84 return true85 }86 87 return false88 } catch (error) {89 console.error(`✗ Error processing ${filePath}:`, error.message)90 return false91 }92}93 94function getTargetFiles() {95 try {96 // Get the current script's filename to exclude it from processing97 const currentScript = path.basename(__filename)98 99 // Find TypeScript/JavaScript files, excluding common directories that typically don't contain target imports100 const findCommand = `find . -name node_modules -prune -o -name .git -prune -o -name dist -prune -o -name build -prune -o -name coverage -prune -o -name "*.ts" -print -o -name "*.js" -print -o -name "*.tsx" -print -o -name "*.jsx" -print`101 const files = execSync(findCommand, {102 encoding: "utf8",103 maxBuffer: 50 * 1024 * 1024, // 50MB buffer104 })105 .split("\n")106 .filter((line) => line.trim())107 108 console.log(files)109 110 const targetFiles = []111 let processedCount = 0112 113 console.log(`📄 Scanning ${files.length} files for target imports and require statements...`)114 115 for (const file of files) {116 try {117 // Skip the current script file118 const fileName = path.basename(file)119 if (fileName === currentScript) {120 processedCount++121 continue122 }123 const content = fs.readFileSync(file, "utf8")124 if (125 /from\s+['"]@?mikro-orm\//.test(content) ||126 /from\s+['"]awilix['"]/.test(content) ||127 /from\s+['"]pg['"]/.test(content) ||128 /require\s*\(\s*['"]@?mikro-orm\//.test(content) ||129 /require\s*\(\s*['"]awilix['"]/.test(content) ||130 /require\s*\(\s*['"]pg['"]/.test(content)131 ) {132 targetFiles.push(file.startsWith("./") ? file.slice(2) : file)133 }134 processedCount++135 if (processedCount % 100 === 0) {136 process.stdout.write(137 `\r📄 Processed ${processedCount}/${files.length} files...`138 )139 }140 } catch (fileError) {141 // Skip files that can't be read142 continue143 }144 }145 146 if (processedCount > 0) {147 console.log(`\r📄 Processed ${processedCount} files. `)148 }149 150 return targetFiles151 } catch (error) {152 console.error("Error finding target files:", error.message)153 return []154 }155}156 157function main() {158 console.log("🔄 Finding files with target imports and require statements...")159 160 const targetFiles = getTargetFiles()161 162 if (targetFiles.length === 0) {163 console.log("ℹ️ No files found with target imports or require statements.")164 return165 }166 167 console.log(`📁 Found ${targetFiles.length} files to process`)168 169 let modifiedCount = 0170 let errorCount = 0171 172 targetFiles.forEach((filePath) => {173 const fullPath = path.resolve(filePath)174 if (fs.existsSync(fullPath)) {175 if (processFile(fullPath)) {176 modifiedCount++177 }178 } else {179 console.warn(`⚠️ File not found: ${filePath}`)180 errorCount++181 }182 })183 184 console.log("\n📊 Summary:")185 console.log(` Files processed: ${targetFiles.length}`)186 console.log(` Files modified: ${modifiedCount}`)187 console.log(` Errors: ${errorCount}`)188 189 if (modifiedCount > 0) {190 console.log("\n✅ Import replacement completed successfully!")191 console.log("\n💡 Next steps:")192 console.log(" 1. Review the changes with: git diff")193 console.log(" 2. Run your tests to ensure everything works correctly")194 console.log(" 3. Commit the changes if you're satisfied")195 } else {196 console.log(197 "\n✅ No modifications needed - all imports are already correct!"198 )199 }200}201 202// Run if called directly203if (require.main === module) {204 main()205}206 207module.exports = { processFile, getTargetFiles, main }
This script scans your project for files that import from mikro-orm/{subpath}
, awilix
, or pg
, and replaces those imports with their new equivalents from @medusajs/framework
. It handles both ES module import
statements and CommonJS require
statements in JavaScript and TypeScript files.
Next, run the following command in your terminal to make the script executable:
node
without changing permissions.Finally, execute the script with the following command:
This will scan your project files, apply the necessary import replacements, and provide a summary of the changes made.
Next Steps#
After running the codemod, review the changes made to your codebase. You can use git diff
to see the modifications. Additionally, run your tests to ensure everything works as expected.
If everything is working correctly, you can remove the replace-imports.js
file from your project. You can also remove the following packages from your package.json
, as they're now included in the @medusajs/framework
package:
@mikro-orm/*
packages (for example,@mikro-orm/core
,@mikro-orm/migrations
, etc.)awilix
pg
@opentelemetry/instrumentation-pg
@opentelemetry/resources
@opentelemetry/sdk-node
@opentelemetry/sdk-trace-node