Last active
March 6, 2022 18:36
-
-
Save kubijo/8c2346dcfe6a3380a700906c4bd6bb04 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path'); | |
const { xfs } = require('@yarnpkg/fslib'); | |
/** | |
* @example preffixCssPath('foo/bar/baz.scss') // >> 'foo/bar/_baz.scss' | |
* | |
* @param {String} p | |
* @return {string} | |
*/ | |
function preffixCssPath(p) { | |
return path.join(path.dirname(p), `_${path.basename(p)}`); | |
} | |
/** | |
* Neither node-sass (C) or sass (Dart) node modules support Yarn's PNP architecture, | |
* but node-sass is much faster and supports custom importer function. | |
* | |
* So I've took the distictive pleassure of writing one that uses | |
* yarn's patched `require.resolve` & `fs` modules. | |
* | |
* @see https://yarnpkg.com/features/pnp | |
* @see https://yarnpkg.com/api/modules/yarnpkg_fslib.html | |
* | |
* Also, SASS allows import of partials (files starting with "_") | |
* without explicit mention of "_", so we need to try that as well… | |
* | |
* @param {String} url | |
* The `@import` request as it's encountered in the sources. | |
* Passed in from node-sass's importer function. | |
* | |
* @param {String} prev | |
* The current file's `@import` declaration (afaik ¯\_(ツ)_/¯). | |
* Passed in from node-sass's importer function. | |
* | |
* @param {function({ contents: String }): void} done | |
* Passed in from node-sass's importer function. | |
* | |
* @return {undefined|Error} | |
*/ | |
function sassPnpImporter(url, prev, done) { | |
/** | |
* SASS alows us to omit the extension, but we need it for `require.resolve` | |
* to see the files as by default it searches for javascript modules | |
* and changing `require.extensions` is discouraged. | |
* | |
* @see https://nodejs.org/api/modules.html#modules_require_extensions | |
* | |
* Also webpack came with a great idea for tilde at the front to signify import from module… | |
* (which actually helps us here as we then know for sure what user means by that) | |
*/ | |
const cleanedUrl = url | |
// Remove '~' at the start | |
.replace(/^~/, '') | |
// Add extension if missing | |
.replace(/(\.scss)?$/, '.scss'); | |
// This will be the real absolute path on filesystem, | |
// that we'll try to load at the end… | |
let realPath = null; | |
// Explicitly marked as node module by "~" | |
if (url.startsWith('~')) { | |
// Partials import ... first try the normal version, then the underscored one. | |
try { | |
realPath = require.resolve(cleanedUrl, 'explicit node module'); | |
} catch (_) { | |
realPath = require.resolve(preffixCssPath(cleanedUrl), '_explicit node module'); | |
} | |
} | |
// Relative | |
else if ( | |
// starts with `./` or `../` | |
/^\.{1,2}\//.test(url) || | |
// No path delimiter | |
!url.includes('/') | |
) { | |
const p = path.join( | |
// We've got current file's import location by reporting it in `done({ file: "…" })`, | |
// so now we have something to base the relative imports from. | |
path.dirname(prev), | |
cleanedUrl, | |
); | |
// Partials import ... first try the normal version, then the underscored one. | |
try { | |
realPath = require.resolve(p, 'local'); | |
} catch (_) { | |
realPath = require.resolve(preffixCssPath(p), '_local'); | |
} | |
} | |
// Asume node module resolution | |
else { | |
// Partials import ... first try the normal version, then the underscored one. | |
try { | |
realPath = require.resolve(cleanedUrl); | |
} catch (_) { | |
realPath = require.resolve(preffixCssPath(cleanedUrl)); | |
} | |
} | |
// If we have a path now, it should be load-able by Yarn's patched `fs` | |
if (typeof realPath === 'string' && realPath.length) { | |
// By including the `file` parameter we'll get it later as origin of current file! | |
done({ file: realPath, contents: xfs.readFileSync(realPath, 'utf-8') }); | |
return; | |
} | |
// (╯°□°)╯︵ ┻━┻ | |
return new Error(`Couldn't load requested SASS import "${url}"!`); | |
} | |
// … later on | |
// { | |
// loader: 'sass-loader', | |
// options: { | |
// sassOptions: { importer: sassPnpImporter }, | |
// }, | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment