Skip to content

Commit 80944cc

Browse files
committed
Refactor path resolving algorithm
#138 New algorithm: - Imports with no file extension: - Prefer modules starting with '_' - File extension precedence: .scss, .sass, .css - Imports with file extension: - If the file is a CSS-file, do not include it all - The exact file name must match This is how node-sass does it.
1 parent aceaf03 commit 80944cc

File tree

1 file changed

+95
-102
lines changed

1 file changed

+95
-102
lines changed

index.js

+95-102
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ var SassError = {
1414
file: 'stdin',
1515
status: 1
1616
};
17-
var resolveError = /Cannot resolve/;
17+
18+
var extPrecedence = ['.scss', '.sass', '.css'];
19+
var matchCss = /\.css$/;
1820

1921
/**
2022
* The sass-loader makes node-sass available to webpack modules.
@@ -27,12 +29,8 @@ module.exports = function (content) {
2729
var isSync = typeof callback !== 'function';
2830
var self = this;
2931
var resourcePath = this.resourcePath;
30-
var extensionMatcher = /\.(sass|scss|css)$/;
3132
var result;
32-
var fileExt;
3333
var opt;
34-
var contextMatch;
35-
var extension;
3634

3735
/**
3836
* Enhances the sass error with additional information about what actually went wrong.
@@ -70,31 +68,82 @@ module.exports = function (content) {
7068
function getWebpackImporter() {
7169
if (isSync) {
7270
return function syncWebpackImporter(url, context) {
73-
url = urlToRequest(url, context);
71+
url = utils.urlToRequest(url, opt.root);
7472
context = normalizeContext(context);
7573

76-
return syncResolve(self, url, context);
74+
return syncResolve(context, url, getImportsToResolve(url));
7775
};
7876
}
7977
return function asyncWebpackImporter(url, context, done) {
80-
url = urlToRequest(url, context);
78+
url = utils.urlToRequest(url, opt.root);
8179
context = normalizeContext(context);
8280

83-
asyncResolve(self, url, context, done);
81+
asyncResolve(context, url, getImportsToResolve(url), done);
8482
};
8583
}
8684

87-
function urlToRequest(url, context) {
88-
contextMatch = context.match(extensionMatcher);
85+
/**
86+
* Tries to resolve the given url synchronously. If a resolve error occurs, a second try for the same
87+
* module prefixed with an underscore is started.
88+
*
89+
* @param {object} loaderContext
90+
* @param {string} url
91+
//* @param {string} context
92+
* @returns {object}
93+
*/
94+
function syncResolve(fileContext, originalImport, importsToResolve) {
95+
var importToResolve = importsToResolve.shift();
96+
var resolvedFilename;
8997

90-
// Add sass/scss/css extension if it is missing
91-
// The extension is inherited from importing resource or the default is used
92-
if (!url.match(extensionMatcher)) {
93-
extension = contextMatch && contextMatch[0] || fileExt;
94-
url = url + extension;
98+
if (!importToResolve) {
99+
return {
100+
file: originalImport
101+
};
95102
}
96103

97-
return utils.urlToRequest(url, opt.root);
104+
try {
105+
resolvedFilename = self.resolveSync(fileContext, importToResolve);
106+
resolvedFilename = resolvedFilename.replace(matchCss, '');
107+
return {
108+
file: resolvedFilename
109+
};
110+
} catch (err) {
111+
return syncResolve(fileContext, originalImport, importsToResolve);
112+
}
113+
}
114+
115+
/**
116+
* Tries to resolve the given url asynchronously. If a resolve error occurs, a second try for the same
117+
* module prefixed with an underscore is started.
118+
*
119+
* @param {object} loaderContext
120+
* @param {string} url
121+
* @param {string} fileContext
122+
* @param {function} done
123+
*/
124+
function asyncResolve(fileContext, originalImport, importsToResolve, done) {
125+
var importToResolve = importsToResolve.shift();
126+
127+
if (!importToResolve) {
128+
done({
129+
file: originalImport
130+
});
131+
return;
132+
}
133+
134+
self.resolve(fileContext, importToResolve, function onWebpackResolve(err, resolvedFilename) {
135+
if (err) {
136+
asyncResolve(fileContext, originalImport, importsToResolve, done);
137+
return;
138+
}
139+
// Use self.loadModule() before calling done() to make imported files available to
140+
// other webpack tools like postLoaders etc.?
141+
142+
resolvedFilename = resolvedFilename.replace(matchCss, '');
143+
done({
144+
file: resolvedFilename.replace(matchCss, '')
145+
});
146+
});
98147
}
99148

100149
function normalizeContext(context) {
@@ -145,7 +194,6 @@ module.exports = function (content) {
145194

146195
// indentedSyntax is a boolean flag
147196
opt.indentedSyntax = Boolean(opt.indentedSyntax);
148-
fileExt = '.' + (opt.indentedSyntax? 'sass' : 'scss');
149197

150198
// opt.importer
151199
opt.importer = getWebpackImporter();
@@ -208,94 +256,39 @@ function getFileExcerptIfPossible(err) {
208256
}
209257
}
210258

211-
/**
212-
* Tries to resolve the given url synchronously. If a resolve error occurs, a second try for the same
213-
* module prefixed with an underscore is started.
214-
*
215-
* @param {object} loaderContext
216-
* @param {string} url
217-
* @param {string} context
218-
* @returns {object}
219-
*/
220-
function syncResolve(loaderContext, url, context) {
221-
var filename;
222-
var basename;
223-
224-
try {
225-
filename = loaderContext.resolveSync(context, url);
226-
} catch (err) {
227-
basename = path.basename(url);
228-
if (requiresLookupForUnderscoreModule(err, basename)) {
229-
url = addUnderscoreToBasename(url, basename);
230-
return syncResolve(loaderContext, url, context);
231-
}
259+
function getImportsToResolve(originalImport) {
260+
var ext = path.extname(originalImport);
261+
var basename = path.basename(originalImport);
262+
var dirname = path.dirname(originalImport);
263+
var startsWithUnderscore = basename.charAt(0) === '_';
264+
var paths = [];
232265

233-
// let the libsass do the rest job, e.g. search module in includePaths
234-
filename = path.join(path.dirname(url), removeUnderscoreFromBasename(basename));
266+
function add(file) {
267+
paths.push(dirname + path.sep + file);
235268
}
236269

237-
return {
238-
file: filename
239-
};
240-
}
241-
242-
/**
243-
* Tries to resolve the given url asynchronously. If a resolve error occurs, a second try for the same
244-
* module prefixed with an underscore is started.
245-
*
246-
* @param {object} loaderContext
247-
* @param {string} url
248-
* @param {string} context
249-
* @param {function} done
250-
*/
251-
function asyncResolve(loaderContext, url, context, done) {
252-
loaderContext.resolve(context, url, function onWebpackResolve(err, filename) {
253-
var basename;
254-
255-
if (err) {
256-
basename = path.basename(url);
257-
if (requiresLookupForUnderscoreModule(err, basename)) {
258-
url = addUnderscoreToBasename(url, basename);
259-
return asyncResolve(loaderContext, url, context, done);
260-
}
261-
262-
// Let libsass do the rest of the job, like searching for the module in includePaths
263-
filename = path.join(path.dirname(url), removeUnderscoreFromBasename(basename));
270+
if (originalImport.charAt(0) !== '.') {
271+
if (dirname === '.') {
272+
return [originalImport];
264273
}
265-
266-
// Use self.loadModule() before calling done() to make imported files available to
267-
// other webpack tools like postLoaders etc.?
268-
269-
done({
270-
file: filename
274+
}
275+
if (ext) {
276+
if (ext === '.scss' || ext === '.sass') {
277+
add(basename);
278+
if (!startsWithUnderscore) {
279+
add('_' + basename);
280+
}
281+
}/* else {
282+
Leave unknown extensions (like .css) untouched
283+
}*/
284+
} else {
285+
extPrecedence.forEach(function (ext) {
286+
add('_' + basename + ext);
271287
});
272-
});
273-
}
274-
275-
/**
276-
* Check whether its a resolve error and the basename does *not* start with an underscore.
277-
*
278-
* @param {Error} err
279-
* @param {string} basename
280-
* @returns {boolean}
281-
*/
282-
function requiresLookupForUnderscoreModule(err, basename) {
283-
return resolveError.test(err.message) && basename.charAt(0) !== '_';
284-
}
285-
286-
/**
287-
* @param {string} url
288-
* @param {string} basename
289-
* @returns {string}
290-
*/
291-
function addUnderscoreToBasename(url, basename) {
292-
return url.slice(0, -basename.length) + '_' + basename;
293-
}
288+
extPrecedence.forEach(function (ext) {
289+
add(basename + ext);
290+
});
291+
}
294292

295-
/**
296-
* @param {string} basename
297-
* @returns {string}
298-
*/
299-
function removeUnderscoreFromBasename(basename) {
300-
return basename[0] === '_' ? basename.substring(1) : basename;
293+
return paths;
301294
}

0 commit comments

Comments
 (0)