9
9
import * as fs from 'fs' ;
10
10
import MiniCssExtractPlugin from 'mini-css-extract-plugin' ;
11
11
import * as path from 'path' ;
12
- import { Configuration , RuleSetUseItem } from 'webpack' ;
12
+ import type { FileImporter } from 'sass' ;
13
+ import { pathToFileURL } from 'url' ;
14
+ import type { Configuration , LoaderContext , RuleSetUseItem } from 'webpack' ;
13
15
import { StyleElement } from '../../builders/browser/schema' ;
14
16
import { SassWorkerImplementation } from '../../sass/sass-service' ;
15
17
import { SassLegacyWorkerImplementation } from '../../sass/sass-service-legacy' ;
@@ -267,11 +269,11 @@ export function getStylesConfig(wco: WebpackConfigOptions): Configuration {
267
269
loader : require . resolve ( 'sass-loader' ) ,
268
270
options : getSassLoaderOptions (
269
271
root ,
270
- projectRoot ,
271
272
sassImplementation ,
272
273
includePaths ,
273
274
false ,
274
275
! buildOptions . verbose ,
276
+ ! ! buildOptions . preserveSymlinks ,
275
277
) ,
276
278
} ,
277
279
] ,
@@ -289,11 +291,11 @@ export function getStylesConfig(wco: WebpackConfigOptions): Configuration {
289
291
loader : require . resolve ( 'sass-loader' ) ,
290
292
options : getSassLoaderOptions (
291
293
root ,
292
- projectRoot ,
293
294
sassImplementation ,
294
295
includePaths ,
295
296
true ,
296
297
! buildOptions . verbose ,
298
+ ! ! buildOptions . preserveSymlinks ,
297
299
) ,
298
300
} ,
299
301
] ,
@@ -376,29 +378,23 @@ function getTailwindConfigPath({ projectRoot, root }: WebpackConfigOptions): str
376
378
377
379
function getSassLoaderOptions (
378
380
root : string ,
379
- projectRoot : string ,
380
381
implementation : SassWorkerImplementation | SassLegacyWorkerImplementation ,
381
382
includePaths : string [ ] ,
382
383
indentedSyntax : boolean ,
383
384
verbose : boolean ,
385
+ preserveSymlinks : boolean ,
384
386
) : Record < string , unknown > {
385
387
return implementation instanceof SassWorkerImplementation
386
388
? {
387
389
sourceMap : true ,
388
390
api : 'modern' ,
389
391
implementation,
390
- // Webpack importer is only implemented in the legacy API.
392
+ // Webpack importer is only implemented in the legacy API and we have our own custom Webpack importer .
391
393
// See: https://github.com/webpack-contrib/sass-loader/blob/997f3eb41d86dd00d5fa49c395a1aeb41573108c/src/utils.js#L642-L651
392
394
webpackImporter : false ,
393
- sassOptions : {
394
- loadPaths : [
395
- ...includePaths ,
396
- // Needed to resolve node packages and retain the same behaviour of with the legacy API as sass-loader resolves
397
- // scss also from the cwd and project root.
398
- // See: https://github.com/webpack-contrib/sass-loader/blob/997f3eb41d86dd00d5fa49c395a1aeb41573108c/src/utils.js#L307
399
- projectRoot ,
400
- path . join ( root , 'node_modules' ) ,
401
- ] ,
395
+ sassOptions : ( loaderContext : LoaderContext < { } > ) => ( {
396
+ importers : [ getSassResolutionImporter ( loaderContext , root , preserveSymlinks ) ] ,
397
+ loadPaths : includePaths ,
402
398
// Use expanded as otherwise sass will remove comments that are needed for autoprefixer
403
399
// Ex: /* autoprefixer grid: autoplace */
404
400
// See: https://github.com/webpack-contrib/sass-loader/blob/45ad0be17264ceada5f0b4fb87e9357abe85c4ff/src/getSassOptions.js#L68-L70
@@ -407,7 +403,7 @@ function getSassLoaderOptions(
407
403
quietDeps : ! verbose ,
408
404
verbose,
409
405
syntax : indentedSyntax ? 'indented' : 'scss' ,
410
- } ,
406
+ } ) ,
411
407
}
412
408
: {
413
409
sourceMap : true ,
@@ -439,3 +435,42 @@ function getSassLoaderOptions(
439
435
} ,
440
436
} ;
441
437
}
438
+
439
+ function getSassResolutionImporter (
440
+ loaderContext : LoaderContext < { } > ,
441
+ root : string ,
442
+ preserveSymlinks : boolean ,
443
+ ) : FileImporter < 'async' > {
444
+ const commonResolverOptions : Parameters < typeof loaderContext [ 'getResolve' ] > [ 0 ] = {
445
+ conditionNames : [ 'sass' , 'style' ] ,
446
+ mainFields : [ 'sass' , 'style' , 'main' , '...' ] ,
447
+ extensions : [ '.scss' , '.sass' , '.css' ] ,
448
+ restrictions : [ / \. ( ( s a | s c | c ) s s ) $ / i] ,
449
+ preferRelative : true ,
450
+ symlinks : ! preserveSymlinks ,
451
+ } ;
452
+
453
+ // Sass also supports import-only files. If you name a file <name>.import.scss, it will only be loaded for imports, not for @uses.
454
+ // See: https://sass-lang.com/documentation/at-rules/import#import-only-files
455
+ const resolveImport = loaderContext . getResolve ( {
456
+ ...commonResolverOptions ,
457
+ dependencyType : 'sass-import' ,
458
+ mainFiles : [ '_index.import' , '_index' , 'index.import' , 'index' , '...' ] ,
459
+ } ) ;
460
+
461
+ const resolveModule = loaderContext . getResolve ( {
462
+ ...commonResolverOptions ,
463
+ dependencyType : 'sass-module' ,
464
+ mainFiles : [ '_index' , 'index' , '...' ] ,
465
+ } ) ;
466
+
467
+ return {
468
+ findFileUrl : ( url , { fromImport } ) : Promise < URL | null > => {
469
+ const resolve = fromImport ? resolveImport : resolveModule ;
470
+
471
+ return resolve ( root , url )
472
+ . then ( ( file ) => pathToFileURL ( file ) )
473
+ . catch ( ( ) => null ) ;
474
+ } ,
475
+ } ;
476
+ }
0 commit comments