medusajs / medusa

The world's most flexible commerce platform.
https://medusajs.com
MIT License
25.92k stars 2.6k forks source link

Docs: Plugin development troubleshooting #1945

Closed richardwardza closed 2 years ago

richardwardza commented 2 years ago

Preliminary Checks

Issue Summary

When developing a medusa plugin and using the npm link method described in the documentation, the user may still end up with the following when starting the medusa backend:

 > npm run develop

> medusa-starter-default@0.0.1 develop
> medusa develop

Successfully compiled 0 files with Babel (6ms).
[medusa-config] ⚠️ redis_url not found. A fake redis instance will be used.
info:    Using fake Redis
✔ Models initialized – 25ms
✔ Plugin models initialized – 6ms
✔ Repositories initialized – 87ms
✔ Database initialized – 78ms
✔ Strategies initialized – 15ms
✔ Services initialized – 228ms
✔ Express intialized – 7ms
⠋ Initializing pluginserror:   The class must be a valid service implementation, please check /Users/richard/Development/play/medusa-village/medusa-woo/services/my.js
Error: The class must be a valid service implementation, please check /Users/richard/Development/play/medusa-village/medusa-woo/services/my.js
    at /Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:344:51
    at step (/Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:33:23)
    at Object.next (/Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:14:53)
    at /Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:4:12)
    at /Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:331:87
    at Array.map (<anonymous>)
    at /Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:331:60
    at step (/Users/richard/Development/play/medusa-village/backend/node_modules/@medusajs/medusa/dist/loaders/plugins.js:33:23)
⠼ Initializing plugins

This appears to be caused by the plugin having a node_modules/medusa-interfaces installation and when the plugin is linked to the backend, the structure ends up as follows: medusa-backend/node_modules/your-plugin/node_modules/medusa-interfaces

When the backend starts it does ( src/loaders/plugins.ts ):

 if (
        !(loaded.prototype instanceof LegacyBaseService) &&
        !(loaded.prototype instanceof BaseService)
      ) {
        const logger = container.resolve<Logger>("logger")
        const message = `The class must be a valid service implementation, please check ${fn}`
        logger.error(message)
        throw new Error(message)
      }

BaseService is being read from medusa-backend/node_modules/medusa-interfaces while the plugin is extending from medusa-backend/node_modules/your-plugin/node_modules/medusa-interfaces. This makes the loaded.prototype not an instance of BaseService.

There is a draft PR to fix this (by allowing --preserve-symlinks) here: https://github.com/medusajs/medusa/pull/1860 but I couldn't get the --preserve-symlinks flag to work.

How can this issue be resolved?

Link the medusa-interfaces from the backend into the plugin being developed:

  1. cd /your/medusa-backend/node_modules/medusa-interfaces
  2. npm link
  3. cd /your/plugin/directory
  4. npm link medusa-interfaces
  5. npm link
  6. cd /your/medusa-backend
  7. npm link your-plugin

Now, from your-plugin directory you can run npm run watch and from your backend you can run npm run develop.

Changes from the plugin will be visible in the backend whenever the backend reloads.

Are you interested in working on this issue?

ripvannwinkler commented 1 year ago

I'm developing a plugin inside a yarn v3 monorepo and the prescribed yarn link method doesn't work here. The scenario described seems to fit my problem - backend/node_modules/@myscope/myplugin/node_modules/medusa-interfaces is linked from the plugin inheritance chain which causes instanceof AbstractFileService to return false, but I'm not finding a way to resolve this using the new yarn. Any suggestions?

const { LocalFileService } = require('@myscope/medusa-file-local');
const { AbstractFileService: fs1 } = require('@myscope/medusa-file-local/node_modules/@medusajs/medusa');
const { AbstractFileService: fs2 } = require('@medusajs/medusa');

const ts = new LocalFileService({}, {});
const isFs1 = ts instanceof fs1;
const isFs2 = ts instanceof fs2;

console.log('isfs1', isFs1); // true
console.log('isfs2', isFs2); // false

Edit: I found the solution for yarn v2+

yarn add @medusajs/medusa@link:<PATH_TO_BACKEND>/node_modules/@medusajs/medusa
yarn add medusa-interfaces@link:<PATH_TO_BACKEND>/node_modules/medusa-interfaces

Caveat: running node with --preserve-symlinks breaks this. I'm not sure how to avoid that.