Skip to content

Commit aeb0983

Browse files
committed
fix: bare Sass imports to files with fake extensions
If a Sass file attempted to import something like `~module/foo.bar`, our custom resolver would expect `.bar` to be the filetype and would forego appending `.scss` or `.sass`, causing `require.resolve()` to throw an exception. We can be explicit about the filetype by successively testing against `.scss`, `.sass` (depending on configured language in the <style>-bock), and `.css`. Add `.css` support for bare imports, and `.sass` imports from within `.scss` or vice-versa.
1 parent 22122c4 commit aeb0983

File tree

5 files changed

+53
-18
lines changed

5 files changed

+53
-18
lines changed

README.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,23 @@ If a string is provided, it will be an assumed path to a TypeScript configuratio
298298
### Supported style languages
299299

300300
- **stylus** (`lang="stylus"`, `lang="styl"`)
301-
- **sass** (`lang="sass"`)
302-
- The SASS compiler supports jest's [moduleNameMapper](https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string) which is the suggested way of dealing with Webpack aliases.
301+
- **sass** (`lang="sass"`), and
303302
- **scss** (`lang="scss"`)
304303

305-
- The SCSS compiler supports jest's [moduleNameMapper](https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string) which is the suggested way of dealing with Webpack aliases.
304+
- The Sass compiler supports Jest's [moduleNameMapper](https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string) which is the suggested way of dealing with Webpack aliases. Webpack's `sass-loader` uses a [special syntax](https://github.com/webpack-contrib/sass-loader/blob/v9.0.2/README.md#resolving-import-at-rules) for indicating non-relative imports, so you'll likely need to copy this syntax into your `moduleNameMapper` entries if you make use of it. For aliases of bare imports (imports that require node module resolution), the aliased value must also be prepended with this `~` or `vue-jest`'s custom resolver won't recognize it.
305+
```json
306+
{
307+
"jest": {
308+
"moduleNameMapper": {
309+
"^~foo/(.*)": "<rootDir>/foo/$1",
310+
// @import '~foo'; -> @import 'path/to/project/foo';
311+
"^~bar/(.*)": "~baz/lib/$1"
312+
// @import '~bar/qux'; -> @import 'path/to/project/node_modules/baz/lib/qux';
313+
// Notice how the tilde (~) was needed on the bare import to baz.
314+
}
315+
}
316+
}
317+
```
306318
- To import globally included files (ie. variables, mixins, etc.), include them in the Jest configuration at `jest.globals['vue-jest'].resources.scss`:
307319

308320
```json

e2e/__projects__/style/components/Scss.vue

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
<style lang="scss" module>
66
@import '~__styles/scss-a';
7-
@import '~vue-jest-test/partial.scss';
7+
// Import absolute URL via moduleNameMapper.
8+
@import '~tmp/absolute';
9+
// Import _partial from within node_modules.
10+
@import '~vue-jest-test/partial';
11+
@import '~vue-jest-test/foo.bar'; // Import false extension.
12+
@import '~vue-jest-test/baz'; // Import .css from within .scss.
13+
@import '~vue-jest-test/qux'; // Import .sass from within .scss.
814
915
.c {
1016
background-color: $primary-color;

e2e/__projects__/style/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"^.+\\.vue$": "vue-jest"
3131
},
3232
"moduleNameMapper": {
33+
"^~tmp/(.*)": "/tmp/$1",
3334
"^~?__styles/(.*)$": "<rootDir>/components/styles/$1"
3435
},
3536
"globals": {

e2e/__projects__/style/setup.js

+4
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ if (!fs.existsSync(testDir)) {
66
fs.mkdirSync(testDir)
77
}
88

9+
fs.openSync('/tmp/absolute.scss', 'w')
910
fs.openSync(`${testDir}/_partial.scss`, 'w')
11+
fs.openSync(`${testDir}/foo.bar.scss`, 'w')
12+
fs.openSync(`${testDir}/baz.css`, 'w')
13+
fs.openSync(`${testDir}/qux.sass`, 'w')

lib/module-name-mapper-helper.js

+26-14
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
const path = require('path')
2-
const matchModuleImport = /^[^?]*~/
2+
33
/**
4-
* Resolves the path to the file/module.
4+
* Resolve a Sass @import or @use rule.
55
*
6-
* @param {String} to - the name of the file to resolve to
7-
* @param {String} importPath - the local path
8-
* @param {String} fileType - extension of the file to be resolved
9-
* @returns {String} path - path to the file to import
6+
* @param {String} to - The path to the current file
7+
* @param {String} importPath - The path to resolve
8+
* @param {String} fileType - The filetype of the current file
109
*/
11-
function resolve(to, importPath, fileType) {
12-
importPath =
13-
path.extname(importPath) === '' ? `${importPath}.${fileType}` : importPath
10+
function resolveSass(to, importPath, fileType) {
11+
// Mimic Sass-loader's `~` syntax for bare imports.
12+
const matchModuleImport = /^~/
1413

1514
if (path.isAbsolute(importPath)) {
1615
return importPath
1716
} else if (matchModuleImport.test(importPath)) {
1817
const dirname = path.dirname(importPath).replace(matchModuleImport, '')
1918
const basename = path.basename(importPath)
2019

21-
try {
22-
return require.resolve(path.join(dirname, basename))
23-
} catch (_) {
24-
return require.resolve(path.join(dirname, `_${basename}`))
20+
const filenames = []
21+
22+
if (!/\.(sc|sa|c)ss/.test(basename)) {
23+
const extensions = ['scss', 'sass', 'css'].filter(e => e !== fileType)
24+
extensions.unshift(fileType)
25+
extensions.forEach(ext => {
26+
filenames.push(`${basename}.${ext}`, `_${basename}.${ext}`)
27+
})
28+
} else {
29+
filenames.push(basename, `_${basename}`)
30+
}
31+
32+
for (const filename of filenames) {
33+
try {
34+
return require.resolve(path.join(dirname, filename))
35+
} catch (_) {}
2536
}
2637
}
38+
2739
return path.join(path.dirname(to), importPath)
2840
}
2941

@@ -61,5 +73,5 @@ module.exports = function applyModuleNameMapper(
6173
)
6274
}, source)
6375

64-
return resolve(filePath, importPath, fileType)
76+
return resolveSass(filePath, importPath, fileType)
6577
}

0 commit comments

Comments
 (0)