swc-useclient icon indicating copy to clipboard operation
swc-useclient copied to clipboard

Plugin does not work from nextjs v14.1.2 and onwards

Open arnoBruynseels opened this issue 1 year ago • 11 comments

The plugin works perfectly on nextjs v14.1.1 but once you install nextjs v14.1.2 everything breaks.

Nextjs complains that I need to use the 'use client' directive for the external component library I added to the plugin config.

Any fixes?

arnoBruynseels avatar Mar 26 '24 13:03 arnoBruynseels

The plugin works perfectly on nextjs v14.1.1 but once you install nextjs v14.1.2 everything breaks.

Nextjs complains that I need to use the 'use client' directive for the external component library I added to the plugin config.

Any fixes?

I did not reproduce this issue in my repo used nextjsv14.1.4. Please provide your code to better reproduce the issue.

coder-xiaotian avatar Mar 26 '24 16:03 coder-xiaotian

The plugin works perfectly on nextjs v14.1.1 but once you install nextjs v14.1.2 everything breaks. Nextjs complains that I need to use the 'use client' directive for the external component library I added to the plugin config. Any fixes?

I did not reproduce this issue in my repo used nextjsv14.1.4. Please provide your code to better reproduce the issue.

In your provided repo you don't have any swcPlugins defined.

This is how we use the plugin:

image

arnoBruynseels avatar Mar 28 '24 15:03 arnoBruynseels

The plugin works perfectly on nextjs v14.1.1 but once you install nextjs v14.1.2 everything breaks. Nextjs complains that I need to use the 'use client' directive for the external component library I added to the plugin config. Any fixes?

I did not reproduce this issue in my repo used nextjsv14.1.4. Please provide your code to better reproduce the issue.

In your provided repo you don't have any swcPlugins defined.

This is how we use the plugin:

image

I can start success in my repo with antd. I think it's probably an issue with the directory structure of your component library. The export directory structure of antd is as follows: image I can't find @spot/ui-components in npm. Please provide more information about it. And you can provide the error logs.

coder-xiaotian avatar Mar 28 '24 15:03 coder-xiaotian

Hey, this no longer works with next 14.2.0, any chance of a fix for this? : failed to run Wasm plugin transform. Please ensure the version of swc_core used by the plugin is compatible with the host runtime.

tjones4701 avatar Apr 15 '24 22:04 tjones4701

Hey, this no longer works with next 14.2.0, any chance of a fix for this? : failed to run Wasm plugin transform. Please ensure the version of swc_core used by the plugin is compatible with the host runtime.

I get it. What is your OS version useing.

coder-xiaotian avatar Apr 16 '24 01:04 coder-xiaotian

Hey, this no longer works with next 14.2.0, any chance of a fix for this?嘿,这不再适用于下一个 14.2.0,有修复这个问题的机会吗? : failed to run Wasm plugin transform. Please ensure the version of swc_core used by the plugin is compatible with the host runtime.:无法运行 Wasm 插件转换。请确保插件使用的 swc_core 版本与主机运行时兼容。

I fixed it. You can update use-client to 1.2.0.

coder-xiaotian avatar Apr 16 '24 16:04 coder-xiaotian

Hi it's me again.

I'm trying to up our Nextjs version from 14.1.1 to to lastest (14.2.6).

Steps I took:

  1. Install Nextjs version 14.2.6
  2. Install use-client version 1.2.0
  3. Applied following config:

image

NOTE: all the @spot packages are private internal repositories. We create our own component library. This component library is build using React and Vite where all components are exported in an index.js file

Below is what it looks like:

image

  1. Run the Nextjs application
  2. Following error occurs:

image

Where does it go wrong? Did we implement or use this package wrong? Did we implement or expose our UI library the wrong way?

Please help, we can't seem to find the source of the problem.

Thanks!

arnoBruynseels avatar Aug 23 '24 13:08 arnoBruynseels

Hi it's me again.

I'm trying to up our Nextjs version from 14.1.1 to to lastest (14.2.6).

Steps I took:

  1. Install Nextjs version 14.2.6
  2. Install use-client version 1.2.0
  3. Applied following config:

image

NOTE: all the @spot packages are private internal repositories. We create our own component library. This component library is build using React and Vite where all components are exported in an index.js file

Below is what it looks like:

image

  1. Run the Nextjs application
  2. Following error occurs:

image

Where does it go wrong? Did we implement or use this package wrong? Did we implement or expose our UI library the wrong way?

Please help, we can't seem to find the source of the problem.

Thanks!

Please provide the [local]/users page code and this file code. image

coder-xiaotian avatar Aug 25 '24 14:08 coder-xiaotian

``> > Hi it's me again.

I'm trying to up our Nextjs version from 14.1.1 to to lastest (14.2.6). Steps I took:

  1. Install Nextjs version 14.2.6
  2. Install use-client version 1.2.0
  3. Applied following config:

image NOTE: all the @spot packages are private internal repositories. We create our own component library. This component library is build using React and Vite where all components are exported in an index.js file Below is what it looks like: image

  1. Run the Nextjs application
  2. Following error occurs:

image Where does it go wrong? Did we implement or use this package wrong? Did we implement or expose our UI library the wrong way? Please help, we can't seem to find the source of the problem. Thanks!

Please provide the [local]/users page code and this file code. image

[local]/users page code:

image

UserPageContent code:

image

'use client';

import { AgGrid, BooleanText, agGridCssClasses } from '@spot/ui-components';
import type { AgGridProps } from '@spot/ui-components';
import type { ICellRendererParams, RowClassParams } from 'ag-grid-community';
import { useTranslations } from 'next-intl';
import type { ReactNode } from 'react';
import { useCallback, useMemo } from 'react';
import { useState } from 'react';

import UserModalContent from '@/app/[locale]/users/_components/users-page-content/components/user-modal-content/UserModalContent';
import UserModalFooter from '@/app/[locale]/users/_components/users-page-content/components/user-modal-footer/UserModalFooter';
import type { UserModalContentTabKey } from '@/app/[locale]/users/_components/users-page-content/components/user-modal-header/UserModalHeader';
import UserModalHeader from '@/app/[locale]/users/_components/users-page-content/components/user-modal-header/UserModalHeader';
import type { User } from '@/generated/graphql/types';
import { useSelector } from '@/lib/redux/redux-store';
import { selectUsersData } from '@/lib/redux/slices/user-management/selectors';

const UsersPageContent = () => {
  const t = useTranslations();

  const users = useSelector(selectUsersData);

  const [modalActiveTabKey, setModalActiveTabKey] = useState<UserModalContentTabKey>('generalTab');

  const modalTitle = useCallback(
    (selectedRecord: unknown): ReactNode => {
      return (
        <UserModalHeader
          user={selectedRecord as User}
          selectedTabKey={modalActiveTabKey}
          setSelectedTabKey={setModalActiveTabKey}
        />
      );
    },
    [modalActiveTabKey],
  );

  const modalContent = useCallback(
    (selectedRecord: unknown): ReactNode => {
      return <UserModalContent user={selectedRecord as User} selectedTabKey={modalActiveTabKey} />;
    },
    [modalActiveTabKey],
  );

  const modalFooter = useCallback(
    (selectedRecord: unknown): ReactNode => {
      return <UserModalFooter isCopyVisible={modalActiveTabKey === 'debugTab'} user={selectedRecord as User} />;
    },
    [modalActiveTabKey],
  );

  const columnDefs: AgGridProps['columnDefs'] = useMemo(() => {
    return [
      {
        headerName: t('application-common.user'),
        children: [
          {
            field: 'user',
            headerName: t('application-common.userID'),
          },
          {
            field: 'firstName',
            headerName: t('application-common.firstName'),
          },
          {
            field: 'lastName',
            headerName: t('application-common.lastName'),
          },
          {
            field: 'email',
            headerName: t('application-common.email'),
          },
          {
            field: 'department',
            headerName: t('application-common.department'),
          },

          {
            field: 'organisation',
            headerName: t('application-common.organisation'),
          },
        ],
      },
      {
        headerName: t('application-common.privileges'),
        children: [
          {
            field: 'hasAccess',
            headerName: t('application-common.access'),
            cellRenderer: (params: ICellRendererParams) => {
              return (
                <BooleanText
                  value={params.value}
                  variant={params.value ? 'success' : 'danger'}
                  trueText="Normal"
                  falseText="Blocked"
                />
              );
            },
            filterValueGetter: (params) => {
              return params.data.hasAccess ? t('application-common.normal') : t('application-common.blocked');
            },
          },
          {
            field: 'isAdmin',
            headerName: t('application-common.admin'),
            cellRenderer: (params: ICellRendererParams) => {
              return <BooleanText value={params.value} variant={params.value ? 'success' : 'danger'} />;
            },
            filterValueGetter: (params) => {
              return params.data.isAdmin ? t('application-common.yes') : t('application-common.no');
            },
            minWidth: 115,
            columnGroupShow: 'open',
          },
          {
            field: 'isWyreUser',
            headerName: t('application-common.wyreUser'),
            cellRenderer: (params: ICellRendererParams) => {
              return <BooleanText value={params.value} variant={params.value ? 'success' : 'danger'} />;
            },
            filterValueGetter: (params) => {
              return params.data.isWyreUser ? t('application-common.yes') : t('application-common.no');
            },
            minWidth: 115,
            columnGroupShow: 'open',
          },
          {
            field: 'isBusinessExpert',
            headerName: t('application-common.businessExpert'),
            cellRenderer: (params: ICellRendererParams) => {
              return <BooleanText value={params.value} variant={params.value ? 'success' : 'danger'} />;
            },
            filterValueGetter: (params) => {
              return params.data.isBusinessExpert ? t('application-common.yes') : t('application-common.no');
            },
            minWidth: 115,
            columnGroupShow: 'open',
          },
        ],
      },
      {
        headerName: t('application-common.usage'),
        children: [
          {
            field: 'lastSeen',
            headerName: t('application-common.lastName'),
          },
          {
            field: 'firstSeen',
            headerName: t('application-common.firstSeen'),
            minWidth: 115,
            columnGroupShow: 'open',
          },
        ],
      },
    ];
  }, [t]);

  const getRowClass: AgGridProps['getRowClass'] = useCallback((params: RowClassParams) => {
    return params.data.hasAccess === false ? agGridCssClasses.DangerBackground : '';
  }, []);

  const defaultColDef: AgGridProps['defaultColDef'] = useMemo(() => {
    return {
      sortable: true,
      resizable: true,
      filter: true,
    };
  }, []);

  const onCloseLogic: AgGridProps['onCloseLogic'] = useCallback(() => {
    setModalActiveTabKey('generalTab');
  }, []);

  const dataViewModalConfig: AgGridProps['dataViewModalConfig'] = useMemo(() => {
    return {
      hasSlideTabs: true,
      title: modalTitle,
      customContent: modalContent,
      customFooter: modalFooter,
    };
  }, [modalContent, modalFooter, modalTitle]);

  const columnsState: AgGridProps['columnsState'] = useMemo(() => {
    return [
      {
        colId: 'lastSeen',
        sort: 'desc',
      },
    ];
  }, []);

  return (
    <AgGrid
      showRecordCount
      enableGlobalFilter
      enableDataViewModal
      rowData={users}
      columnDefs={columnDefs}
      getRowClass={getRowClass}
      defaultColDef={defaultColDef}
      onCloseLogic={onCloseLogic}
      dataViewModalConfig={dataViewModalConfig}
      columnsState={columnsState}
    />
  );
};

export default UsersPageContent;

TimestampRefreshButton code:

Transpiled & Compiled:

image

Actual Component code:

import type { ButtonProps } from '@lib/inputs/button/Button';
import Button from '@lib/inputs/button/Button';
import Icon from '@lib/utilities/icon/Icon';
import IntervalIndication from '@lib/utilities/timestamp-refresh-button/components/interval-indication/IntervalIndication';
import TimeIndication from '@lib/utilities/timestamp-refresh-button/components/time-indication/TimeIndication';
import clsx from 'clsx';
import { addWeeks, isAfter, isSameDay, isSameWeek, isTomorrow, isYesterday } from 'date-fns';
import type { Moment } from 'moment';
import type { CSSProperties, PropsWithChildren } from 'react';

import styles from './TimestampRefreshButton.module.css';

export interface TimestampRefreshButtonProps {
  /**
   * Optional styling
   */
  style?: CSSProperties;
  /**
   * Optional className
   */
  className?: string;
  /**
   * Optional buttonText
   */
  buttonText?: string;
  /**
   * Optional plainDateFormat
   */
  plainDateFormat?: boolean;
  /**
   * Optional intervalSeparator
   */
  intervalSeparator?: string;
  /**
   * Optional onClick
   */
  handleClick: ButtonProps['handleClick'];
  /**
   * Optional time
   */
  time?: Date | Moment | null;
  /**
   * Optional timeFormat
   */
  timeFormat?: string;
  /**
   * Optional timeTo
   */
  timeTo?: Date | Moment;
  /**
   * Optional timeToFormat
   */
  timeToFormat?: string;
}

/**
 * TimestampRefreshButton component
 */
const TimestampRefreshButton = ({
  style,
  className,
  buttonText = 'Refresh',
  plainDateFormat = false,
  intervalSeparator = ' ... ',
  handleClick,
  time = new Date(),
  timeFormat = 'yyyy-MM-dd HH:mm',
  timeTo,
  timeToFormat = 'yyyy-MM-dd HH:mm',
}: PropsWithChildren<TimestampRefreshButtonProps>) => {
  let validTime: Date | null = null;

  if (time) {
    validTime = time instanceof Date ? time : time.toDate();
  }

  const today = new Date();

  let validTimeTo = timeTo;

  let relativeTimeFormat = 'yyyy-MM-dd HH:mm';

  if (timeTo) {
    validTimeTo = timeTo instanceof Date ? timeTo : timeTo.toDate();
  }

  if (!plainDateFormat && validTime !== null) {
    if (isSameDay(today, validTime)) {
      relativeTimeFormat = "'Today' HH:mm";
    } else if (isTomorrow(validTime)) {
      relativeTimeFormat = "'Tomorrow' HH:mm";
    } else if (isSameWeek(addWeeks(today, 1), validTime)) {
      relativeTimeFormat = 'yyyy-MM-dd HH:mm';
    } else if (isSameWeek(addWeeks(today, -1), validTime)) {
      relativeTimeFormat = 'yyyy-MM-dd HH:mm';
    } else if (isYesterday(validTime)) {
      relativeTimeFormat = "'Yesterday' HH:mm";
    } else if (isAfter(validTime, addWeeks(today, 1))) {
      relativeTimeFormat = 'yyyy-MM-dd HH:mm';
    }
  }

  const renderTime = () => {
    if (validTime === null) {
      return null;
    }

    if (timeTo) {
      return (
        <IntervalIndication
          time={validTime}
          timeFormat={timeFormat}
          timeTo={validTimeTo as Date}
          timeToFormat={timeToFormat}
          intervalSeparator={intervalSeparator}
          plainDateFormat={plainDateFormat}
          relativeTimeFormat={relativeTimeFormat}
        />
      );
    }

    return (
      <TimeIndication
        time={validTime}
        timeFormat={timeFormat}
        plainDateFormat={plainDateFormat}
        relativeTimeFormat={relativeTimeFormat}
      />
    );
  };

  return (
    <div className={clsx(className, [styles.timestampRefreshButton])} style={style}>
      {renderTime()}
      <Button handleClick={handleClick} variant="success" size="extra-small" className={styles.refreshButton}>
        <Icon icon="cached" size={15} className={styles.refreshIcon} />
        {buttonText}
      </Button>
    </div>
  );
};

export default TimestampRefreshButton;

arnoBruynseels avatar Aug 26 '24 07:08 arnoBruynseels

@arnoBruynseels You can try these two ways.

  1. add "@lib/" to config like this include: ["@lib/", ...];
  2. you can change change the import way from @spot/ui-components, for example change import { AgGrid } from '@spot/ui-components' to import AgGrid from '@spot/ui-components/your-component-path'.

coder-xiaotian avatar Aug 27 '24 02:08 coder-xiaotian

@arnoBruynseels You can try these two ways.

  1. add "@lib/" to config like this include: ["@lib/", ...];
  2. you can change change the import way from @spot/ui-components, for example change import { AgGrid } from '@spot/ui-components' to import AgGrid from '@spot/ui-components/your-component-path'.

Thanks, I will try this and keep you posted!

arnoBruynseels avatar Aug 28 '24 13:08 arnoBruynseels