Skip to content

Fix bare imports of Sass files with dots.in.filename.scss #259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,23 @@ If a string is provided, it will be an assumed path to a TypeScript configuratio
### Supported style languages

- **stylus** (`lang="stylus"`, `lang="styl"`)
- **sass** (`lang="sass"`)
- 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.
- **sass** (`lang="sass"`), and
- **scss** (`lang="scss"`)

- 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.
- 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.
```json
{
"jest": {
"moduleNameMapper": {
"^~foo/(.*)": "<rootDir>/foo/$1",
// @import '~foo'; -> @import 'path/to/project/foo';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that GitHub does not like my usage of comments in a JSON block, fair enough, I'll have to change this to a js block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, comments are not valid json. That's why!

"^~bar/(.*)": "~baz/lib/$1"
// @import '~bar/qux'; -> @import 'path/to/project/node_modules/baz/lib/qux';
// Notice how the tilde (~) was needed on the bare import to baz.
}
}
}
```
- To import globally included files (ie. variables, mixins, etc.), include them in the Jest configuration at `jest.globals['vue-jest'].resources.scss`:

```json
Expand Down
8 changes: 7 additions & 1 deletion e2e/__projects__/style/components/Scss.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@

<style lang="scss" module>
@import '~__styles/scss-a';
@import '~vue-jest-test/partial.scss';
// Import absolute URL via moduleNameMapper.
@import '~tmp/absolute';
// Import _partial from within node_modules.
@import '~vue-jest-test/partial';
@import '~vue-jest-test/foo.bar'; // Import false extension.
@import '~vue-jest-test/baz'; // Import .css from within .scss.
@import '~vue-jest-test/qux'; // Import .sass from within .scss.

.c {
background-color: $primary-color;
Expand Down
1 change: 1 addition & 0 deletions e2e/__projects__/style/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"^.+\\.vue$": "vue-jest"
},
"moduleNameMapper": {
"^~tmp/(.*)": "/tmp/$1",
"^~?__styles/(.*)$": "<rootDir>/components/styles/$1"
},
"globals": {
Expand Down
4 changes: 4 additions & 0 deletions e2e/__projects__/style/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ if (!fs.existsSync(testDir)) {
fs.mkdirSync(testDir)
}

fs.openSync('/tmp/absolute.scss', 'w')
fs.openSync(`${testDir}/_partial.scss`, 'w')
fs.openSync(`${testDir}/foo.bar.scss`, 'w')
fs.openSync(`${testDir}/baz.css`, 'w')
fs.openSync(`${testDir}/qux.sass`, 'w')
40 changes: 26 additions & 14 deletions lib/module-name-mapper-helper.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
const path = require('path')
const matchModuleImport = /^[^?]*~/

/**
* Resolves the path to the file/module.
* Resolve a Sass @import or @use rule.
*
* @param {String} to - the name of the file to resolve to
* @param {String} importPath - the local path
* @param {String} fileType - extension of the file to be resolved
* @returns {String} path - path to the file to import
* @param {String} to - The path to the current file
* @param {String} importPath - The path to resolve
* @param {String} fileType - The filetype of the current file
*/
function resolve(to, importPath, fileType) {
importPath =
path.extname(importPath) === '' ? `${importPath}.${fileType}` : importPath
function resolveSass(to, importPath, fileType) {
// Mimic Sass-loader's `~` syntax for bare imports.
const matchModuleImport = /^~/

if (path.isAbsolute(importPath)) {
return importPath
} else if (matchModuleImport.test(importPath)) {
const dirname = path.dirname(importPath).replace(matchModuleImport, '')
const basename = path.basename(importPath)

try {
return require.resolve(path.join(dirname, basename))
} catch (_) {
return require.resolve(path.join(dirname, `_${basename}`))
const filenames = []

if (!/\.(sc|sa|c)ss/.test(basename)) {
const extensions = ['scss', 'sass', 'css'].filter(e => e !== fileType)
extensions.unshift(fileType)
extensions.forEach(ext => {
filenames.push(`${basename}.${ext}`, `_${basename}.${ext}`)
})
} else {
filenames.push(basename, `_${basename}`)
}

for (const filename of filenames) {
try {
return require.resolve(path.join(dirname, filename))
} catch (_) {}
}
}

return path.join(path.dirname(to), importPath)
}

Expand Down Expand Up @@ -61,5 +73,5 @@ module.exports = function applyModuleNameMapper(
)
}, source)

return resolve(filePath, importPath, fileType)
return resolveSass(filePath, importPath, fileType)
}