@@ -2,66 +2,58 @@ import path from 'path';
22import fs from 'fs' ;
33import sass from 'sass' ;
44
5+ const DEFAULT_EXTS = [ 'scss' , 'sass' , 'css' ] ;
6+
7+ export function resolveUrls ( url : string , extensions : string [ ] = DEFAULT_EXTS ) {
8+ // We only care about tilde-prefixed imports that do not look like paths.
9+ if ( ! url . startsWith ( '~' ) || url . startsWith ( '~/' ) ) {
10+ return [ ] ;
11+ }
12+
13+ const module_path = path . join ( 'node_modules' , url . substring ( 1 ) ) ;
14+ let variants = [ module_path ] ;
15+
16+ const parts = path . parse ( module_path ) ;
17+
18+ // Support sass partials by including paths where the file is prefixed by an underscore.
19+ if ( ! parts . base . startsWith ( '_' ) ) {
20+ const underscore_name = '_' . concat ( parts . name ) ;
21+ const replacement = {
22+ root : parts . root ,
23+ dir : parts . dir ,
24+ ext : parts . ext ,
25+ base : `${ underscore_name } ${ parts . ext } ` ,
26+ name : underscore_name ,
27+ } ;
28+ variants . push ( path . format ( replacement ) ) ;
29+ }
30+
31+ // Support index files.
32+ variants . push ( path . join ( module_path , '_index' ) ) ;
33+
34+ // Create variants such that it has entries of the form
35+ // node_modules/@foo /bar/baz.(scss|sass)
36+ // for an import of the form ~@foo /bar/baz(.(scss|sass))?
37+ if ( ! extensions . some ( ( ext ) => parts . ext == `.${ ext } ` ) ) {
38+ variants = extensions . flatMap ( ( ext ) =>
39+ variants . map ( ( variant ) => `${ variant } .${ ext } ` ) ,
40+ ) ;
41+ }
42+
43+ return variants ;
44+ }
45+
546/**
647 * Creates a sass importer which resolves Webpack-style tilde-imports.
748 */
849export const sassTildeImporter : sass . FileImporter < 'sync' > = {
950 findFileUrl ( url ) {
10- // We only care about tilde-prefixed imports that do not look like paths.
11- if ( ! url . startsWith ( '~' ) || url . startsWith ( '~/' ) ) {
12- return null ;
13- }
14-
15- // Create subpathsWithExts such that it has entries of the form
16- // node_modules/@foo /bar/baz.(scss|sass)
17- // for an import of the form ~@foo /bar/baz(.(scss|sass))?
18- const nodeModSubpath = path . join ( 'node_modules' , url . substring ( 1 ) ) ;
19- const subpathsWithExts : string [ ] = [ ] ;
20- if (
21- nodeModSubpath . endsWith ( '.scss' ) ||
22- nodeModSubpath . endsWith ( '.sass' ) ||
23- nodeModSubpath . endsWith ( '.css' )
24- ) {
25- subpathsWithExts . push ( nodeModSubpath ) ;
26- } else {
27- // Look for .scss first.
28- subpathsWithExts . push (
29- `${ nodeModSubpath } .scss` ,
30- `${ nodeModSubpath } .sass` ,
31- `${ nodeModSubpath } .css` ,
32- ) ;
33- }
34-
35- // Support index files.
36- subpathsWithExts . push (
37- `${ nodeModSubpath } /_index.scss` ,
38- `${ nodeModSubpath } /_index.sass` ,
39- ) ;
40-
41- // Support sass partials by including paths where the file is prefixed by an underscore.
42- const basename = path . basename ( nodeModSubpath ) ;
43- if ( ! basename . startsWith ( '_' ) ) {
44- const partials = subpathsWithExts . map ( ( file ) =>
45- file . replace ( basename , `_${ basename } ` ) ,
46- ) ;
47- subpathsWithExts . push ( ...partials ) ;
48- }
51+ const searchPaths = resolveUrls ( url ) ;
4952
50- // Climbs the filesystem tree until we get to the root, looking for the first
51- // node_modules directory which has a matching module and filename.
52- let prevDir = '' ;
53- let dir = path . dirname ( url ) ;
54- while ( prevDir !== dir ) {
55- const searchPaths = subpathsWithExts . map ( ( subpathWithExt ) =>
56- path . join ( dir , subpathWithExt ) ,
57- ) ;
58- for ( const searchPath of searchPaths ) {
59- if ( fs . existsSync ( searchPath ) ) {
60- return new URL ( `file://${ path . resolve ( searchPath ) } ` ) ;
61- }
53+ for ( const searchPath of searchPaths ) {
54+ if ( fs . existsSync ( searchPath ) ) {
55+ return new URL ( `file://${ path . resolve ( searchPath ) } ` ) ;
6256 }
63- prevDir = dir ;
64- dir = path . dirname ( dir ) ;
6557 }
6658
6759 // Returning null is not itself an error, it tells sass to instead try the
0 commit comments