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

LATE_SETUP_CALL error with Next.js #12

Open
rstacruz opened this issue Apr 27, 2021 · 8 comments
Open

LATE_SETUP_CALL error with Next.js #12

rstacruz opened this issue Apr 27, 2021 · 8 comments

Comments

@rstacruz
Copy link

rstacruz commented Apr 27, 2021

Hi there! Great work with Twind... it's an amazing concept and I am really loving the API.

I tried integrating it with Next.js + Webpack 5 and got this error:

info  - ready on http://localhost:3000
Error: [LATE_SETUP_CALL] {}
    at withTwindDocument (/home/rsc/@dev/@rstacruz/til-2021/node_modules/@twind/next/document/document.cjs:38:27)
    at eval (webpack-internal:///./pages/_document.js:42:123)
    at Object../pages/_document.js (/home/rsc/@dev/@rstacruz/til-2021/.next/server/pages/_document.js:22:1)
    ...

A lot of times it's not really causing any issues (pages continue to work) but sometimes they cause Internal Server Error to show up for some pages.

package.json
  "dependencies": {
    "@twind/next": "^1.0.7",
    "next": "10.1.3",
    "next-mdx-remote": "^2.1.4",
    "twind": "^0.16.13",
  }
twind.config.js
import * as colors from 'twind/colors'
/** @type {import('twind').Configuration} */

export default {
  // mode: 'strict',
  theme: {
    extend: {
      borderColor: {
        line: colors.gray['200'],
      },
      textColor: {
        mute: colors.gray['600'],
        accent: colors.red['600'],
      },
    },
    fontFamily: {
      sans: ['Roboto', 'ui-sans', 'sans-serif'],
      serif: ['Eczar', 'ui-serif', 'Georgia', 'Cambria', 'sans-serif'],
    },
    maxWidth: {
      article: '580px',
    },
  },
}
pages/_document.js
import withTwindDocument from '@twind/next/document'
import twindConfig from '../twind.config'
export default withTwindDocument(twindConfig)
pages/_app.js
import twindConfig from '../twind.config'
import withTwindApp from '@twind/next/app'
import '../styles/globals.css'
import '@fontsource/eczar/400.css'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/700.css'
import { css, tw } from 'twind/css'

function MyApp({ Component, pageProps }) {
  // tw(
  //   css({
  //     ':global': { body: { '@apply': 'font-sans', },
  //     },
  //   })
  // )

  return <Component {...pageProps} />
}

export default withTwindApp(twindConfig, MyApp)
next.config.js
module.exports = {
  future: { webpack5: true },
}
@rstacruz rstacruz changed the title LATE_SETUPNext.js LATE_SETUP_CALL error with Next.js Apr 27, 2021
@Andrewnt219
Copy link

For me, when using Webpack 5, the style is not pre-rendered on the server (which is what the setup for).
A demo for this: issue https://codesandbox.io/s/magical-river-45guq?file=/pages/index.js

@sastan
Copy link
Contributor

sastan commented May 10, 2021

Sorry for not responding earlier. I try to look into this at the end of the week.

@rodrigojmr
Copy link

rodrigojmr commented May 19, 2021

Unsure if this is the right issue to share this on. I also have this happen to values of a custom theme I've made, but only on a build and thus in production, not in development. It results in breaking classes that have mentions to these custom theme values, and thus breaking other styles as well. This happens at random during refresh, and most often during the first load of the site for any given client, in desktop, mobile, pagespeedinsights, etc.

brave_rFKXzmUBOO
brave_y951KwrSCh

package.json
"dependencies": {
    "@twind/next": "^1.0.7",
    "@twind/react": "^0.0.4",
    "next": "^10.2.0",
    "twind": "^0.16.16",
  },
Theme
const config: Configuration = {
  preflight: (preflight, { theme }) => ({
    ...preflight,
    html: {
      "scroll-behavior": "smooth",
      "scroll-padding": "4.5rem",
    },

    "h1, h2, h3, h4, h5, h6": {
      fontFamily: "Montserrat",
    },
    body: {
      fontFamily: "Gotham",
    },
    // Modal background
    ".snipcart-modal__container, .snipcart-modal, .snipcart-cart-header": {
      backgroundColor: `${theme("colors.gray.100")} !important`,
    },
[etc...]
  prefix: true,
  theme: {
    extend: {
  fontFamily: { sans: ["Montserrat"], secondary: ["Gotham"] },
  colors: {
    ...colors,
    transparent: "transparent",
    current: "currentColor",
    gray: {
      "50": "#f7f9f8",
      "70": "#eff5f7",
      "100": "#ebf1f4",
      "200": "#d2dfe8",
      "300": "#a7becb",
      "400": "#7397a6",
      "500": "#587684",
      "600": "#475b66",
      "700": "#37444d",
      "800": "#262e36",
      "900": "#171c23",
    },
    red: colors.red,
    blue: colors.lightBlue,
    yellow: colors.amber,
    primary: {
      logo: "#446C52",
      saturated: "#67A147",
      dark: "#35542F",
      light: "#EDF2B0",
    },
    yellowLight: "#F4E9BD",
    blueLight: "#D3E6E9",
    lightBlueGrey: "#e9f1f2",
    bg: "#EDF7EA",
    newGreen: {
      "50": "#F5F7F2",
      "100": "#E8F0D9",
      "200": "#CAE5B0",
      "300": "#97C877",
      "400": "#72B162",
      "500": "#378B29",
      "600": "#2D731B",
      "700": "#275817",
      "800": "#1C3C13",
      "900": "#132411",
    },
   [more custom colors...]
  },
  container: {
    padding: {
      DEFAULT: "1rem",
      sm: "2rem",
      lg: "4rem",
      xl: "5rem",
      "2xl": "6rem",
    },
  },
  boxShadow: (theme) => ({
    highlighted: `0px 0px 2px 3px ${theme("colors.chocolate.200")}`,
  }),
  screens: { standalone: { raw: "(display-mode:standalone)" } },
};
  },
_document
import { FB_PIXEL_ID } from "@lib/fpixel";
import withTwindDocument from "@twind/next/shim/document";
import Document, { Head, Html, Main, NextScript } from "next/document";
import twindConfig from "../twind.config";

class CustomDocument extends Document {
  render() {
    return (
      <Html lang="pt">
        <Head>
          <link rel="preconnect" href="https://app.snipcart.com" />
          <link rel="preconnect" href="https://cdn.snipcart.com" />

          <link href="/static/fonts/style.css" rel="stylesheet" />
          <link href="/static/style.css" rel="stylesheet" />
        </Head>
        <body>
          <Main />
          <NextScript />
          <style
            dangerouslySetInnerHTML={{
              __html: ` </style>
                <link
                  rel="preload"
                  as="style"
                  onload="this.onload=null;this.rel='stylesheet'"
                  href="https://cdn.snipcart.com/themes/v3.0.31/default/snipcart.css"
                  crossOrigin="anonymous"
                />
              <style>`,
            }}
          ></style>
          <script
            crossOrigin="anonymous"
            async
            src="https://cdn.snipcart.com/themes/v3.0.31/default/snipcart.js"
          ></script>
          <script src="/static/snipcart.js"></script>
          <div
            hidden
            data-config-add-product-behavior="none"
            id="snipcart"
            data-api-key={process.env.SNIPCART_PUBLIC}
            data-config-modal-style="side"
            data-templates-url="/static/snipcart-templates.html"
          ></div>
        </body>
      </Html>
    );
  }
}

export default withTwindDocument(twindConfig, CustomDocument);
_app
import FBPixelProvider from "@components/FacebookPixel";
import Layout from "@components/layout";
import { useNif, useSnipcart } from "@lib/snipcart";
import { wrapper } from "@redux/store";
import withTwindApp from "@twind/next/shim/app";
import { DefaultSeo } from "next-seo";
import { AppProps } from "next/app";
import Head from "next/head";
import * as React from "react";
import { useEffect } from "react";
import SEO from "../next-seo.config";
import twindConfig from "../twind.config";

function MyApp({ Component, pageProps }: AppProps) {

  return (
    <FBPixelProvider>
      <Layout>
        <>
          <Head>
            <link
              rel="icon"
              type="image/png"
              sizes="32x32"
              href="/favicon-32x32.png"
            />
            <link
              rel="icon"
              type="image/png"
              sizes="16x16"
              href="/favicon-16x16.png"
            />
            <link
              rel="apple-touch-icon"
              sizes="180x180"
              href="/apple-touch-icon.png"
            />
            <link rel="manifest" href="/site.webmanifest" />
            <link
              rel="mask-icon"
              href="/safari-pinned-tab.svg"
              color="#5bbad5"
            />
            <meta name="theme-color" content="#ffffff" />
          </Head>
          <DefaultSeo {...SEO} />
          <Component {...pageProps} />
        </>
      </Layout>
    </FBPixelProvider>
  );
}

export default wrapper.withRedux(withTwindApp(twindConfig, MyApp));
next.config.js
const withPlugins = require("next-compose-plugins");
const withImages = require("next-images");
const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
});
const { withSentryConfig } = require("@sentry/nextjs");

const config = {
  future: {
    webpack5: true,
  },

  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack"],
    });

    return config;
  },
};

module.exports = withPlugins([[withBundleAnalyzer, withImages]], config);

@rschristian
Copy link
Member

rschristian commented Jul 2, 2021

I've been running into this too with WMR, so doesn't look to be tied to Next. (might be premature assumption, but does look to be tied to the lack of styles being prerendered)

Repo + instructions to reproduce: https://github.com/rschristian/twind-late-setup-call-bug

@moviezhou
Copy link

moviezhou commented Sep 20, 2021

Has anyone solved this problem? Please let me know, thx a lot.

@sastan
Copy link
Contributor

sastan commented Dec 30, 2021

As we run into some trouble with this and re-configuring twind during runtime I'm thinking about to allow setup calls anytime you want. That would require some internal changes and may only come v1.

@JLarky
Copy link

JLarky commented Jan 4, 2022

Is there a way to check if setup function has already run from the user point of view? :) for me it happens because of HMR and I store the flag on window :) kind of like:

if (!window.Twind) {
  setup({});
  window.Twind = true;
}

@JLarky
Copy link

JLarky commented Jan 4, 2022

Here's a more involved workaround:

const createTwind = () => {
  setup({
    theme: {
      extend: {
        colors: {
          "brand-red-500": "#FA3541",
        },
      },
    },
  });
  // in my code here I create some custom sheets
  return 123;
};

let singletonHolder: ReturnType<typeof createTwind> | undefined;

// call this function instead of `setup` itself
export function getTwind() {
  return singletonHolder || (singletonHolder = createTwind());
}

// my typescript is a bit misconfigured, you might not need this
declare global {
  interface NodeModule {
    hot?: {
      data: { singletonHolder?: typeof singletonHolder };
      dispose: (callback: (data: { singletonHolder?: typeof singletonHolder }) => void) => void;
    };
  }
}

if (module.hot) {
  // here I'm restoring my sheets, you can just check if twind is already initialized
  singletonHolder = module.hot?.data?.singletonHolder || singletonHolder;
  module.hot.dispose(data => {
    // I'm storing my stuff, you will just mark that initialization happened
    data.singletonHolder = singletonHolder;
  });
}

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

No branches or pull requests

7 participants