Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve React compatibility #579

Closed
jlarmstrongiv opened this issue Mar 2, 2022 · 8 comments
Closed

Improve React compatibility #579

jlarmstrongiv opened this issue Mar 2, 2022 · 8 comments
Assignees

Comments

@jlarmstrongiv
Copy link

jlarmstrongiv commented Mar 2, 2022

I have seen multiple issues and blog posts on how to use tsup with react. Often, I see:

  • the --inject option for react imports
  • adding the --external react option

Unfortunately, solving the ReferenceError: React is not defined error only leads to the Unhandled Runtime Error and Error: Invalid hook call..

I can’t seem to figure out how to use tsup with react. I’d love a workaround, though ideally tsup would support react without any configuration necessary.

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@jlarmstrongiv
Copy link
Author

jlarmstrongiv commented Mar 2, 2022

I am using npm link with tsup. Specially, I am using turborepo and npm workspaces with nextjs.

Luckily, found a solution:

tsup.config.ts

import fs from "fs";
import path from "path";
import { readPackageSync, NormalizeOptions } from "read-pkg";
import { defineConfig } from "tsup";

export default defineConfig((options) => {
  const source = getPkgSource();

  const inject: string[] = [];
  const injectReactImport = detectAndInjectReactImport(source);
  if (injectReactImport) inject.push(injectReactImport);

  return {
    ...options,
    entry: [source],
    // react external https://github.com/vercel/turborepo/issues/360#issuecomment-1013885148
    external: ["react"],
    inject,
    format: ["esm", "cjs", "iife"],
    silent: !options.watch,
    minify: !options.watch,
    incremental: !options.watch,
    dts: true,
    sourcemap: true,
    clean: false,
    splitting: false,
  };
});

export function getPkgSource(options?: NormalizeOptions | undefined) {
  const pkg = readPackageSync(options);

  if (typeof pkg.source !== "string") {
    throw new Error(
      "Please define a source https://github.com/developit/microbundle"
    );
  } else {
    return pkg.source;
  }
}

export function detectAndInjectReactImport(source: string): string | void {
  // ignore non-react packages
  if (!source.endsWith(".tsx")) {
    return;
  }

  const file = `// NOTE: This file should not be edited
// see @configs/tsup for implementation.
// - https://esbuild.github.io/content-types/#auto-import-for-jsx
// - https://github.com/egoist/tsup/issues/390#issuecomment-933488738

import * as React from "react";

export { React };
`;

  const relativefilePath = "./inject-react-import.js";
  const absoluteFilePath = path.join(process.cwd(), relativefilePath);

  if (!fs.existsSync(absoluteFilePath)) {
    fs.writeFileSync(absoluteFilePath, file);
  }

  return relativefilePath;
}

next.config.js
Funny enough, the config.resolve.alias order matters.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias["react/jsx-dev-runtime"] = require.resolve(
      "react/jsx-dev-runtime.js"
    );
    config.resolve.alias["react/jsx-runtime"] = require.resolve(
      "react/jsx-runtime.js"
    );
    config.resolve.alias["react"] = require.resolve("react");

    // Important: return the modified config
    return config;
  },
};

module.exports = nextConfig;

P.S.
You should be using peerDependencies for react.

@egoist
Copy link
Owner

egoist commented Mar 2, 2022

Unfortunately, solving the ReferenceError: React is not defined error only leads to the Unhandled Runtime Error and Error: Invalid hook call..

regarding this, I'd like to see a minimal repo so I can understand what's going on

@jlarmstrongiv
Copy link
Author

@egoist sent you an email subject tsup: minimal repo with a minimal example. Hope it helps!

@jlarmstrongiv
Copy link
Author

For react 18, please note facebook/create-react-app#11769 and instead use:

      config.resolve.alias["react/jsx-dev-runtime"] = require.resolve(
        "react/jsx-dev-runtime",
      );
      config.resolve.alias["react/jsx-runtime"] =
        require.resolve("react/jsx-runtime");
      config.resolve.alias["react"] = require.resolve("react");

@elrumordelaluz
Copy link

elrumordelaluz commented Aug 26, 2022

Here

Unfortunately, solving the ReferenceError: React is not defined error only leads to the Unhandled Runtime Error and Error: Invalid hook call..

regarding this, I'd like to see a minimal repo so I can understand what's going on

Here is a repo with this problem, any insight? thanks! 🙏🏼

@jlarmstrongiv
Copy link
Author

jlarmstrongiv commented Nov 27, 2022

Related #715 (comment)

@devinrhode2
Copy link

@jlarmstrongiv When researching zero-config ts+react bundlers, this issue title initially turned me away from tsup. However, it sounds like the originally may be largely solved? I'm not totally sure. Could the issue title be made more specific? Maybe you could add a small summary to the top? Maybe this issue could be closed altogether?

gabro added a commit to buildo/bento-design-system that referenced this issue Mar 17, 2023
@jlarmstrongiv
Copy link
Author

jlarmstrongiv commented Mar 18, 2023

@devinrhode2 many of the issues have been fixed! React support has improved a lot 🎉 For example, tsup now supports jsxImportSource for automatically injecting React (in most cases, except #792), nextjs has been patched, peer dependencies such as react and react-dom are now automatically excluded from the bundle, and more.

For future readers, other issues I have workarounds for are:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

No branches or pull requests

4 participants