storybookjs / frontpage

🌐 The website for storybook.js.org
https://storybook.js.org
MIT License
212 stars 116 forks source link

[sass]: ModuleParseError: Module parse failed: Unexpected character '@' #607

Closed well1791 closed 8 months ago

well1791 commented 8 months ago

Details

Recipe Page: sass Recipe Template: sass

Browser: Arc: Version 1.13.1 (42579) Chromium Engine Version 118.0.5993.88

What is wrong

Seems like this solution is not "magical", I believe the site should consider these scenarios and provide alternatives to the problem.

Screenshots

image

Solution

Please consider this answer, as simple as that.

npm i -D @storybook/preset-scss css-loader sass-loader style-loader

and this config

/// .storybook/main.js
const config: StorybookConfig = {
  // ...
  addons: [
    // ...
    '@storybook/preset-scss',
ShaunEvening commented 8 months ago

Hey @well1791 👋

@storybook/preset-scss is deprecated and no longer maintained. Can you please give me more details about your project to see how we can help improve our recipe for your use case.

well1791 commented 8 months ago

ooooooooooohhh!! that's not good.. Thanks for replying @Integrayshaun.

I guess I can start by providing context of my status, stack and config files later. So, currently I'm upgrading an old system progressively, so, you'll notice outdated packages. The stack I'm trying to integrate with storybook is:

Having said that, I'll share my config files:

package.json

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.tsx",
  "scripts": {
    "start": "webpack serve --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js",
    "prepare": "husky install",
    "test": "jest --watch",
    "test:e2e": "cypress run",
    "test:e2e-gui": "cypress open",
    "lint": "eslint src --quiet --fix",
    "format": "prettier src --fix --write",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/**/*.{js,ts,tsx}": [
      "npx eslint --quiet --fix"
    ]
  },
  "keywords": [],
  "private": "true",
  "devDependencies": {
    "@babel/preset-env": "^7.23.2",
    "@babel/preset-react": "^7.22.15",
    "@babel/preset-typescript": "^7.23.2",
    "@storybook/addon-essentials": "^7.5.1",
    "@storybook/addon-interactions": "^7.5.1",
    "@storybook/addon-links": "^7.5.1",
    "@storybook/addon-onboarding": "^1.0.8",
    "@storybook/addon-styling-webpack": "^0.0.5",
    "@storybook/blocks": "^7.5.1",
    "@storybook/preset-scss": "^1.0.3",
    "@storybook/react": "^7.5.1",
    "@storybook/react-webpack5": "^7.5.1",
    "@storybook/testing-library": "^0.2.2",
    "@svgr/webpack": "^6.5.1",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "12.0.0",
    "@testing-library/user-event": "^14.4.3",
    "@trivago/prettier-plugin-sort-imports": "^4.1.1",
    "@types/date-and-time": "^0.13.0",
    "@types/fabric": "^5.3.0",
    "@types/lodash": "^4.14.172",
    "@types/lodash.debounce": "^4.0.7",
    "@types/react": "^18.0.35",
    "@types/react-dom": "^18.0.11",
    "@types/react-gtm-module": "^2.0.1",
    "@types/react-redux": "^7.1.25",
    "@types/react-router-dom": "^5.3.3",
    "@types/redux-logger": "^3.0.9",
    "@types/styled-components": "^5.1.29",
    "@types/vec2": "^1.6.2",
    "@typescript-eslint/eslint-plugin": "^5.5.0",
    "@typescript-eslint/parser": "^5.5.0",
    "case-sensitive-paths-webpack-plugin": "^2.4.0",
    "copy-webpack-plugin": "^9.0.1",
    "css-loader": "^6.8.1",
    "cypress": "^12.17.4",
    "date-and-time": "^2.1.0",
    "esbuild": "^0.15.15",
    "esbuild-loader": "^2.20.0",
    "eslint": "^8.51.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-config-react-app": "7.0.1",
    "eslint-plugin-prettier": "^5.0.1",
    "eslint-plugin-react": "^7.33.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-sonarjs": "^0.21.0",
    "eslint-plugin-storybook": "^0.6.15",
    "eslint-plugin-unused-imports": "^2.0.0",
    "html-webpack-plugin": "^5.3.2",
    "husky": "^8.0.0",
    "jest": "^27.1.0",
    "less": "^4.1.1",
    "less-loader": "^10.0.1",
    "libphonenumber-js": "^1.10.14",
    "lint-staged": "^13.2.2",
    "node-sass-variables": "^0.0.5",
    "path": "^0.12.7",
    "prettier": "^3.0.3",
    "react-hotjar": "^5.2.0",
    "sass-loader": "^12.6.0",
    "source-map-loader": "^3.0.0",
    "storybook": "^7.5.1",
    "style-loader": "^3.3.3",
    "terser-webpack-plugin": "^5.3.3",
    "ts-jest": "^27.0.5",
    "ts-node": "^10.9.1",
    "tsconfig-paths-webpack-plugin": "^3.5.1",
    "typescript": "^4.4.2",
    "webpack": "^5.51.1",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.1.0"
  },
  "dependencies": {
    "@ant-design/compatible": "^5.1.2",
    "@ant-design/icons": "^4.6.4",
    "@emotion/react": "^11.10.6",
    "@emotion/styled": "^11.10.6",
    "@fvilers/disable-react-devtools": "^1.3.0",
    "@mui/material": "^5.12.0",
    "@reduxjs/toolkit": "^1.9.1",
    "antd": "^5.10.0",
    "axios": "^0.27.2",
    "classnames": "^2.3.1",
    "fabric": "^5.3.0",
    "html-react-parser": "^3.0.9",
    "i18next": "^20.5.0",
    "i18next-http-backend": "^1.3.0",
    "lodash": "^4.17.21",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-gtm-module": "^2.0.11",
    "react-i18next": "^11.11.4",
    "react-redux": "^8.0.5",
    "react-resize-detector": "^7.1.2",
    "react-router-dom": "^6.17.0",
    "redux-logger": "^3.0.6",
    "rxjs": "7.3.0",
    "styled-components": "^6.1.0",
    "vec2": "^1.6.1"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "src",
    "noEmit": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noFallthroughCasesInSwitch": true,
    "paths": {
      "@common/*": ["common/*"],
      "@components/*": ["components/*"],
      "@core/*": ["core/*"],
      "@local-api/*": ["api/local-api/*"],
      "@redux/*": ["redux/*"],
      "~atoms/*": ["UI/components/atoms/*"],
      "~molecules/*": ["UI/components/molecules/*"],
      "~organisms/*": ["UI/components/organisms/*"],
      "~templates/*": ["UI/components/templates/*"],
      "~pages/*": ["UI/pages/*"],
      "~router": ["router.tsx"],
      "~theme/*": ["theme/*"]
    },
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strict": true,
    "target": "ES6",
    "typeRoots": ["node_modules/@types", "core/typings"]
  },
  "include": [
    "src",
    "src/core/types/svg-types.d.ts",
    "src/core/types/scss-types.d"
  ]
}

webpack.dev.js

const path = require('path');
const common = require('./webpack.common');

const { merge } = require('webpack-merge');

const devOptions = {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    compress: true,
    hot: true,
    port: 4200,
    static: path.resolve(__dirname, 'dist'),
    historyApiFallback: true,
  },
};

module.exports = (env) =>
  merge(
    common,
    devOptions,
  );

webpack.common.js

const path = require('path');
const { ANTD_THEME_PATH } = require('./build/build-constants');
const { getVariablesFromSassFile } = require('./build/build-utils');

const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

const themePath = path.join(__dirname, ANTD_THEME_PATH);
const themeVariables = getVariablesFromSassFile(themePath);

const { convertLegacyToken } = require('@ant-design/compatible/lib');
const { theme } = require('antd/lib');

const { defaultAlgorithm, defaultSeed } = theme;

const mapToken = defaultAlgorithm(defaultSeed);
const v4Token = convertLegacyToken(mapToken);

const styleLoader = { loader: 'style-loader' };
const cssLoader = {
  loader: 'css-loader',
};
const lessLoader = 'less-loader';
const sassLoader = {
  loader: 'sass-loader',
  options: {
    additionalData: '@import "./src/theme/main";',
  },
};

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.bundle.[contenthash].js',
    publicPath: '/',
    clean: true,
  },
  resolve: {
    // loads the 'paths' property from the 'tsconfig.json` file
    plugins: [new TsconfigPathsPlugin()],
    extensions: ['.tsx', '.ts', '.js'],
    alias: {
      '@ee-theme': path.resolve(__dirname, 'src/theme'),
    },
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        enforce: 'pre',
        use: ['source-map-loader'],
      },
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'esbuild-loader',
        options: {
          loader: 'tsx',
          target: 'es2015',
          tsconfigRaw: require('./tsconfig.json'),
          sourcemap: true,
        },
      },
      {
        test: /\.(css|scss)$/,
        exclude: /\.module\.(css|scss)$/,
        use: [styleLoader, cssLoader, sassLoader],
      },
      {
        test: /\.module\.(css|scss)$/,
        use: [
          styleLoader,
          {
            ...cssLoader,
            options: {
              modules: true,
              sourceMap: true,
            },
          },
          sassLoader,
        ],
      },
      {
        test: /\.less$/,
        use: [
          styleLoader,
          cssLoader,
          {
            loader: lessLoader,
            options: {
              lessOptions: {
                javascriptEnabled: true,
                modifyVars: {
                  ...v4Token,
                  ...themeVariables,
                },
              },
            },
          },
        ],
      },
      {
        test: /\.(jpg|jpeg|png|gif)$/,
        type: 'asset/resource',
      },
      {
        test: /\.svg$/,
        use: ['@svgr/webpack'],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'src', 'index.html'),
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public/**/*',
          globOptions: {
            dot: true,
            gitignore: true,
            ignore: ['**/file.*', '**/ignored-directory/**'],
          },
        },
        {
          from: 'errors/**/*',
          globOptions: {
            dot: true,
            gitignore: true,
            ignore: ['**/file.*', '**/ignored-directory/**'],
          },
        },
      ],
    }),
    new CaseSensitivePathsPlugin(),
  ],
};

.storybook/main.ts

import type { StorybookConfig } from '@storybook/react-webpack5';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  staticDirs: ['../'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-onboarding',
    '@storybook/addon-interactions',
    '@storybook/preset-scss',
  ],
  framework: {
    name: '@storybook/react-webpack5',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};

export default config;

Not sure if that's too much unnecessary information or if it helps, however, that's what makes storybook work fine, however, I'm unable to "merge" my webpack.dev.js with storybook yet, I guess if I'm able to do so, there won't be any need of using the styling addon right?

ShaunEvening commented 8 months ago

Hey @well1791 👋

woah!!! Our configuration addons are made to handle most common use cases, but what you have here is much more advanced.

I would recommend a few things to get you moving in the right direction.

  1. Remove @storybook/preset-scss and @storybook/addon-styling-webpack from your project entirely as they have other dependencies that might conflict with what you have for Webpack.
  2. Merging the rules, plugins, and alias resolving from webpack.common.js into Storybook's Webpack using a webpackFinal function in your .storybook/main.ts.

If you're still stuck after that, please reach out on the Storybook Discord and we can set up time for a call to help you out.

well1791 commented 8 months ago

Since I'll opt for webpackFinal, I'll close this issue for now, if anything pops up, I'll write back here.