@@ -15,14 +15,15 @@ var SassError = {
15
15
status : 1
16
16
} ;
17
17
18
+ // libsass uses this precedence when importing files without extension
18
19
var extPrecedence = [ '.scss' , '.sass' , '.css' ] ;
19
20
var matchCss = / \. c s s $ / ;
20
21
21
22
/**
22
23
* The sass-loader makes node-sass available to webpack modules.
23
24
*
24
25
* @param {string } content
25
- * @returns {* }
26
+ * @returns {string }
26
27
*/
27
28
module . exports = function ( content ) {
28
29
var callback = this . async ( ) ;
@@ -69,93 +70,106 @@ module.exports = function (content) {
69
70
*/
70
71
function getWebpackImporter ( ) {
71
72
if ( isSync ) {
72
- return function syncWebpackImporter ( url , context ) {
73
+ return function syncWebpackImporter ( url , fileContext ) {
74
+ var dirContext ;
75
+
73
76
url = utils . urlToRequest ( url , opt . root ) ;
74
- context = normalizeContext ( context ) ;
77
+ dirContext = fileToDirContext ( fileContext ) ;
75
78
76
- return syncResolve ( context , url , getImportsToResolve ( url ) ) ;
79
+ return resolveSync ( dirContext , url , getImportsToResolve ( url ) ) ;
77
80
} ;
78
81
}
79
- return function asyncWebpackImporter ( url , context , done ) {
82
+ return function asyncWebpackImporter ( url , fileContext , done ) {
83
+ var dirContext ;
84
+
80
85
url = utils . urlToRequest ( url , opt . root ) ;
81
- context = normalizeContext ( context ) ;
86
+ dirContext = fileToDirContext ( fileContext ) ;
82
87
83
- asyncResolve ( context , url , getImportsToResolve ( url ) , done ) ;
88
+ resolve ( dirContext , url , getImportsToResolve ( url ) , done ) ;
84
89
} ;
85
90
}
86
91
87
92
/**
88
- * Tries to resolve the given url synchronously . If a resolve error occurs, a second try for the same
89
- * module prefixed with an underscore is started .
93
+ * Tries to resolve the first url of importsToResolve . If that resolve fails, the next url is tried.
94
+ * If all imports fail, the import is passed to libsass which also take includePaths into account .
90
95
*
91
- * @param {object } loaderContext
92
- * @param {string } url
93
- // * @param {string } context
96
+ * @param {string } dirContext
97
+ * @param {string } originalImport
98
+ * @param {Array } importsToResolve
94
99
* @returns {object }
95
100
*/
96
- function syncResolve ( fileContext , originalImport , importsToResolve ) {
101
+ function resolveSync ( dirContext , originalImport , importsToResolve ) {
97
102
var importToResolve = importsToResolve . shift ( ) ;
98
103
var resolvedFilename ;
99
104
100
105
if ( ! importToResolve ) {
106
+ // No import possibilities left. Let's pass that one back to libsass...
101
107
return {
102
108
file : originalImport
103
109
} ;
104
110
}
105
111
106
112
try {
107
- resolvedFilename = self . resolveSync ( fileContext , importToResolve ) ;
113
+ resolvedFilename = self . resolveSync ( dirContext , importToResolve ) ;
114
+ // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
115
+ // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
108
116
self . dependency ( resolvedFilename ) ;
117
+ // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
109
118
resolvedFilename = resolvedFilename . replace ( matchCss , '' ) ;
110
119
return {
111
120
file : resolvedFilename
112
121
} ;
113
122
} catch ( err ) {
114
- return syncResolve ( fileContext , originalImport , importsToResolve ) ;
123
+ return resolveSync ( dirContext , originalImport , importsToResolve ) ;
115
124
}
116
125
}
117
126
118
127
/**
119
- * Tries to resolve the given url asynchronously . If a resolve error occurs, a second try for the same
120
- * module prefixed with an underscore is started .
128
+ * Tries to resolve the first url of importsToResolve . If that resolve fails, the next url is tried.
129
+ * If all imports fail, the import is passed to libsass which also take includePaths into account .
121
130
*
122
- * @param {object } loaderContext
123
- * @param {string } url
124
- * @param {string } fileContext
131
+ * @param {string } dirContext
132
+ * @param {string } originalImport
133
+ * @param {Array } importsToResolve
125
134
* @param {function } done
126
135
*/
127
- function asyncResolve ( fileContext , originalImport , importsToResolve , done ) {
136
+ function resolve ( dirContext , originalImport , importsToResolve , done ) {
128
137
var importToResolve = importsToResolve . shift ( ) ;
129
138
130
139
if ( ! importToResolve ) {
140
+ // No import possibilities left. Let's pass that one back to libsass...
131
141
done ( {
132
142
file : originalImport
133
143
} ) ;
134
144
return ;
135
145
}
136
146
137
- self . resolve ( fileContext , importToResolve , function onWebpackResolve ( err , resolvedFilename ) {
147
+ self . resolve ( dirContext , importToResolve , function onWebpackResolve ( err , resolvedFilename ) {
138
148
if ( err ) {
139
- asyncResolve ( fileContext , originalImport , importsToResolve , done ) ;
149
+ resolve ( dirContext , originalImport , importsToResolve , done ) ;
140
150
return ;
141
151
}
152
+ // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
153
+ // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
142
154
self . dependency ( resolvedFilename ) ;
155
+ // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
156
+ resolvedFilename = resolvedFilename . replace ( matchCss , '' ) ;
157
+
143
158
// Use self.loadModule() before calling done() to make imported files available to
144
159
// other webpack tools like postLoaders etc.?
145
160
146
- resolvedFilename = resolvedFilename . replace ( matchCss , '' ) ;
147
161
done ( {
148
162
file : resolvedFilename . replace ( matchCss , '' )
149
163
} ) ;
150
164
} ) ;
151
165
}
152
166
153
- function normalizeContext ( context ) {
167
+ function fileToDirContext ( fileContext ) {
154
168
// The first file is 'stdin' when we're using the data option
155
- if ( context === 'stdin' ) {
156
- context = resourcePath ;
169
+ if ( fileContext === 'stdin' ) {
170
+ fileContext = resourcePath ;
157
171
}
158
- return path . dirname ( context ) ;
172
+ return path . dirname ( fileContext ) ;
159
173
}
160
174
161
175
// When files have been imported via the includePaths-option, these files need to be
@@ -266,6 +280,22 @@ function getFileExcerptIfPossible(err) {
266
280
}
267
281
}
268
282
283
+ /**
284
+ * When libsass tries to resolve an import, it uses this "funny" algorithm:
285
+ *
286
+ * - Imports with no file extension:
287
+ * - Prefer modules starting with '_'
288
+ * - File extension precedence: .scss, .sass, .css
289
+ * - Imports with file extension:
290
+ * - If the file is a CSS-file, do not include it all, but just link it via @import url()
291
+ * - The exact file name must match (no auto-resolving of '_'-modules)
292
+ *
293
+ * Since the sass-loader uses webpack to resolve the modules, we need to simulate that algorithm. This function
294
+ * returns an array of import paths to try.
295
+ *
296
+ * @param {string } originalImport
297
+ * @returns {Array }
298
+ */
269
299
function getImportsToResolve ( originalImport ) {
270
300
var ext = path . extname ( originalImport ) ;
271
301
var basename = path . basename ( originalImport ) ;
@@ -278,23 +308,26 @@ function getImportsToResolve(originalImport) {
278
308
}
279
309
280
310
if ( originalImport . charAt ( 0 ) !== '.' ) {
311
+ // If true: originalImport is a module import like 'bootstrap-sass...'
281
312
if ( dirname === '.' ) {
313
+ // If true: originalImport is just a module import without a path like 'bootstrap-sass'
314
+ // In this case we don't do that auto-resolving dance at all.
282
315
return [ originalImport ] ;
283
316
}
284
317
}
285
318
if ( ext ) {
286
319
if ( ext === '.scss' || ext === '.sass' ) {
287
320
add ( basename ) ;
288
- if ( ! startsWithUnderscore ) {
289
- add ( '_' + basename ) ;
290
- }
291
321
} /* else {
292
322
Leave unknown extensions (like .css) untouched
293
323
}*/
294
324
} else {
295
- extPrecedence . forEach ( function ( ext ) {
296
- add ( '_' + basename + ext ) ;
297
- } ) ;
325
+ if ( ! startsWithUnderscore ) {
326
+ // Prefer modules starting with '_' first
327
+ extPrecedence . forEach ( function ( ext ) {
328
+ add ( '_' + basename + ext ) ;
329
+ } ) ;
330
+ }
298
331
extPrecedence . forEach ( function ( ext ) {
299
332
add ( basename + ext ) ;
300
333
} ) ;
0 commit comments