griffel icon indicating copy to clipboard operation
griffel copied to clipboard

Css extraction issue with transpiled code

Open pksorensen opened this issue 1 year ago • 5 comments

I have used my project (quickform-core) in another project and used css extraction without problems. But in a new project i am just hitting my head against the wall for hours now not able to figure out why.

my next.config.js


import { env } from "process";

import { withGriffelCSSExtraction } from '@griffel/next-extraction-plugin';

/** @type {import('next').NextConfig} */
const nextConfig =  {
    output: env.NODE_ENV === "production" ? 'standalone' : undefined,
    compress: false,
    experimental: {
        instrumentationHook: true,
        swcPlugins: [['fluentui-next-appdir-directive', { paths: ['@griffel', '@fluentui', '@eavfw'] }]],
    },
    transpilePackages: ["@fluentui/react-components", '@eavfw/quickform-core', '@eavfw/quickform-input-select', '@eavfw/query'],
 

    webpack(config) {
        config.module.rules.unshift({
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: [
                {
                    loader: '@griffel/webpack-loader',
                },
            ],
        });

        // If your project uses TypeScript
        config.module.rules.unshift({
            test: /\.(ts|tsx)$/,
            exclude: /node_modules/,
            use: [
                {
                    loader: '@griffel/webpack-loader',
                    options: {
                        babelOptions: {
                            presets: [
                                "next/babel"
                            ]
                        },
                    },
                },
            ],
        });
       
        return config;
    },
     
};

export default withGriffelCSSExtraction()(nextConfig);

and i am getting the below error. I tried to identify the difference between my working project and the new project, but so far they are very similar.

Any hint to help me find the root cause would be nice.

@eavfw\quickform-core is basically a bunch of typescritp files distributed by npm

./src/components/quickform/GroupOnlineQuickform.tsx
C:\dev\go\qp\node_modules\@eavfw\quickform-core\src\index.ts:1
(function (exports) { export * from "./components";
                      ^^^^^^

SyntaxError: Unexpected token 'export'
    at new Script (node:vm:117:7)
    at Module.evaluate (C:\dev\go\qp\node_modules\@linaria\babel-preset\lib\module.js:280:20)
    at require.Object.assign.ensure (C:\dev\go\qp\node_modules\@linaria\babel-preset\lib\module.js:214:13)
    at C:\dev\go\qp\apps\GroupOnline.QuotationPlatformHost\src\components\quickform-container\goTokens.tsx:4:22
    at C:\dev\go\qp\apps\GroupOnline.QuotationPlatformHost\src\components\quickform-container\goTokens.tsx:17:3
    at Script.runInContext (node:vm:149:12)
    at Module.evaluate (C:\dev\go\qp\node_modules\@linaria\babel-preset\lib\module.js:283:12)
    at require.Object.assign.ensure (C:\dev\go\qp\node_modules\@linaria\babel-preset\lib\module.js:214:13)
    at C:\dev\go\qp\apps\GroupOnline.QuotationPlatformHost\src\components\quickform\GroupOnlineQuickform.tsx:3:17
    at C:\dev\go\qp\apps\GroupOnline.QuotationPlatformHost\src\components\quickform\GroupOnlineQuickform.tsx:40:3
    at Script.runInContext (node:vm:149:12)
    at Module.evaluate (C:\dev\go\qp\node_modules\@linaria\babel-preset\lib\module.js:283:12)
    at evaluate (C:\dev\go\qp\node_modules\@griffel\babel-preset\src\utils\evaluatePathsInVM.js:22:9)
    at evaluatePathsInVM (C:\dev\go\qp\node_modules\@griffel\babel-preset\src\utils\evaluatePathsInVM.js:80:21)
    at evaluatePaths (C:\dev\go\qp\node_modules\@griffel\babel-preset\src\utils\evaluatePaths.js:22:51)
    at PluginPass.exit (C:\dev\go\qp\node_modules\@griffel\babel-preset\src\transformPlugin.js:186:59)
    at newFn (C:\dev\go\qp\node_modules\@babel\traverse\lib\visitors.js:172:14)
    at NodePath._call (C:\dev\go\qp\node_modules\@babel\traverse\lib\path\context.js:49:20)
    at NodePath.call (C:\dev\go\qp\node_modules\@babel\traverse\lib\path\context.js:39:18)
    at NodePath.visit (C:\dev\go\qp\node_modules\@babel\traverse\lib\path\context.js:96:8)
    at TraversalContext.visitQueue (C:\dev\go\qp\node_modules\@babel\traverse\lib\context.js:90:16)
    at TraversalContext.visitSingle (C:\dev\go\qp\node_modules\@babel\traverse\lib\context.js:66:19)
    at TraversalContext.visit (C:\dev\go\qp\node_modules\@babel\traverse\lib\context.js:113:19)
    at traverseNode (C:\dev\go\qp\node_modules\@babel\traverse\lib\traverse-node.js:22:17)
    at traverse (C:\dev\go\qp\node_modules\@babel\traverse\lib\index.js:53:34)
    at transformFile (C:\dev\go\qp\node_modules\@babel\core\lib\transformation\index.js:82:31)
    at transformFile.next (<anonymous>)
    at run (C:\dev\go\qp\node_modules\@babel\core\lib\transformation\index.js:24:12)
    at run.next (<anonymous>)
    at C:\dev\go\qp\node_modules\@babel\core\lib\transform-ast.js:23:33
    at Generator.next (<anonymous>)
    at evaluateSync (C:\dev\go\qp\node_modules\gensync\index.js:251:28)
    at sync (C:\dev\go\qp\node_modules\gensync\index.js:89:14)
    at stopHiding - secret - don't use this - v1 (C:\dev\go\qp\node_modules\@babel\core\lib\errors\rewrite-stack-trace.js:47:12)
    at Object.transformFromAstSync (C:\dev\go\qp\node_modules\@babel\core\lib\transform-ast.js:43:83)
    at transformSync (C:\dev\go\qp\node_modules\@griffel\webpack-loader\src\transformSync.js:21:35)
    at Object.webpackLoader (C:\dev\go\qp\node_modules\@griffel\webpack-loader\src\webpackLoader.js:80:52)

Import trace for requested module:
./src/components/quickform/GroupOnlineQuickform.tsx


> Build failed because of webpack errors

pksorensen avatar Nov 14 '24 11:11 pksorensen

I was able to track down the issue, in the new project we was importing our tokens .

in a component if using tokens from library then the error surfaces, but if we just type out the var(...) manually things work

"use client";
import { QuickForm, QuickFormDefinition, QuickFormProvider, quickformtokens } from "@eavfw/quickform-core"
import { QuotationFormStatus } from "../../eav/quotationFormTemplates.api";
import { makeStaticStyles, makeStyles } from "@fluentui/react-components";

import { goTokens } from "../../style/tokens";
import { useState } from "react";
import { useServerInsertedHTML } from "next/navigation";

const useQuickformStyles = makeStyles({
    slide: {
        margin: "2rem auto",
        maxWidth: "72rem",
    },
    ending: {
        alignItems: "center",
        lineHeight: "1.4"
    },
    slideButton: {
        paddingLeft: '25px',
        paddingRight: '25px',
        borderRadius: quickformtokens.questionBorderRadius // 'var(--slide-button-border-radius,100px)' This works when typed directly, not imported tokens
    },
    banner: {
        padding: '1rem 0',
        marginBottom: '4px',
        display: 'flex',
        justifyContent: 'center',
        fontWeight: '900',
        borderRadius: '20px',
        boxShadow: 'rgba(100, 100, 111, 0.2) 0px 7px 29px 0px',
    }
});

Library Tokens

/* Color-scheme is inspired by Material Design (https://m2.material.io/design/color/the-color-system.html#color-theme-creation) */

import { log } from "console";
import { resolveQuickFormService } from "../services/QuickFormServices";
import { camelToKebabCase, defineVariables } from "../utils/quickformUtils";
import { defaultQuickFormTokens } from "./defaultQuickFormTokens";

type Color = string;
type FontSize = string;
type Gap = string;
type NuancedColor<T extends string> = `${T}${'Darker' | 'Lighter'}${'' | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900}`;
type QuickFormStructuredColorProperties = 'primary' | 'onPrimary' | 'secondary' | 'onSecondary' | 'background' | 'onBackground' | 'surface' | 'onSurface' | 'error' | 'onError';

type QuickFormTokensBase = {
    /* Pure colors */
    white: Color,
    black: Color,
    warning: Color,
    success: Color,
    info: Color,
    questionPlaceholderColor: Color,

    /* Structured colors */
    primary: Color,
    onPrimary: Color,
    secondary: Color,
    onSecondary: Color,
    background: Color,
    onBackground: Color,
    surface: Color,
    onSurface: Color,
    error: Color,
    onError: Color,

    /* Typography */
    fontFamily: string,
    headlineFontSize: FontSize;
    subtitleFontSize: FontSize;
    paragraphFontSize: FontSize;
    paragraphMobileFontSize: FontSize;
    btnFontSize: FontSize,
    btnEnterKeyTextFontSize: FontSize,
    btnFontWeight: number,
    multilineTextFontSize: FontSize,
    multilineTextMobileFontSize: FontSize,

    questionHeadlineFontSize: FontSize,
    questionHeadlineFontWeight: number,
    questionParagraphFontSize: FontSize,
    questionNumberFontSize: FontSize,
    questionInputFontSize: FontSize,

    /* Structural properties */
    gap1: Gap,
    gap2: Gap,
    gap4: Gap,
    disabledOpacity: number;
    dividerOpacity: number;
    lowEmphasisOpacity: number;
    mediumEmphasisOpacity: number;
    highEmphasisOpacity: number;

    // Question
    questionBorderRadius: string;
    questionTopMargin: string;
    questionBottomMargin: string;
    questionPadding: string;
    questionInputGap: Gap,
    questionPaddingBottom: string;

    slideButtonIconSize: string;
};

export type QuickFormTokens = QuickFormTokensBase & {
    [Property in NuancedColor<QuickFormStructuredColorProperties>]?: Color;
};

/**
 * Defines and returns the CSS variables for the Quick Form based on provided tokens.
 * This function merges user-defined tokens with the default tokens, ensuring that
 * any customizations are applied on top of the defaults. The result is a flat object
 * where keys are CSS variable names in camel-case, suitable for direct use in styling objects that use React.CSSProperties.
 * 
 * @param tokens - An array of token objects. Each object can partially override the default tokens.
 * @returns A flat object with CSS camel-case variable names as keys and their corresponding values.
 */
export const defineQuickFormTokens = (...tokens: Array<Partial<QuickFormTokens>>) => {
    const logger = resolveQuickFormService("logger");
    logger.log("Merging Quick Form tokens.", tokens);
    // Merges and overrides default tokens with provided ones in reverse order for precedence.
    const mergedTokens = tokens.reduce((prevTokens, currentTokens) => {
        logger.log("Merging currentTokens into prevTokens", prevTokens, currentTokens);
        return ({
            ...prevTokens,
            ...currentTokens,
        })
    }, defaultQuickFormTokens);

    // Ensures merged tokens are camelCase CSS variables that React.CSSProperties can use and return.
    return defineVariables(mergedTokens);
};




/**
 * Provides QuickForm with css tokens to be passed around in the components so they refer to the same css variables that are loaded into the QuickFormProvider upon application instantiation.
 * @returns A flat object with CSS variables in camelCase that have corresponding values provided as kebab-case tokens variable names that map to globally defined colors.
 * See example: quickformtokens = { onPrimary: "var(--on-primary)"; onSecondary: "var(--on-secondary)" } and so on. You get the idea.
 */
export const quickformtokens = camelToKebabCase(defaultQuickFormTokens);

pksorensen avatar Nov 14 '24 15:11 pksorensen

@layershifter - are there any special way one need to export tokens for them to be used. I looked at fluentui and it seems it is just a dictionary of key value - i changed above to the same to not have any logic like the cameltoKebabCase, but the result is the same.

Need to figure out how to export tokens from my lib so it can be used in consumer project with css extraction

pksorensen avatar Nov 14 '24 16:11 pksorensen

(will follow up on Monday 👀 )

layershifter avatar Nov 15 '24 09:11 layershifter

@pksorensen do I correctly understand that you have a monorepo:

Image

Are you using Yarn Workspaces? NPM?

Or should @eavfw/quickform-core come from a separate repo? (i.e. external dependency)


Monorepo case

It's odd that it gets resolved into node_modules/*, should be C:\dev\go\qp\packages\SMTH.

External dependency

In this case, it seems that @eavfw/quickform-core it's not bundled properly. Something like tsup (with bundling both ESM & CJS) + proper fields in package.json (i.e. main & module) should solve this case.

layershifter avatar Nov 18 '24 10:11 layershifter

@pksorensen Were you able to solve that problem? Mind sharing the solution please?

creage avatar Apr 04 '25 10:04 creage