@@ -15,6 +15,7 @@ import { pathToFileURL } from 'node:url';
1515import { SassCompiler } from '../../sass/sass-service' ;
1616import {
1717 SassStylesheetLanguage ,
18+ getPackageScope ,
1819 isPackageUrl ,
1920 resetSassWorkerPoolCaches ,
2021 shutdownSassWorkerPool ,
@@ -59,6 +60,43 @@ describe('sass-language', () => {
5960 } ) ;
6061 } ) ;
6162
63+ describe ( 'getPackageScope' , ( ) => {
64+ const scope = ( containingPath ?: string ) => getPackageScope ( containingPath , '/app' ) ;
65+
66+ it ( 'should use the working directory for a stylesheet outside node_modules' , ( ) => {
67+ expect ( scope ( '/app/src/app.scss' ) ) . toBe ( '/app' ) ;
68+ expect ( scope ( '/app/src/foo-node_modules/a.scss' ) ) . toBe ( '/app' ) ;
69+ expect ( scope ( '/app/src/node_modules.scss' ) ) . toBe ( '/app' ) ;
70+ expect ( scope ( undefined ) ) . toBe ( '/app' ) ;
71+ } ) ;
72+
73+ it ( 'should use the enclosing package root for a stylesheet within node_modules' , ( ) => {
74+ expect ( scope ( '/app/node_modules/pkg/a.scss' ) ) . toBe ( '/app/node_modules/pkg' ) ;
75+ expect ( scope ( '/app/node_modules/pkg/sub/a.scss' ) ) . toBe ( '/app/node_modules/pkg' ) ;
76+ expect ( scope ( '/app/node_modules/@scope/pkg/sub/a.scss' ) ) . toBe ( '/app/node_modules/@scope/pkg' ) ;
77+ expect ( scope ( '/app/node_modules/foo-node_modules/a.scss' ) ) . toBe (
78+ '/app/node_modules/foo-node_modules' ,
79+ ) ;
80+ expect ( scope ( '/app/node_modules/a.scss' ) ) . toBe ( '/app/node_modules' ) ;
81+ } ) ;
82+
83+ it ( 'should use the innermost package root for a nested dependency' , ( ) => {
84+ expect ( scope ( '/app/node_modules/dep/node_modules/pkg/a.scss' ) ) . toBe (
85+ '/app/node_modules/dep/node_modules/pkg' ,
86+ ) ;
87+ expect ( scope ( '/app/node_modules/.pnpm/pkg@1.0.0/node_modules/pkg/a.scss' ) ) . toBe (
88+ '/app/node_modules/.pnpm/pkg@1.0.0/node_modules/pkg' ,
89+ ) ;
90+ } ) ;
91+
92+ it ( 'should support Windows path separators' , ( ) => {
93+ expect ( getPackageScope ( 'C:\\app\\node_modules\\pkg\\sub\\a.scss' , 'C:\\app' ) ) . toBe (
94+ 'C:/app/node_modules/pkg' ,
95+ ) ;
96+ expect ( getPackageScope ( 'C:\\app\\src\\app.scss' , 'C:\\app' ) ) . toBe ( 'C:\\app' ) ;
97+ } ) ;
98+ } ) ;
99+
62100 describe ( 'package resolution caching' , ( ) => {
63101 let temporaryRoot : string ;
64102 let projectRoot : string ;
@@ -205,6 +243,26 @@ describe('sass-language', () => {
205243 expect ( resolveRequests . length ) . toBe ( 1 ) ;
206244 } ) ;
207245
246+ it ( 'should share a package resolution between the stylesheets of different folders of a dependency' , async ( ) => {
247+ const dependencyRoot = join ( projectRoot , 'node_modules' , 'dependency' ) ;
248+ const first = await compile ( join ( dependencyRoot , 'sub1' , 'styles.scss' ) ) ;
249+ const second = await compile ( join ( dependencyRoot , 'sub2' , 'styles.scss' ) ) ;
250+
251+ expect ( first ) . toContain ( 'content: "dependency";' ) ;
252+ expect ( second ) . toContain ( 'content: "dependency";' ) ;
253+ expect ( resolveRequests . length ) . toBe ( 1 ) ;
254+ } ) ;
255+
256+ it ( 'should share a package resolution between the stylesheets of different folders of a scoped package' , async ( ) => {
257+ const packageRoot = join ( projectRoot , 'node_modules' , '@scope' , 'pkg' ) ;
258+ const first = await compile ( join ( packageRoot , 'sub1' , 'styles.scss' ) ) ;
259+ const second = await compile ( join ( packageRoot , 'sub2' , 'styles.scss' ) ) ;
260+
261+ expect ( first ) . toContain ( 'content: "project";' ) ;
262+ expect ( second ) . toContain ( 'content: "project";' ) ;
263+ expect ( resolveRequests . length ) . toBe ( 1 ) ;
264+ } ) ;
265+
208266 it ( 'should resolve a package url of a non-file containing URL from the working directory' , async ( ) => {
209267 // The stylesheets of a build have file URLs, but Sass does not limit a containing URL to them.
210268 spyOn ( SassCompiler . prototype , 'compileStringAsync' ) . and . callFake ( async ( _ , options ) => {
0 commit comments