uiwjs / react-color

🎨 Is a tiny color picker widget component for React apps.
https://uiwjs.github.io/react-color
MIT License
274 stars 92 forks source link

Uncaught TypeError: Cannot redefine property: render #104

Closed indapublic closed 6 months ago

indapublic commented 1 year ago
Uncaught TypeError: Cannot redefine property: render
    at Function.defineProperty (<anonymous>)
    at <unknown> (file:///..../app/node_modules/@uiw/react-color/cjs/index.js:188:10)

NextJS (tried to turn off SSR, checked for window variable etc) - no difference. Do you have any advises here? Thank you in advance

jaywcjlove commented 1 year ago

@indapublic

https://github.com/uiwjs/next-remove-imports

jaywcjlove commented 1 year ago

@indapublic

victoriaSh commented 1 year ago

I've attempted to use the 'next-remove-imports', but I'm still getting 'Cannot redefine property: render' error. Am I doing something incorrectly?

My next.config.js:

const { withSentryConfig } = require('@sentry/nextjs');
const removeImports = require("next-remove-imports")();
const withPlugins = require('next-compose-plugins');

let config = {
  reactStrictMode: true,
  experimental: { esmExternals: true },
  eslint: {
    // Warning: This allows production builds to successfully complete even if
    // your project has ESLint errors.
    ignoreDuringBuilds: true,
  },
};

module.exports = withPlugins([
    removeImports,
    [ withSentryConfig,
        { silent: true ,
        hideSourcemaps: true },
    ]
], config);
jaywcjlove commented 1 year ago

@victoriaSh

// next.config.js
const removeImports = require('next-remove-imports')();

module.exports = (phase, { defaultConfig }) => {
  return removeImports({
    ...defaultConfig
  });
};
// next.config.js
const removeImports = require('next-remove-imports')({
  test: /node_modules([\s\S]*?)\.(tsx|ts|js|mjs|jsx)$/,
  matchImports: "\\.(less|css|scss|sass|styl)$"
});

module.exports = removeImports({
  webpack(config, options) {
    return config
  },
});
// next.config.js
const removeImports = require('next-remove-imports')()
module.exports = removeImports({
  webpack(config, options) {
    return config
  },
});
fantoine commented 11 months ago

Hi there, it looks like the same error occurs on Jest test suites ran on a pure React project (no NextJS involved here).

I use the commonJS bundle and tracked the error down to @uiw/react-color-colorful lib import. In the built source (in @uiw/react-color/cjs/index.js file), it happens here (line 188):

image

I'm not sure that the next-remove-imports lib is a solution in this case. Do you have any other solution in mind ?

Thanks !

fantoine commented 11 months ago

OK, I found a solution by myself.

If other people get the same issue, that's what I did:

SimplySayHi commented 8 months ago

I have a NextJS project and faced the same issue. Just found out that importing components from @uiw/react-color/src/index, solved the issue.

For example: import { Circle } from "@uiw/react-color/src/index";

Importing components from @uiw/react-color/esm raised another error.

clarkmcc commented 8 months ago

Had this same issue when using Astro. Changing imports to @uiw/react-color/src/index worked

import { Sketch } from "@uiw/react-color/src/index";
dangreaves commented 7 months ago

Just to clarify, this problem is nothing to do with Next.js or Jest. The problem is that the CommonJS build is broken, so anything which imports the CommonJS build will get this error.

This can be confirmed by creating the following simple CommonJS file, and seeing the error.

const reactColor = require("@uiw/react-color");
console.log(reactColor);
// Uncaught TypeError: Cannot redefine property: render

The reason this breaks is because in the package export, every sub package is exported with export * from "foo". The problem with that is multiple packages define a render export.

When this compiles to CommonJS, it ends up like this. Each of these Object.keys blocks is looping over all the exports from the sub package, and attaching them to exports using Object.defineProperty.

var _reactColorSwatch = _interopRequireWildcard(require("@uiw/react-color-swatch"));
Object.keys(_reactColorSwatch).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  if (key in exports && exports[key] === _reactColorSwatch[key]) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function get() {
      return _reactColorSwatch[key];
    }
  });
});
var _reactColorWheel = _interopRequireWildcard(require("@uiw/react-color-wheel"));
Object.keys(_reactColorWheel).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  if (key in exports && exports[key] === _reactColorWheel[key]) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function get() {
      return _reactColorWheel[key];
    }
  });
});

You cannot use Object.defineProperty to redefine an existing property. So the first block defines a render property on exports, and then subsequent packages try to set the same property, causing the error.

The solution is to avoid exporting sub packages like this, or at least being careful that each sub package defines unique exports. I have added a PR (#140) which addresses this.

As a super hacky fix, I have added a patch to my project which adds the configurable option to each Object.defineProperty in the CommonJS file. This allows subsequent packages to overwrite the render export without the error, but it would be preferable if this package did not add duplicate exports at all.

var _reactColorWheel = _interopRequireWildcard(require("@uiw/react-color-wheel"));
Object.keys(_reactColorWheel).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  if (key in exports && exports[key] === _reactColorWheel[key]) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
+   configurable: true,
    get: function get() {
      return _reactColorWheel[key];
    }
  });
});