Skip to content

Commit a820e3b

Browse files
authored
Fix modern SASS resolution rules (#10018)
1 parent 0da7b63 commit a820e3b

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

packages/core/integration-tests/test/sass.js

+26
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,30 @@ describe('sass', function () {
373373
},
374374
]);
375375
});
376+
377+
it('should support sass import resolution rules', async function () {
378+
const dir = path.join(__dirname, 'sass-extensions');
379+
overlayFS.mkdirp(dir);
380+
381+
await fsFixture(overlayFS, dir)`
382+
index.js:
383+
import './main.scss';
384+
385+
main.scss:
386+
@use '~test' as test;
387+
388+
node_modules/test/package.json:
389+
{ "name": "test" }
390+
391+
node_modules/test/_index.scss:
392+
@use 'other';
393+
394+
node_modules/test/_other.scss:
395+
.foo { color: red }
396+
`;
397+
398+
await bundle(path.join(dir, '/index.js'), {
399+
inputFS: overlayFS,
400+
});
401+
});
376402
});

packages/transformers/sass/src/modern.js

+34-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ function resolvePathImporter({
9595
See also: https://github.com/sass/dart-sass/blob/006e6aa62f2417b5267ad5cdb5ba050226fab511/lib/src/importer/node/implementation.dart
9696
*/
9797

98-
let containingPath = fileURLToPath(containingUrl);
98+
let containingPath = containingUrl
99+
? fileURLToPath(containingUrl)
100+
: asset.filePath;
101+
if (!containingUrl) {
102+
// If containingUrl is not provided, then url should be an absolute file:/// URL.
103+
let filePath = fileURLToPath(url);
104+
url = path.relative(path.dirname(containingPath), filePath);
105+
}
106+
99107
let paths = [path.dirname(containingPath)];
100108
if (loadPaths) {
101109
paths.push(...loadPaths);
@@ -110,17 +118,39 @@ function resolvePathImporter({
110118
);
111119
}
112120

121+
// The importer should look for stylesheets by adding the prefix _ to the URL's basename,
122+
// and by adding the extensions .sass and .scss if the URL doesn't already have one of those extensions.
113123
const urls = [url];
114124
const urlFileName = path.basename(url);
115125
if (urlFileName[0] !== '_') {
116-
urls.push(path.join(path.dirname(url), `_${urlFileName}`));
126+
urls.push(path.posix.join(path.dirname(url), `_${urlFileName}`));
117127
}
118128

129+
let ext = path.extname(urlFileName);
130+
if (ext !== '.sass' && ext !== '.scss') {
131+
for (let url of [...urls]) {
132+
urls.push(url + '.sass');
133+
urls.push(url + '.scss');
134+
}
135+
}
136+
137+
// If none of the possible paths is valid, the importer should perform the same resolution on the URL followed by /index.
138+
urls.push(path.posix.join(url, 'index.sass'));
139+
urls.push(path.posix.join(url, 'index.scss'));
140+
urls.push(path.posix.join(url, '_index.sass'));
141+
urls.push(path.posix.join(url, '_index.scss'));
142+
119143
if (url[0] !== '~') {
120144
for (let p of paths) {
121145
for (let u of urls) {
122-
const filePath = path.resolve(p, u);
123-
if (await asset.fs.exists(filePath)) {
146+
let filePath = path.resolve(p, u);
147+
let stat;
148+
try {
149+
stat = await asset.fs.stat(filePath);
150+
} catch (err) {
151+
// ignore.
152+
}
153+
if (stat?.isFile()) {
124154
return pathToFileURL(filePath);
125155
}
126156

0 commit comments

Comments
 (0)