albertogasparin / react-magnetic-di

Dependency injection and replacement for React components and hooks
MIT License
131 stars 7 forks source link

Error: `Invalid di(...) arguments: must be called with plain identifiers` #94

Open ratasorin opened 3 weeks ago

ratasorin commented 3 weeks ago

Hello,

I am trying to setup this amazing library to work in my storybook + React (Typescript) application, but when I run the stories I get an error: Invalid di(...) arguments: must be called with plain identifiers. coming from multiple typescript files: email.reducer.ts, history.reducer.ts, manage.reducer.ts, user.ts, etc. The error points to the use of di:

For example, in InactiveSessionNotify/index.tsx, the error is:

SyntaxError: /home/sorin_rata/ui/src/components/Notifications/InactiveSessionNotify/index.tsx: Invalid di(...) arguments: must be called with plain identifiers. 
   6 |   notificationText
   7 | }) => {
>  8 |   const [_createPortal] = _di([createPortal], InactiveSessionNotify);
     |                           ^^^
   9 |   if (notificationText !== "") return _createPortal(<div className="inactive-session">
  10 |         <span className="inactive-body">
  11 |           {notificationText}
    at buildCodeFrameError (/home/sorin_rata/ui/node_modules/@babel/core/src/transformation/file/file.ts:247:12)
    at buildCodeFrameError (/home/sorin_rata/ui/node_modules/@babel/traverse/src/path/index.ts:140:21)
    at Object.isValidArgument (/home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/utils.js:24:17)
    at /home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/processor-di.js:39:12
    at Array.forEach (<anonymous>)
    at processReference (/home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/processor-di.js:38:322)
    at PluginPass.Function (/home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/index.js:171:9)

But my component only does:

import { createPortal } from "react-dom";
import * as React from "react";
import "./index.scss"

const InactiveSessionNotify: React.FC<{ notificationText: string }> = ({
  notificationText,
}) => {
  if (notificationText !== "")
    return createPortal(
      <div className="inactive-session">
        <span className="inactive-body">
          {notificationText}
        </span>
      </div>,
      document.body,
    );
  else return null;
};

export default InactiveSessionNotify;

I should mention that some of the code in my repository has circular dependencies that are a bit too complex to solve now, but this component is not part of them, so I don't understand what causes the error...

My babel config in storybook main.ts is:

  babel: async (config, { configType }) => {

    if (configType === "DEVELOPMENT") {
      const reactMagneticSetup = ["react-magnetic-di/babel-plugin", {
        exclude: [/stories/, ".storybook"],
      }];

      if (config.plugins) config.plugins.push(reactMagneticSetup);
      else config.plugins = [reactMagneticSetup];

      if(config.presets) config.presets.push("@babel/preset-typescript");
      else config.presets = ["@babel/preset-typescript"];
    }

    return config;
  },
ratasorin commented 3 weeks ago

This component fails as well:

import React from 'react';
import PhoneCallbackIcon from '@mui/icons-material/PhoneCallback';
import InteractionSessionTime from '../Metrics/InteractionSessionTime';
import Popup from '../UI/Popup';

export const AgentInCallPopup: React.FC<{ startCallTime: number }> = ({ startCallTime }) => {
    return <Popup id="in-call-popup__container">
        <span className="in-call-popup__title">In call</span>
            <div className={"in-call-header-popup"}>
                <div className={"in-call-icons"}>
                    <PhoneCallbackIcon color="error" />
                </div>
                <InteractionSessionTime
                    time={(Math.ceil((new Date().getTime() - startCallTime) / 1000)) + 1} />
            </div>
    </Popup>
}

The error:

yntaxError: /home/sorin_rata/ui/src/components/Popups/agent-in-call.tsx: Invalid di(...) arguments: must be called with plain identifiers. 
   9 |   startCallTime
  10 | }) => {
> 11 |   const [_InteractionSessionTime, _PhoneCallbackIcon, _Popup] = _di([InteractionSessionTime, PhoneCallbackIcon, Popup], AgentInCallPopup);
     |                                                                 ^^^
  12 |   return <_Popup id="in-call-popup__container">
  13 |         <span className="in-call-popup__title">In call</span>
  14 |             <div className={"in-call-header-popup"}>
    at buildCodeFrameError (/home/sorin_rata/ui/node_modules/@babel/core/src/transformation/file/file.ts:247:12)
    at buildCodeFrameError (/home/sorin_rata/ui/node_modules/@babel/traverse/src/path/index.ts:140:21)
    at Object.isValidArgument (/home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/utils.js:24:17)
    at /home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/processor-di.js:39:12
    at Array.forEach (<anonymous>)
    at processReference (/home/sorin_rata/ui/node_modules/react-magnetic-di/lib/cjs/babel/processor-di.js:38:322)
ratasorin commented 3 weeks ago

I created a demo repository that contains a minimal reproduction of the issue: https://github.com/ratasorin/react-magnetic-di-error

albertogasparin commented 3 weeks ago

Thanks for providing the repo! Seems like the problem is with magnetic-di babel plugin running twice. Somehow that babel() function is called for both for storybook and dev contexts on shared files, and that duplicated transformation is not supported (the error message should be better, I'll see what I can do there).

I could not find a decent way to only add it once, aside from checking parent._name which seems fragile. However, using a dedicated babel file (eg .babelrc) and setting the config there, removing the babel() function in storybook config, works:

{
  "plugins": [
    ["react-magnetic-di/babel-plugin", {}]
  ],
  "presets": ["@babel/preset-typescript"]
}
ratasorin commented 3 weeks ago

Sweet, I like the proposed fix as I can definetly implement it in the existing codebase! Thank you a lot for this project! It has really come in clutch for me ✨