UNPKG

@spaced-out/ui-design-system

Version:
230 lines (206 loc) 6.62 kB
// .storybook/main.ts import {fileURLToPath} from 'node:url'; import {createRequire} from 'node:module'; import type {StorybookConfig} from '@storybook/react-webpack5'; import path, {dirname} from 'node:path'; import webpack from 'webpack'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const require = createRequire(import.meta.url); const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.tsx'], addons: [ '@storybook/addon-docs', '@storybook/addon-a11y', '@storybook/addon-webpack5-compiler-babel', '@chromatic-com/storybook', // NOTE: We handle CSS manually in webpackFinal to have full control ], babel: async (options: any) => ({ ...options, sourceType: 'unambiguous', presets: [ [ '@babel/preset-env', { targets: [ 'last 2 Chrome versions', 'last 2 Safari versions', 'last 2 Firefox versions', 'last 2 Edge versions', 'last 3 iOS Major versions', ], }, ], ['@babel/preset-react', {runtime: 'automatic', development: true}], '@babel/preset-typescript', ], plugins: ['macros', '@regrapes/babel-plugin-add-react-memo-displayname'], }), webpackFinal: async (cfg) => { const config: any = {...cfg}; // ---------- Resolve / aliases ---------- config.resolve ||= {}; config.resolve.alias = { ...(config.resolve.alias ?? {}), src: path.resolve(__dirname, '../src'), }; config.resolve.fallback = { ...(config.resolve.fallback ?? {}), process: require.resolve('process/browser'), }; config.module ||= {}; config.module.rules = config.module.rules || []; // ---------- CRITICAL: Remove Storybook's built-in CSS rules ---------- // This prevents conflicts with our custom CSS Module configuration config.module.rules = config.module.rules.filter((rule: any) => { if (!rule) return true; if (!rule.test) return true; const testStr = rule.test.toString(); // Remove any CSS-related rules (both .css and .scss/.sass) if (testStr.includes('css') || testStr.includes('scss') || testStr.includes('sass')) { return false; } return true; }); // ---------- Add our CSS Module rules ---------- // Rule 1: CSS Modules for *.module.css files config.module.rules.push({ test: /\.module\.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules: { // css-loader 7.x requires these options for `import css from` pattern namedExport: false, exportLocalsConvention: 'as-is', localIdentName: '[name]__[local]--[hash:base64:5]', }, }, }, { loader: require.resolve('postcss-loader'), options: { implementation: require.resolve('postcss'), }, }, ], }); // Rule 2: CSS Modules for styles/variables/*.css (design tokens with @value) config.module.rules.push({ test: /\.css$/, include: [path.resolve(__dirname, '../src/styles/variables')], use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules: { namedExport: false, exportLocalsConvention: 'as-is', localIdentName: '[name]__[local]--[hash:base64:5]', }, }, }, { loader: require.resolve('postcss-loader'), options: { implementation: require.resolve('postcss'), }, }, ], }); // Rule 3: Regular CSS (non-modules) - must come AFTER module rules config.module.rules.push({ test: /\.css$/, exclude: [ /\.module\.css$/, path.resolve(__dirname, '../src/styles/variables'), ], use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, modules: false, // Plain CSS, not modules }, }, { loader: require.resolve('postcss-loader'), options: { implementation: require.resolve('postcss'), }, }, ], }); // Rule 4: Sass/SCSS support config.module.rules.push({ test: /\.s[ac]ss$/i, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 2, modules: { auto: (resourcePath: string) => resourcePath.endsWith('.module.scss') || resourcePath.endsWith('.module.sass'), namedExport: false, exportLocalsConvention: 'as-is', localIdentName: '[name]__[local]--[hash:base64:5]', }, }, }, { loader: require.resolve('sass-loader'), options: { implementation: require.resolve('sass'), }, }, ], }); // ---------- Font rule tweak ---------- config.module.rules = config.module.rules.map((rule: any) => { if (rule?.test && rule.test.toString().includes('woff')) { return { ...rule, test: /\.(svg|ico|jpg|jpeg|png|gif|webp|cur|ani|pdf)(\?.*)?$/, }; } return rule; }); // ---------- TS/TSX via babel-loader ---------- config.module.rules.push({ test: /\.(ts|tsx)$/, loader: require.resolve('babel-loader'), options: { presets: [['react-app', {flow: false, typescript: true}]], plugins: ['@regrapes/babel-plugin-add-react-memo-displayname'], }, }); // ---------- Plugins ---------- config.plugins = [ ...(config.plugins ?? []), new webpack.ProvidePlugin({ process: 'process/browser', }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify( process.env.NODE_ENV ?? 'development', ), }), ]; (config.resolve.extensions as string[]).push('.ts', '.tsx'); return config; }, staticDirs: ['public', '../design-tokens'], framework: {name: '@storybook/react-webpack5', options: {}}, typescript: {reactDocgen: 'react-docgen-typescript'}, }; export default config;