label-studio-frontend icon indicating copy to clipboard operation
label-studio-frontend copied to clipboard

Error while importing package in Next.js

Open Rugz007 opened this issue 3 years ago • 11 comments

Hey I tried using this npm package in my next.js project and am getting error:

Automatic publicPath is not supported in this browser

This is my code:

import React from "react";

import LabelStudio from '@heartexlabs/label-studio'

export default function App() {
  return (
    <div className="App">
      <LabelStudioWrapper />
    </div>
  );
}

const LabelStudioWrapper = (props) => {
  // we need a reference to a DOM node here so LSF knows where to render
  const rootRef = React.useRef();
  // this reference will be populated when LSF initialized and can be used somewhere else
  const lsfRef = React.useRef();
  // we're running an effect on component mount and rendering LSF inside rootRef node
  React.useEffect(() => {
    if (rootRef.current) {
      lsfRef.current = new LabelStudio(rootRef.current, {
        /* all the options according to the docs */
        config: `
        <View style="padding: 25px;
               box-shadow: 2px 2px 8px #AAA">
    <Header value="Label the image with polygons"/>
    <Image name="img" value="https://app.heartex.ai/static/samples/sample.jpg"/>
    <Text name="text1"
          value="Select label, start to click on image"/>

    <PolygonLabels name="tag" toName="img">
      <Label value="Airbus" background="blue"/>
      <Label value="Boeing" background="red"/>
    </PolygonLabels>
  </View>
  `,
      });
    }
  }, []);
  // just a wrapper node to place LSF into
  return <div className="label-studio-root" ref={rootRef}></div>;
}

Rugz007 avatar Feb 18 '22 07:02 Rugz007

hi, @Rugz007

do you receive an error on a build phase or in the runtume? we actually never faced it before, so it needs further investigation

nicholasrq avatar Feb 22 '22 06:02 nicholasrq

Hey there, I am actually facing the same issue while trying to run tests with React testing library.

Automatic publicPath is not supported in this browser

It looks like an issue with Webpack but I do not have more info than this!

giorgosera avatar Mar 13 '22 16:03 giorgosera

Is there any news? Facing same issue right now, while running tests with Jest

AleshaOleg avatar Sep 15 '22 15:09 AleshaOleg

Facing the same issue on Nuxt2 and Nuxt3.

carloseam94 avatar Jan 17 '23 12:01 carloseam94

we've been receiving similar reports about Next.js lately. unfortunately, this integration currently appears to be broken without a known workaround

we're currently experiencing two different issues:

  • inability to import LS into existing Next.js applications (probably due to webpack/package.json setup)
  • official LS + React integration instruction results in an empty screen

the team is currently exploring possible solutions to these issues. we'll keep you posted

sorry for the inconvenience

nicholasrq avatar Feb 03 '23 13:02 nicholasrq

Running into the same problem here (with Nuxt 3). From what I've read I think it's worth considering setting webpack's output publicPath to an empty string or a string consisting of a space. I don't know if there is a reason for not setting this value. (similar issues: https://github.com/cypress-io/cypress/issues/18435, https://stackoverflow.com/a/64715069)

As such, what I attempted: webpack.config-builder.js:

  // ...
  const result = {
    filename: "[name]-[contenthash].js",
    chunkFilename: "[name]-[contenthash]-[id].chunk.js",
    publicPath: '' // added
  };
  // ...

However, I am running into problems when attempting to building the bundle and using it in my project. It would be nice if someone could validate this.

eensander avatar Feb 28 '23 14:02 eensander

@nicholasrq regarding this issue:

official LS + React integration instruction results in an empty screen

I was able to modify the official instruction to get it to work. Here is the working code:

import LabelStudio from '@heartexlabs/label-studio';
import '@heartexlabs/label-studio/build/static/css/main.css';
import React, { useEffect, useRef } from 'react';

const lsContainerId = 'label-studio';

/**
 * Turns a `LabelStudio` front-end instance into a React component. Sources:
 *  - https://react.dev/reference/react/useEffect#controlling-a-non-react-widget
 *  - https://labelstud.io/guide/frontend.html#React-integration
 */
export const ReactLabelStudio = (options) => {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const lsRef = useRef<LabelStudio>();

  useEffect(() => {
    if (!lsRef.current) {
      lsRef.current = new LabelStudio(lsContainerId, options);
    }
  }, [options]);

  return <div id={lsContainerId} ref={containerRef} />;
};

However, regarding this issue:

inability to import LS into existing Next.js applications (probably due to webpack/package.json setup)

I am also experiencing this error, but when trying to test the above component using react-testing-library (RTL). RTL uses jsdom, and jsdom doesn't have a document.currentScript property, which Webpack uses to power the "Automatic publicPath" feature.

epeters3 avatar Mar 22 '23 18:03 epeters3

Using the inspector, it seems that the error is thrown from the following:

var scriptUrl;
if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
var document = __webpack_require__.g.document;
if (!scriptUrl && document) {
	if (document.currentScript)
		scriptUrl = document.currentScript.src
	if (!scriptUrl) {
		var scripts = document.getElementsByTagName("script");
		if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
	}
}
// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");

As a very hacky workaround, without altering the browsers document.currentScript, I create a new script tag with a non-empty/non-falsy src, which will (hopefully) be the last result of getElementsByTagName("script"):

const script = document.createElement('script');
script.src = "data:text/javascript,void(0);";
document.body.appendChild(script);

For me this seems to work. However, hopefully it will be fixed in the future with a more definite solution.

eensander avatar Sep 27 '23 10:09 eensander

Using the inspector, it seems that the error is thrown from the following:

var scriptUrl;
if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
var document = __webpack_require__.g.document;
if (!scriptUrl && document) {
	if (document.currentScript)
		scriptUrl = document.currentScript.src
	if (!scriptUrl) {
		var scripts = document.getElementsByTagName("script");
		if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
	}
}
// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");

As a very hacky workaround, without altering the browsers document.currentScript, I create a new script tag with a non-empty/non-falsy src, which will (hopefully) be the last result of getElementsByTagName("script"):

const script = document.createElement('script');
script.src = "data:text/javascript,void(0);";
document.body.appendChild(script);

For me this seems to work. However, hopefully it will be fixed in the future with a more definite solution.

@eensander Sorry to bother, but may I know where do you add the three lines of codes in? Because everytime I refresh the page, it will trigger the "Automatic publicPath is not supported in this browser" error, and the document is still undefined. Thanks in advance for your help! image image

richardcoder avatar Mar 18 '24 09:03 richardcoder

Running into the same problem here (with Nuxt 3). From what I've read I think it's worth considering setting webpack's output publicPath to an empty string or a string consisting of a space. I don't know if there is a reason for not setting this value. (similar issues: cypress-io/cypress#18435, https://stackoverflow.com/a/64715069)

As such, what I attempted: webpack.config-builder.js:

  // ...
  const result = {
    filename: "[name]-[contenthash].js",
    chunkFilename: "[name]-[contenthash]-[id].chunk.js",
    publicPath: '' // added
  };
  // ...

However, I am running into problems when attempting to building the bundle and using it in my project. It would be nice if someone could validate this. @eensander I also tried this solution, and build a new bundle. But it seems not to work. Thanks. image

richardcoder avatar Mar 18 '24 09:03 richardcoder

Here is the solution I tried, and I think it works. It is not about the script or automatic public path, it is about Server Side Render not having access to document and window. Therefore, you have to use client-side render. To achieve this, it is a bit tricky, as Label Studio is a class constructor/function, not a React Component. Here is how I have done it on Next js.

First you must have 2 file.

  1. index.js
  2. LFS.js

In LFS.js, you import "Label Studio", just like what you would do in React

import LabelStudio from '@heartexlabs/label-studio'; import 'label-studio/build/static/css/main.css';

// Initialize Label Studio as usual...

In index.js

import dynamic from 'next/dynamic'
 
const AnnotateMain = dynamic(() => import('./LFS.js'), {
  ssr: false,
})

const index = () => {
  return (
    <div className=' max-w-screen-xl flex flex-col h-full  mx-auto  '>
        <AnnotateMain/>
    </div>
  )
}

export default index

In this way, you import the LFS.js in client side render, therefore the Label Studio can behave like in react. Hope this help *wink @richardcoder

Aiden1024 avatar Mar 19 '24 03:03 Aiden1024