vuejs / vue-jest

Jest Vue transformer
MIT License
746 stars 157 forks source link

Using PostCSS mixins in CSS modules causes a syntax error #424

Closed sevilyilmaz closed 2 years ago

sevilyilmaz commented 2 years ago

This issue might be related to vue-jest but I'll open it here because I got the error from the test utils and I'm 100% sure of the what is responsible from it. Please feel free to point me to the correct repo if this is not the one.

Description

I get the error below when I use a PostCSS mixin in a CSS file. I assume it expects a semicolon where I use the mixin while parsing the CSS file but it cannot find it and throws this error.

 FAIL  src/components/HellowWorld.test.js
  ● Test suite failed to run

    undefined:12:1: missing '}'

      at error (node_modules/css/lib/parse/index.js:62:15)
      at declarations (node_modules/css/lib/parse/index.js:260:26)
      at rule (node_modules/css/lib/parse/index.js:561:21)
      at rules (node_modules/css/lib/parse/index.js:118:70)
      at stylesheet (node_modules/css/lib/parse/index.js:81:21)
      at Object.module.exports [as parse] (node_modules/css/lib/parse/index.js:565:20)
      at Function.extractClasses (node_modules/extract-from-css/lib/index.js:14:23)
      at extractClassMap (node_modules/@vue/vue3-jest/lib/process-style.js:24:31)
      at processStyle (node_modules/@vue/vue3-jest/lib/process-style.js:123:25)
      at node_modules/@vue/vue3-jest/lib/process.js:149:13

Reproduction link: https://github.com/sevilyilmaz/vue-3-setup-css-modules-mixin

Setup

HelloWorld.vue

<script>
import { ref, computed, useCssModule } from 'vue';

export default {
  props: {
    msg: String,
  },
  setup(props) {
    const styles = useCssModule();
    const rootClasses = computed(() => ({
      [styles.root]: true,
    }));

    return { rootClasses };
  },
};
</script>

<template>
  <div :class="rootClasses">{{ msg }}</div>
</template>

<style src="./HelloWorld.module.css" module />

HelloWorld.module.css

:root {
  --theme_color_background_base: #f00;
}

@define-mixin hover {
  outline: none;
  box-shadow: 0 0 0 2px var(--theme_color_background_base);
}

.root {
  color: #42b983;
}
.root:hover {
  @mixin hover;
}

HelloWorld.test.js

import { mount } from '@vue/test-utils';
import HelloWorld from './HelloWorld.vue';

describe('HelloWorld', () => {
  it('renders with correct classes', () => {
    const wrapper = mount(HelloWorld);

    expect(wrapper.classes()).toEqual(['root']);
  });
});

jest.config.js

module.exports = {
  collectCoverageFrom: ['src/**/*.{js,vue}'],
  coverageReporters: ['text', 'cobertura', 'json', 'lcov', 'clover'],
  coverageProvider: 'v8',
  errorOnDeprecated: true,
  moduleFileExtensions: ['js', 'json', 'vue'],
  notify: false,
  snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
  testEnvironment: 'jest-environment-jsdom',
  testPathIgnorePatterns: ['<rootDir>/cypress/'],
  testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(test).[jt]s?(x)'],
  testURL: 'http://localhost',
  transform: {
    '^.+\\.vue$': '@vue/vue3-jest',
    '^.+\\.js$': 'babel-jest',
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
      require.resolve('jest-transform-stub'),
  },
  transformIgnorePatterns: ['/node_modules/(?!some-internal-modules).+\\.js$'],
};
cexbrayat commented 2 years ago

Hi @sevilyilmaz

I transfered the issue to vue-jest as I indeed think this is a a vue-jest issue. The stacktrace mentions extractClassMap as the source of the error. extractClassMap uses the fairly old (7 years?!) https://github.com/rubennorte/extract-from-css package, so it's definitely possible that this package does not support some syntaxes.

If you want to give a hand, you can try to add a test to this repo and see what syntax is failing specifically. Maybe we can use another dependency to do this job instead of extract-from-css?

sevilyilmaz commented 2 years ago

@cexbrayat vue-jest does not have unit tests. Adding my example in one of the fixtures in E2E tests resulted the same error with less details.

 FAIL  ./test.js
  ● Test suite failed to run

    undefined:13:1: missing '}'

      at error (../../../node_modules/css/lib/parse/index.js:62:15)

I dug into it a bit and this is what I found. vue-jest uses tries to get CSS maps in process-style.js with extract-from-css and that uses an old version (^2.1.0) of the css package to parse the CSS.

We can use @vue/compiler-sfc to compile the CSS (which uses PostCSS) as it's done in @vite/plugin-vue which would be better as vue-jest would be parsing the components as Vue does, though this might increase the scope of this issue.

What do you think?

cexbrayat commented 2 years ago

That would probably be way better, if you would like to make a PR, that would be awesome!

sevilyilmaz commented 2 years ago

I picked an another route and replaced the CSS parser. Please have a look.