@@ -303,6 +303,7 @@ func TestContentMapperPackageMustBeInWorkspace(t *testing.T) {
303303 name string
304304 projectDirectory string
305305 workspaceRoots []string
306+ mapperPackage string
306307 mapperPath string
307308 mapperTarget string
308309 allowed bool
@@ -314,6 +315,30 @@ func TestContentMapperPackageMustBeInWorkspace(t *testing.T) {
314315 mapperPath : "/home/trusted/node_modules/mapper/package.json" ,
315316 allowed : true ,
316317 },
318+ {
319+ name : "directory mapper in workspace" ,
320+ projectDirectory : "/home/trusted" ,
321+ workspaceRoots : []string {"/home/trusted" },
322+ mapperPackage : "./mapper" ,
323+ mapperPath : "/home/trusted/mapper/package.json" ,
324+ allowed : true ,
325+ },
326+ {
327+ name : "workspace directory mapper symlinked outside" ,
328+ projectDirectory : "/home/trusted" ,
329+ workspaceRoots : []string {"/home/trusted" },
330+ mapperPackage : "./mapper" ,
331+ mapperPath : "/home/trusted/mapper" ,
332+ mapperTarget : "/home/outside/mapper" ,
333+ allowed : true ,
334+ },
335+ {
336+ name : "directory mapper outside workspace" ,
337+ projectDirectory : "/home/untrusted" ,
338+ workspaceRoots : []string {"/home/trusted" },
339+ mapperPackage : "./mapper" ,
340+ mapperPath : "/home/untrusted/mapper/package.json" ,
341+ },
317342 {
318343 name : "loose file outside workspace" ,
319344 projectDirectory : "/home/untrusted" ,
@@ -357,10 +382,14 @@ func TestContentMapperPackageMustBeInWorkspace(t *testing.T) {
357382 configPath := test .projectDirectory + "/tsconfig.json"
358383 mainPath := test .projectDirectory + "/main.ts"
359384 boxPath := test .projectDirectory + "/app.box"
385+ mapperPackage := test .mapperPackage
386+ if mapperPackage == "" {
387+ mapperPackage = "mapper"
388+ }
360389 files := map [string ]any {
361390 configPath : `{
362391 "compilerOptions": { "target": "es2020", "module": "esnext", "moduleResolution": "bundler" },
363- "contentMappers": [ { "package": "mapper ", "extensions": [".box"] } ]
392+ "contentMappers": [ { "package": "` + mapperPackage + ` ", "extensions": [".box"] } ]
364393 }` ,
365394 mainPath : "import { version } from \" ./app.box\" ;\n export { version };\n " ,
366395 boxPath : "export const version = #{target};\n " ,
@@ -491,6 +520,65 @@ func TestContentMapperPackageManifestChangeReloadsConfig(t *testing.T) {
491520 assert .Assert (t , strings .Contains (boxFile .Text (), "export const version = 7;" ), "expected fixed mapper manifest to be reloaded: %q" , boxFile .Text ())
492521}
493522
523+ func TestRelativeSymlinkedContentMapperPackageManifestChangeReloadsConfig (t * testing.T ) {
524+ t .Parallel ()
525+ const packageJsonPath = "/home/mapper/package.json"
526+ files := map [string ]any {
527+ "/home/project/tsconfig.json" : `{
528+ "compilerOptions": { "target": "es2020", "module": "esnext", "moduleResolution": "bundler" },
529+ "contentMappers": [{ "package": "./mapper", "extensions": [".box"] }]
530+ }` ,
531+ "/home/project/mapper" : vfstest .Symlink ("/home/mapper" ),
532+ packageJsonPath : `{
533+ "name": "mapper",
534+ "version": "1.0.0",
535+ "typescript": { "contentMapper": { "exec": ["compiler-test-mapper"] } }
536+ }` ,
537+ "/home/project/app.box" : "export const version = #{target};\n " ,
538+ "/home/project/main.ts" : "import { version } from \" ./app.box\" ;\n " ,
539+ }
540+ caps := & lsproto.ResolvedClientCapabilities {}
541+ caps .Workspace .DidChangeWatchedFiles .RelativePatternSupport = true
542+ ctx := lsproto .WithClientCapabilities (context .Background (), caps )
543+ init , utils := projecttestutil .GetSessionInitOptions (files , & project.SessionOptions {
544+ CurrentDirectory : "/home/project" ,
545+ DefaultLibraryPath : bundled .LibPath (),
546+ TypingsLocation : projecttestutil .TestTypingsLocation ,
547+ PositionEncoding : lsproto .PositionEncodingKindUTF8 ,
548+ RunExternalCode : true ,
549+ WatchEnabled : true ,
550+ }, nil )
551+ init .BackgroundCtx = ctx
552+ init .Spawner = contentmappertest .NewSpawner ()
553+ session := project .NewSession (init )
554+ defer session .Close ()
555+
556+ mainURI := lsproto .DocumentUri ("file:///home/project/main.ts" )
557+ session .DidOpenFile (ctx , mainURI , 1 , files ["/home/project/main.ts" ].(string ), lsproto .LanguageKindTypeScript )
558+ _ , err := session .GetLanguageService (ctx , mainURI )
559+ assert .NilError (t , err )
560+ configuredProject := session .Snapshot ().GetDefaultProject (mainURI )
561+ assert .Assert (t , configuredProject != nil )
562+ mappers := configuredProject .CommandLine .ContentMappers ()
563+ assert .Equal (t , len (mappers ), 1 )
564+ assert .Equal (t , mappers [0 ].PackageDirectory , tspath .RootedDirectoryPath ("/home/mapper" ))
565+ session .WaitForBackgroundTasks ()
566+ assert .Assert (t , utils .WatchesFile (packageJsonPath ), "expected the mapper package manifest to be watched" )
567+
568+ fixedManifest := contentmappertest .PackageJSON (contentmappertest .TransformingMapper )
569+ assert .NilError (t , utils .FS ().WriteFile (packageJsonPath , fixedManifest ))
570+ session .DidChangeWatchedFiles (ctx , []* lsproto.FileEvent {{
571+ Uri : lsproto .DocumentUri ("file://" + packageJsonPath ),
572+ Type : lsproto .FileChangeTypeChanged ,
573+ }})
574+
575+ languageService , err := session .GetLanguageService (ctx , mainURI )
576+ assert .NilError (t , err )
577+ boxFile := languageService .GetProgram ().GetSourceFile ("/home/project/app.box" )
578+ assert .Assert (t , boxFile != nil , "expected app.box in the rebuilt program" )
579+ assert .Assert (t , strings .Contains (boxFile .Text (), "export const version = 7;" ), "expected fixed mapper manifest to be reloaded: %q" , boxFile .Text ())
580+ }
581+
494582func TestContentMapperSupplementalFileClonedOnEdit (t * testing.T ) {
495583 t .Parallel ()
496584 files := map [string ]any {
0 commit comments