Skip to content

Commit

Permalink
fix: bare Sass imports to files with fake extensions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vvanpo committed Jul 28, 2020
1 parent 22122c4 commit aeb0983
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 18 deletions.
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';
"^~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)
}

0 comments on commit aeb0983

Please sign in to comment.