From dfab26077a4ad340a11c2875278a470dffc374c2 Mon Sep 17 00:00:00 2001 From: Lachlan McMillan Date: Thu, 30 Jul 2026 16:34:44 +1000 Subject: [PATCH 1/2] test: add tests for CSS modules with @import functionality --- .../__tests__/fixtures/fileA.module.css | 3 ++ .../__tests__/fixtures/fileB.module.css | 5 ++++ src/helpers/__tests__/getDtsSnapshot.test.ts | 29 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 src/helpers/__tests__/fixtures/fileA.module.css create mode 100644 src/helpers/__tests__/fixtures/fileB.module.css diff --git a/src/helpers/__tests__/fixtures/fileA.module.css b/src/helpers/__tests__/fixtures/fileA.module.css new file mode 100644 index 0000000..e8e8373 --- /dev/null +++ b/src/helpers/__tests__/fixtures/fileA.module.css @@ -0,0 +1,3 @@ +.class1 { + color: red; +} diff --git a/src/helpers/__tests__/fixtures/fileB.module.css b/src/helpers/__tests__/fixtures/fileB.module.css new file mode 100644 index 0000000..b4b634b --- /dev/null +++ b/src/helpers/__tests__/fixtures/fileB.module.css @@ -0,0 +1,5 @@ +@import './fileA.module.css'; + +.class2 { + color: blue; +} diff --git a/src/helpers/__tests__/getDtsSnapshot.test.ts b/src/helpers/__tests__/getDtsSnapshot.test.ts index 7491022..d64a021 100644 --- a/src/helpers/__tests__/getDtsSnapshot.test.ts +++ b/src/helpers/__tests__/getDtsSnapshot.test.ts @@ -129,6 +129,35 @@ describe('helpers / cssSnapshots', () => { }); }); + /** + * When fileB.module.css `@import`s fileA.module.css, Vite (and css-loader) + * expose classes from both files on the fileB module export. The plugin's + * default processor does not inline `@import`, so class1 is missing from + * types even though it works at runtime. + */ + describe('with a CSS modules @import of another CSS modules file', () => { + const fileName = join(__dirname, 'fixtures', 'fileB.module.css'); + const css = readFileSync(fileName, 'utf8'); + const defaultProcessor = getProcessor(); + + it('should include classes from both the importing and imported files', () => { + const cssExports = getCssExports({ + css, + fileName, + logger, + options, + processor: defaultProcessor, + compilerOptions, + directory: __dirname, + }); + + expect(cssExports.classes).toEqual({ + class1: 'class1', + class2: 'class2', + }); + }); + }); + describe('with a custom renderer', () => { const fileName = 'exampleFileContents'; const css = 'exampleFileName'; From debee4ba787523795785a15b7b1cf2c84ddbe18c Mon Sep 17 00:00:00 2001 From: Lachlan McMillan Date: Thu, 30 Jul 2026 16:44:41 +1000 Subject: [PATCH 2/2] fix: include classes from CSS @import in default exports --- package.json | 2 +- pnpm-lock.yaml | 27 +++++++++++++++------------ src/helpers/getProcessor.ts | 2 ++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index c8d6c15..5f41d95 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "less": "^4.2.0", "lodash.camelcase": "^4.3.0", "postcss": "^8.4.35", + "postcss-import-sync2": "^1.2.0", "postcss-load-config": "^3.1.4", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.4", @@ -83,7 +84,6 @@ "jest": "^29.7.0", "jest-environment-node-single-context": "^29.4.0", "lint-staged": "^15.2.2", - "postcss-import-sync2": "^1.2.0", "postcss-nested": "^4.2.3", "postcss-preset-env": "^8.5.1", "prettier": "^3.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b687d83..da12aa9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ dependencies: postcss: specifier: ^8.4.35 version: 8.4.35 + postcss-import-sync2: + specifier: ^1.2.0 + version: 1.2.0(postcss@8.4.35) postcss-load-config: specifier: ^3.1.4 version: 3.1.4(postcss@8.4.35) @@ -108,9 +111,6 @@ devDependencies: lint-staged: specifier: ^15.2.2 version: 15.2.2 - postcss-import-sync2: - specifier: ^1.2.0 - version: 1.2.0(postcss@8.4.35) postcss-nested: specifier: ^4.2.3 version: 4.2.3 @@ -139,6 +139,7 @@ packages: /@adobe/css-tools@4.3.3: resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + requiresBuild: true dev: false optional: true @@ -2249,6 +2250,7 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + requiresBuild: true /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -2259,7 +2261,6 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - dev: true /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -2360,7 +2361,6 @@ packages: engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 - dev: true /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -2441,12 +2441,14 @@ packages: /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + requiresBuild: true dependencies: once: 1.4.0 wrappy: 1.0.2 /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + requiresBuild: true /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -2463,7 +2465,6 @@ packages: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.1 - dev: true /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -3316,6 +3317,7 @@ packages: /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + requiresBuild: true dependencies: wrappy: 1.0.2 @@ -3408,6 +3410,7 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + requiresBuild: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -3421,7 +3424,6 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -3448,7 +3450,7 @@ packages: /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - dev: true + dev: false /pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} @@ -3636,7 +3638,7 @@ packages: postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - dev: true + dev: false /postcss-initial@4.0.1(postcss@8.4.35): resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} @@ -3946,7 +3948,7 @@ packages: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} dependencies: pify: 2.3.0 - dev: true + dev: false /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -3993,7 +3995,6 @@ packages: is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} @@ -4148,6 +4149,7 @@ packages: /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} + requiresBuild: true dev: false optional: true @@ -4235,6 +4237,7 @@ packages: /stylus@0.62.0: resolution: {integrity: sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg==} hasBin: true + requiresBuild: true dependencies: '@adobe/css-tools': 4.3.3 debug: 4.3.4 @@ -4270,7 +4273,6 @@ packages: /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} @@ -4456,6 +4458,7 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + requiresBuild: true /write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} diff --git a/src/helpers/getProcessor.ts b/src/helpers/getProcessor.ts index 4e37eba..b975fd9 100644 --- a/src/helpers/getProcessor.ts +++ b/src/helpers/getProcessor.ts @@ -1,5 +1,6 @@ import postcss, { AcceptedPlugin } from 'postcss'; import Processor from 'postcss/lib/processor'; +import postcssImportSync from 'postcss-import-sync2'; import postcssLocalByDefault from 'postcss-modules-local-by-default'; import postcssModulesScope from 'postcss-modules-scope'; import postcssModulesExtractImports from 'postcss-modules-extract-imports'; @@ -9,6 +10,7 @@ export const getProcessor = ( ): Processor => postcss([ ...additionalPlugins, + postcssImportSync(), postcssLocalByDefault(), postcssModulesExtractImports(), postcssModulesScope({