etchteam / storybook-addon-css-variables-theme

Change the CSS variable based design tokens in your storybook on the fly
MIT License
28 stars 12 forks source link

file.use is not a function #12

Closed carstenbaumhoegger closed 1 year ago

carstenbaumhoegger commented 2 years ago

Hey there,

thanks for this helpful extension!

We've been using it with a project based on Nx 11 and everything was working great for us.

We've now updated the project to Nx 13 and we're getting this error: image

We're using this preview.js-config:

import gray from '!style-loader?injectType=lazyStyleTag!css-loader!../libs/ui/assets/styles/themes/lazy.gray.css';
import blue from '!style-loader?injectType=lazyStyleTag!css-loader!../libs/ui/assets/styles/themes/lazy.blue.css';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';
import 'cypress-storybook/angular';
import cssVariablesTheme from '@etchteam/storybook-addon-css-variables-theme';
import { addDecorator } from '@storybook/angular';

export const decorators = [cssVariablesTheme];
setCompodocJson(docJson);
addDecorator(cssVariablesTheme);
const customViewports = {
 ...
};

export const parameters = {
  options: {
    storySort: {
        ...
    },
  },
  viewport: { viewports: customViewports },
  cssVariables: {
    files: {
      'Theme gray': gray,
      'Theme blue': blue
    },
  },
};

The generated code looks like it loads a simple string, so we assume file.use isn't accessible because of that MicrosoftTeams-image

Do you have any idea, why this is happening? Storybook itself is fine but the addon isn't working for us anymore.

gavmck commented 2 years ago

Hey there, sorry for the late reply!

If you just do the import and remove the CSS variables plugin code, what are your variables gray and blue.

file.use comes from using lazyStyleTag which should give you a use/unuse method on the variable for you to turn it on and off.

YasinKuralay commented 2 years ago

Hey @gavmck we have the same problem. We weren't using the addon before though. Our nx version is currently 13.9.5.

I'm assuming you want to know what the variable is when printed without the addon code? When I console.log it I get the following output. The file is called styles.c017.css. Let me know if you need anything else from me:

var exported = {};

import API from "!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";
import domAPI from "!../node_modules/style-loader/dist/runtime/styleDomAPI.js";
import insertFn from "!../node_modules/style-loader/dist/runtime/insertBySelector.js";
import setAttributes from "!../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js";
import insertStyleElement from "!../node_modules/style-loader/dist/runtime/insertStyleElement.js";
import styleTagTransformFn from "!../node_modules/style-loader/dist/runtime/styleTagTransform.js";
import content, * as namedExport from "!!../node_modules/css-loader/dist/cjs.js!./styles.c017.css";

if (content && content.locals) {
  exported.locals = content.locals;
}

var refs = 0;
var update;
var options = {};

options.styleTagTransform = styleTagTransformFn;
options.setAttributes = setAttributes;

options.insert = insertFn.bind(null, "head");

options.domAPI = domAPI;
options.insertStyleElement = insertStyleElement;

exported.use = function (insertOptions) {
  options.options = insertOptions || {};

  if (!refs++) {
    update = API(content, options);
  }

  return exported;
};
exported.unuse = function () {
  if (refs > 0 && !--refs) {
    update();
    update = null;
  }
};

if (module.hot) {
  if (!content.locals || module.hot.invalidate) {
    var isEqualLocals = function isEqualLocals(a, b, isNamedExport) {
      if ((!a && b) || (a && !b)) {
        return false;
      }

      var p;

      for (p in a) {
        if (isNamedExport && p === "default") {
          // eslint-disable-next-line no-continue
          continue;
        }

        if (a[p] !== b[p]) {
          return false;
        }
      }

      for (p in b) {
        if (isNamedExport && p === "default") {
          // eslint-disable-next-line no-continue
          continue;
        }

        if (!a[p]) {
          return false;
        }
      }

      return true;
    };
    var isNamedExport = !content.locals;
    var oldLocals = isNamedExport ? namedExport : content.locals;

    module.hot.accept(
      "!!../node_modules/css-loader/dist/cjs.js!./styles.c017.css",
      function () {
        if (
          !isEqualLocals(
            oldLocals,
            isNamedExport ? namedExport : content.locals,
            isNamedExport
          )
        ) {
          module.hot.invalidate();

          return;
        }

        oldLocals = isNamedExport ? namedExport : content.locals;

        if (update && refs > 0) {
          update(content);
        }
      }
    );
  }

  module.hot.dispose(function () {
    if (update) {
      update();
    }
  });
}

export * from "!!../node_modules/css-loader/dist/cjs.js!./styles.c017.css";
export default exported;
gavmck commented 2 years ago

Huh, so it's the right code, but being imported as text instead of actual usable code.

gavmck commented 2 years ago

I've managed to recreate this and it looks like it's probably an issue with NX. The css file is transformed properly by webpack to give you a bunch of JS with use/unuse functions, but something in there is loading it as text instead of javascript. The !! at the beginning should eject out of pre-set webpack loaders so I can only see that NX is doing something magic.

YasinKuralay commented 2 years ago

Ah okay, so the problem is not the addon. I ll see what I can find/do, thank you very much for the help :)

Nighink commented 2 years ago

I had the same problem with a new Angular 13 project. There is a stackoverflow post that proposes to use some special syntax for imports, which at least gave me an object instead of a string though it is still not working. See https://stackoverflow.com/questions/69614700/import-css-file-into-es6-returns-string-instead-of-object/69661939#69661939

The import I tried was import base from 'test.css.webpack[javascript/auto]!=!!!style-loader?injectType=lazyStyleTag!css-loader!../projects/path/to/base.css'; I looked at the object loaded in the browser developer console and found that I can call use() to add a style to the storybook iframe though the style's content was some javascript containing a string.

In the end, it works for me to skip the css-loader

import base from 'test.css.webpack[javascript/auto]!=!!!style-loader?injectType=lazyStyleTag!../projects/path/to/base.css';

or

import base from 'test.scss.webpack[javascript/auto]!=!!!style-loader?injectType=lazyStyleTag!sass-loader!../projects/path/to/_base.scss';

I spent far too much time experimenting with this. I hope it helps someone.

DanWebb commented 1 year ago

The issue seems to be that some kind of transformation is happening to the css/scss file before the inline loader ever runs.

To workaround that, one way would be to rename the files extension, to avoid affecting any actual project files, a separate file could be created that imports the styles for the theme, so:

- .storybook/
   | - themes/
   |   | - dark.scss.theme
   | - preview.js

Then in dark.scss.theme the actual file from the project could be imported @import '../../src/styles/themes/dark';.

Then the loader can be applied as usual, pointing to the new file:

import dark from '!!style-loader?injectType=lazyStyleTag!css-loader!sass-loader!./themes/dark.scss.theme';

The key is in renaming the files extension to something like .scss.whatever – this avoids any existing configuration applying to .scss files in the codebase.


I'm going to close this issue, because it appears to be down to specific webpack/project configurations.