PostHog / posthog-js

Send usage data from your web app or site to PostHog, with autocapture.
https://posthog.com/docs/libraries/js
Other
295 stars 123 forks source link

missing `pkg.exports` in package.json #908

Open tgds opened 11 months ago

tgds commented 11 months ago

It is not possible to use posthog-js package within an ESM project in Node environment, because the package.json is missing the exports property (https://publint.dev/posthog-js@1.92.1) while Node doesn't read the module property and thus it fails to treat it as an ES package.

You get the following error if you try:

import { PostHogProvider } from "posthog-js/react/dist/esm/index.js";
         ^^^^^^^^^^^^^^^
SyntaxError: Named export 'PostHogProvider' not found. The requested module 'posthog-js/react/dist/esm/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'posthog-js/react/dist/esm/index.js';
const { PostHogProvider } = pkg;

This is within a Remix Vite project, but I guess it's relevant for any ESM project that runs SSR in Node and doesn't prebundle dependencies.

I had to apply the following patch to get it working:

diff --git a/dist/es.js b/dist/es.mjs
similarity index 100%
rename from dist/es.js
rename to dist/es.mjs
diff --git a/dist/es.js.map b/dist/es.mjs.map
similarity index 100%
rename from dist/es.js.map
rename to dist/es.mjs.map
diff --git a/package.json b/package.json
index 4d35188aea0eed88a85d9c9c3a140ea10f578034..c10df882ec27ca97a29c814735f8ea53ad847dc2 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,18 @@
     },
     "main": "dist/module.js",
     "module": "dist/es.js",
+    "exports": {
+        ".": {
+            "types": "./dist/module.d.ts",
+            "import": "./dist/es.mjs",
+            "require": "./dist/module.js"
+        },
+        "./react": {
+            "types": "./react/dist/types/index.d.ts",
+            "import": "./react/dist/esm/index.mjs",
+            "require": "./react/dist/umd/index.js"
+        }
+    },
     "types": "dist/module.d.ts",
     "files": [
         "lib/*",
diff --git a/react/README.md b/react/README.md
deleted file mode 100644
index 6d2737d54f58c39c85eea2afcfb6dd583686aef9..0000000000000000000000000000000000000000
diff --git a/react/dist/esm/index.js b/react/dist/esm/index.mjs
similarity index 100%
rename from react/dist/esm/index.js
rename to react/dist/esm/index.mjs
diff --git a/react/package.json b/react/package.json
index f313bf7c1e42c7d43be66390329013fd342558a0..8932551985ed093f2f2ab25ee48cdb0543b97a71 100644
--- a/react/package.json
+++ b/react/package.json
@@ -21,6 +21,13 @@
     },
     "main": "dist/umd/index.js",
     "module": "dist/esm/index.js",
+    "exports": {
+        ".": {
+            "types": "./dist/types/index.d.ts",
+            "import": "./dist/esm/index.mjs",
+            "require": "./dist/umd/index.js"
+        }
+    },
     "types": "dist/types",
     "files": [
         "dist/*",
meza commented 6 months ago

Bumping this as it's still an issue

tgds commented 6 months ago

@benjackwhite 🙏

matt-hernandez commented 4 months ago

We are currently experiencing this issue as well when we upgraded our Remix project to Vite

dancrumb commented 2 months ago

I used yarn patch to workaround this issue locally.

In short, I added an exports field to the main package file and renamed the ESM files from js to mjs.

This seems to have done the trick.

posthog-js-npm-1.157.2-d8e62a83b4.patch

I do think that this should be a relatively straightforward fix... it's just a case of aligning your package file with the ESM standards as documented in the Node JS docs.

However, I will not that creating a hybrid package (i.e. ESM and CJS) is a huge PITA, but using .cjs and .mjs and including the appropriate exports data should do it.

I'd offer a PR, but I haven't used Rollup, so I'm probably not the best person to do it.

matt-hernandez commented 2 months ago

@dancrumb That isn’t far off from what we’re doing at my job. There’s a little bit of extra work we have to do to make it work on our npm workspaces codebase. The main problem is that our local fix could break in a later version of posthog, and we try to keep everything pretty up to date in our app.

ekojsalim commented 2 months ago

Encountering the same issue with posthog-js/react and Remix + Vite. Since I'm not using the feature flag feature, I found it simpler to just reimplement PostHogProvider and usePostHog. That should be more 'future-proof'.

matt-hernandez commented 2 months ago

@ekojsalim Can you post a code snippet to illustrate your solution?

ekojsalim commented 2 months ago

@ekojsalim Can you post a code snippet to illustrate your solution?

Something like this:

import { atom, useAtomValue, useSetAtom } from "jotai";
import posthog from "posthog-js";
import { useEffect } from "react";

const postHogAtom = atom<typeof posthog | undefined>(undefined);

const PostHogProvider = ({
    children,
    options,
    apiKey,
}: { children: React.ReactNode; options: any; apiKey: string }) => {
    const setPostHogInstance = useSetAtom(postHogAtom);

    useEffect(() => {
        const posthogInstance = posthog.init(apiKey, options);
        setPostHogInstance(posthogInstance);
    }, [apiKey, options, setPostHogInstance]);

    return children;
};

const usePostHog = () => useAtomValue(postHogAtom);

export { PostHogProvider, usePostHog };

We use jotai instead of more usual context/provider scheme so it might be a bit different. Baiscally, it's just taking the existing PostHogProvider and usePostHog and reimplementing it in the internal codebase. Importing posthog-js is fine but posthog-js/react breaks.

zwhitchcox commented 1 month ago

Having this issue too. Took down our site first time tried to install post hog. Vite is very popular bundler. Would be nice to have support.

How is this issue almost a year old?? They even showed you how to fix it in the second reply

zwhitchcox commented 1 month ago

Anyway, here's my solution. It's just ekojsalim's without the jotai dependency:

import posthog from 'posthog-js'
import React, { createContext, useContext, useEffect, useState } from 'react'

type PostHogType = typeof posthog | undefined

const PostHogContext = createContext<PostHogType>(undefined)

interface PostHogProviderProps {
    children: React.ReactNode
    options?: any
    apiKey?: string
}

const PostHogProvider: React.FC<PostHogProviderProps> = ({
    children,
    options,
    apiKey,
}) => {
    const [posthogInstance, setPosthogInstance] = useState<PostHogType>(undefined)

    useEffect(() => {
        if (apiKey) {
            const instance = posthog.init(apiKey, options)
            setPosthogInstance(instance)
        } else {
            // If apiKey is undefined, ensure PostHog instance is not set
            setPosthogInstance(undefined)
        }
    }, [apiKey, options])

    return (
        <PostHogContext.Provider value={posthogInstance}>
            {children}
        </PostHogContext.Provider>
    )
}

const usePostHog = () => useContext(PostHogContext)

export { PostHogProvider, usePostHog }