8
8
9
9
import type { PartialMessage , Plugin , PluginBuild } from 'esbuild' ;
10
10
import { readFile } from 'node:fs/promises' ;
11
- import { dirname , relative } from 'node:path' ;
11
+ import { dirname , join , relative } from 'node:path' ;
12
12
import { fileURLToPath , pathToFileURL } from 'node:url' ;
13
13
import type { CompileResult , Exception } from 'sass' ;
14
- import { SassWorkerImplementation } from '../../sass/sass-service' ;
14
+ import {
15
+ FileImporterWithRequestContextOptions ,
16
+ SassWorkerImplementation ,
17
+ } from '../../sass/sass-service' ;
15
18
16
- let sassWorkerPool : SassWorkerImplementation ;
19
+ let sassWorkerPool : SassWorkerImplementation | undefined ;
17
20
18
21
function isSassException ( error : unknown ) : error is Exception {
19
22
return ! ! error && typeof error === 'object' && 'sassMessage' in error ;
20
23
}
21
24
22
25
export function shutdownSassWorkerPool ( ) : void {
23
26
sassWorkerPool ?. close ( ) ;
27
+ sassWorkerPool = undefined ;
24
28
}
25
29
26
30
export function createSassPlugin ( options : { sourcemap : boolean ; loadPaths ?: string [ ] } ) : Plugin {
@@ -41,6 +45,61 @@ export function createSassPlugin(options: { sourcemap: boolean; loadPaths?: stri
41
45
sourceMap : options . sourcemap ,
42
46
sourceMapIncludeSources : options . sourcemap ,
43
47
quietDeps : true ,
48
+ importers : [
49
+ {
50
+ findFileUrl : async (
51
+ url ,
52
+ { previousResolvedModules } : FileImporterWithRequestContextOptions ,
53
+ ) : Promise < URL | null > => {
54
+ let result = await build . resolve ( url , {
55
+ kind : 'import-rule' ,
56
+ // This should ideally be the directory of the importer file from Sass
57
+ // but that is not currently available from the Sass importer API.
58
+ resolveDir : build . initialOptions . absWorkingDir ,
59
+ } ) ;
60
+
61
+ // Workaround to support Yarn PnP without access to the importer file from Sass
62
+ if ( ! result . path && previousResolvedModules ?. size ) {
63
+ for ( const previous of previousResolvedModules ) {
64
+ result = await build . resolve ( url , {
65
+ kind : 'import-rule' ,
66
+ resolveDir : previous ,
67
+ } ) ;
68
+ }
69
+ }
70
+
71
+ // Check for package deep imports
72
+ if ( ! result . path ) {
73
+ const parts = url . split ( '/' ) ;
74
+ const hasScope = parts . length > 2 && parts [ 0 ] . startsWith ( '@' ) ;
75
+ if ( hasScope || parts . length > 1 ) {
76
+ const [ nameOrScope , nameOrFirstPath , ...pathPart ] = parts ;
77
+ const packageName = hasScope
78
+ ? `${ nameOrScope } /${ nameOrFirstPath } `
79
+ : nameOrScope ;
80
+ const packageResult = await build . resolve ( packageName + '/package.json' , {
81
+ kind : 'import-rule' ,
82
+ // This should ideally be the directory of the importer file from Sass
83
+ // but that is not currently available from the Sass importer API.
84
+ resolveDir : build . initialOptions . absWorkingDir ,
85
+ } ) ;
86
+
87
+ if ( packageResult . path ) {
88
+ return pathToFileURL (
89
+ join (
90
+ dirname ( packageResult . path ) ,
91
+ ! hasScope ? nameOrFirstPath : '' ,
92
+ ...pathPart ,
93
+ ) ,
94
+ ) ;
95
+ }
96
+ }
97
+ }
98
+
99
+ return result . path ? pathToFileURL ( result . path ) : null ;
100
+ } ,
101
+ } ,
102
+ ] ,
44
103
logger : {
45
104
warn : ( text , { deprecation, span } ) => {
46
105
warnings . push ( {
0 commit comments