storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
83.93k stars 9.21k forks source link

[Bug]: next/font/local is not worked in storybook with vanilla Extract #26845

Open Zero-1016 opened 4 months ago

Zero-1016 commented 4 months ago

Describe the bug

I'm going to proceed with the project using NextJS and Storybook. When I try to apply font using localfont, SyntaxError occurs. There seems to be no problem when excluding 'predentardClassname', so that part seems to be the problematic code.

image

but, This problem is not visible when vanilla extract is removed.

To Reproduce

https://github.com/Zero-1016/storybook-playground

System

System:
    OS: Windows 11 10.0.22621
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
  Binaries:
    Node: 20.12.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - C:\Program Files\nodejs\yarn.CMD
    npm: 10.5.0 - C:\Program Files\nodejs\npm.CMD <----- active
    pnpm: 8.6.6 - C:\Program Files\nodejs\pnpm.CMD
  Browsers:
    Edge: Chromium (123.0.2420.97)
  npmPackages:
    @storybook/addon-essentials: ^8.0.8 => 8.0.8
    @storybook/addon-interactions: ^8.0.8 => 8.0.8
    @storybook/addon-links: ^8.0.8 => 8.0.8
    @storybook/addon-onboarding: ^8.0.8 => 8.0.8
    @storybook/blocks: ^8.0.8 => 8.0.8
    @storybook/nextjs: ^8.0.8 => 8.0.8
    @storybook/react: ^8.0.8 => 8.0.8
    @storybook/test: ^8.0.8 => 8.0.8
    @storybook/testing-library: ^0.2.2 => 0.2.2
    eslint-plugin-storybook: ^0.8.0 => 0.8.0
    storybook: ^8.0.8 => 8.0.8

Additional context

No response

valentinpalkovic commented 4 months ago

Hi @Zero-1016

it seems the issue got resolved. Can you quickly elaborate on what the issue/fix was?

Zero-1016 commented 4 months ago

😺Hi @valentinpalkovic

I believe the issue arose from using vanilla extract, but unfortunately, I couldn't find a solution. When I excluded it, everything seemed fine. However, I couldn't determine if the problem was with the vanilla extract, the storybook, or something related to fonts. It's likely due to my lack of expertise. I apologize for the inconvenience.

simonmcallister0210 commented 4 months ago

I have the same issue. Using NextJS and Vanilla Extract. NextJS works fine with local font, but Storybook crashes with this:

SyntaxError
node_modules/.pnpm/css-loader@6.11.0_webpack@5.90.3/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[7].use[1]!../../node_modules/.pnpm/postcss-loader@7.3.4_postcss@8.4.27_typescript@5.1.6_webpack@5.90.3/node_modules/postcss-loader/dist/cjs.js!../../node_modules/.pnpm/@storybook+nextjs@8.0.9_esbuild@0.19.12_next@14.0.4_react-dom@18.2.0_react@18.2.0_typescript@5.1.6_webpack@5.90.3/node_modules/@storybook/nextjs/dist/font/webpack/loader/storybook-nextjs-font-loader.js!../../node_modules/.pnpm/next@14.0.4_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/font/local/target.css?{"path":"apps/mojo.website/src/app/font.ts","import":"","arguments":[{"src":[{"path":"../../public/fonts/TT_Wellingtons_Black.otf","weight":"900"},{"path":"../../public/fonts/TT_Wellingtons_ExtraBold.otf","weight":"800"},{"path":"../../public/fonts/TT_Wellingtons_Bold.otf","weight":"700"},{"path":"../../public/fonts/TT_Wellingtons_DemiBold.otf","weight":"600"},{"path":"../../public/fonts/TT_Wellingtons_Medium.otf","weight":"500"},{"path":"../../public/fonts/TT_Wellingtons_Regular.otf","weight":"400"},{"path":"../../public/fonts/TT_Wellingtons_Light.otf","weight":"300"}],"fallback":["Arial","sans-serif"],"variable":"--font-wellington"}],"variableName":"Wellington"} 

(46:10) /private/tmp/my.mojo.monorepo/node_modules/.pnpm/next@14.0.4_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/font/local/target.css Unknown word

  44 |           
  45 |           
> 46 |         }`;
     |          ^
  47 |       const style = document.createElement('style');
  48 |       style.setAttribute('id', 'font-face-font-d8119c');

It seems to crash if I reference localFont in any preview or story. For example, this works fine:

// .storybook/preview.tsx

import React from "react"
import type { Preview } from "@storybook/react"

const preview: Preview = {
  decorators: [
    Story => (
      <div className={""}>
        <Story />
      </div>
    )
]
}

export default preview

... but this crashes as soon as I reference localFont, even when I'm not using this font ...

// .storybook/preview.tsx

import React from "react"
import type { Preview } from "@storybook/react"
import localFont from "next/font/local"

export const Wellington = localFont({
  src: [
    {
      path: "../../public/fonts/TT_Wellingtons_Black.otf",
      weight: "900",
    },
    // . . . snip . . .
    {
      path: "../../public/fonts/TT_Wellingtons_Light.otf",
      weight: "300",
    },
  ],
  fallback: ["Arial", "sans-serif"],
  variable: "--font-wellington",
})

const preview: Preview = {
  decorators: [
    Story => (
      <div className={""}>
        <Story />
      </div>
    )
  ]
}

export default preview

Here's my .storybook/main.ts file, using the Vanilla Extract recipe here

import type { StorybookConfig } from "@storybook/nextjs"

import { VanillaExtractPlugin } from "@vanilla-extract/webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";

import { join, dirname } from "path"

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value: string): any {
  return dirname(require.resolve(join(value, "package.json")))
}
const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
  addons: [
    getAbsolutePath("@storybook/addon-onboarding"),
    getAbsolutePath("@storybook/addon-links"),
    getAbsolutePath("@storybook/addon-essentials"),
    getAbsolutePath("@chromatic-com/storybook"),
    getAbsolutePath("@storybook/addon-interactions"),
  ],
  framework: {
    name: getAbsolutePath("@storybook/nextjs"),
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
  staticDirs: [
    // { from: "../public", to: "public" },
    { from: "../public", to: "public" },
    { from: "../public/fonts", to: "public/fonts" }
  ],
  webpackFinal(config, _) {
    // Add Vanilla-Extract and MiniCssExtract Plugins
    config.plugins?.push(
      new VanillaExtractPlugin(),
      new MiniCssExtractPlugin()
    );

    // Exclude vanilla extract files from regular css processing
    config.module?.rules?.forEach((rule) => {
      if (
        typeof rule !== "string" &&
        rule.test instanceof RegExp &&
        rule.test.test("test.css")
      ) {
        rule.exclude = /\.vanilla\.css$/i;
      }
    });

    config.module?.rules?.push({
      test: /\.vanilla\.css$/i, // Targets only CSS files generated by vanilla-extract
      use: [
        MiniCssExtractPlugin.loader,
        {
          loader: require.resolve("css-loader"),
          options: {
            url: false, // Required as image imports should be handled via JS/TS import statements
          },
        },
      ],
    });

    return config;
  },
}
export default config
brgndyy commented 1 month ago

I also get the same error.

So I posted a post in storybook discussion

This is the full text of the article.

Summary

Hello, I am currently using Next.js14 version.

I applied the localFont application method as written in the official document, but the error as shown in the picture continues to occur.

Is the path wrong? Even though I followed the same path as the official document, an error occurred and I don't know where the error is.

Currently, the font is applied normally within the application, but the error occurs only in storybook.

스크린샷 2024-07-26 오전 7 22 49

src/components/fonts/font.ts

import localFont from 'next/font/local';

export const myFont = localFont({
  src: './BMHANNAAir_ttf.ttf',
  display: 'swap',
});
스크린샷 2024-07-26 오전 7 25 22 스크린샷 2024-07-26 오전 7 27 46

Additional information

- main.ts

import type { StorybookConfig } from '@storybook/nextjs';
import type { PresetValue } from '@storybook/types';
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import path from 'path';

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-onboarding',
    '@storybook/addon-interactions',
    '@chromatic-com/storybook',
    '@storybook/addon-styling-webpack',
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },

  docs: {},
  typescript: {
    reactDocgen: 'react-docgen-typescript',
  } as PresetValue<Partial<import('@storybook/types').TypescriptOptions> | undefined>,

// This is the path written in the official document.
  staticDirs: [
    {
      from: '../src/components/fonts',
      to: 'src/components/fonts',
    },
  ],

  webpackFinal(config, options) {
    // Add Vanilla-Extract and MiniCssExtract Plugins
    config.plugins?.push(new VanillaExtractPlugin(), new MiniCssExtractPlugin());

    // Exclude vanilla extract files from regular css processing
    config.module?.rules?.forEach((rule) => {
      if (
        rule &&
        typeof rule !== 'string' &&
        rule.test instanceof RegExp &&
        rule.test.test('test.css')
      ) {
        rule.exclude = /\.vanilla\.css$/i;
      }
    });

    config.module?.rules?.push({
      test: /\.vanilla\.css$/i, // Targets only CSS files generated by vanilla-extract
      use: [
        MiniCssExtractPlugin.loader,
        {
          loader: require.resolve('css-loader'),
          options: {
            url: false, // Required as image imports should be handled via JS/TS import statements
          },
        },
      ],
    });

    config.module?.rules?.push({
      test: /\.(ttf|woff|woff2|eot|otf)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/',
          },
        },
      ],
    });

    config.resolve = config.resolve || {};
    config.resolve.alias = config.resolve.alias || {};

    config.resolve.alias['@'] = path.resolve(__dirname, '../src');

    return config;
  },
};

export default config;

- preview.tsx

import type { Preview } from '@storybook/react';
import { fn } from '@storybook/test';
import React from 'react';
import { myFont } from '../src/components/fonts/font';

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
  globalTypes: {
    action: fn,
  },
};

export const decorators = [
  (Story) => (
    <div className={myFont.className}>
      <Story />
    </div>
  ),
];

export default preview;
스크린샷 2024-07-26 오전 7 29 38 스크린샷 2024-07-26 오전 7 30 10