@@ -8,9 +8,15 @@ import * as PlatformError from "effect/PlatformError";
88
99import * as WorkspacePaths from "../workspace/WorkspacePaths.ts" ;
1010import * as ProjectFaviconResolver from "./ProjectFaviconResolver.ts" ;
11+ import * as T3ProjectFileLoader from "./T3ProjectFileLoader.ts" ;
1112
1213const TestLayer = Layer . empty . pipe (
13- Layer . provideMerge ( ProjectFaviconResolver . layer . pipe ( Layer . provide ( WorkspacePaths . layer ) ) ) ,
14+ Layer . provideMerge (
15+ ProjectFaviconResolver . layer . pipe (
16+ Layer . provide ( WorkspacePaths . layer ) ,
17+ Layer . provide ( T3ProjectFileLoader . layer ) ,
18+ ) ,
19+ ) ,
1420 Layer . provideMerge ( NodeServices . layer ) ,
1521) ;
1622
@@ -37,7 +43,7 @@ const writeTextFile = Effect.fn("writeTextFile")(function* (
3743
3844const makeResolverWithFileSystem = ( fileSystem : FileSystem . FileSystem ) =>
3945 ProjectFaviconResolver . make . pipe (
40- Effect . provide ( WorkspacePaths . layer ) ,
46+ Effect . provide ( [ WorkspacePaths . layer , T3ProjectFileLoader . layer ] ) ,
4147 Effect . provideService ( FileSystem . FileSystem , fileSystem ) ,
4248 ) ;
4349
@@ -56,6 +62,63 @@ it.layer(TestLayer)("ProjectFaviconResolverLive", (it) => {
5662 } ) ,
5763 ) ;
5864
65+ it . effect ( "prefers a t3.json iconPath over well-known files" , ( ) =>
66+ Effect . gen ( function * ( ) {
67+ const resolver = yield * ProjectFaviconResolver . ProjectFaviconResolver ;
68+ const cwd = yield * makeTempDir ;
69+ yield * writeTextFile ( cwd , "t3.json" , '{ "iconPath": "brand/mark.svg" }' ) ;
70+ yield * writeTextFile ( cwd , "brand/mark.svg" , "<svg>mark</svg>" ) ;
71+ yield * writeTextFile ( cwd , "favicon.svg" , "<svg>favicon</svg>" ) ;
72+
73+ const resolved = yield * resolver . resolvePath ( cwd ) ;
74+
75+ expect ( resolved ) . not . toBeNull ( ) ;
76+ expect ( resolved ) . toContain ( "brand/mark.svg" ) ;
77+ } ) ,
78+ ) ;
79+
80+ it . effect ( "falls back to well-known files when the t3.json iconPath does not exist" , ( ) =>
81+ Effect . gen ( function * ( ) {
82+ const resolver = yield * ProjectFaviconResolver . ProjectFaviconResolver ;
83+ const cwd = yield * makeTempDir ;
84+ yield * writeTextFile ( cwd , "t3.json" , '{ "iconPath": "brand/missing.svg" }' ) ;
85+ yield * writeTextFile ( cwd , "favicon.svg" , "<svg>favicon</svg>" ) ;
86+
87+ const resolved = yield * resolver . resolvePath ( cwd ) ;
88+
89+ expect ( resolved ) . not . toBeNull ( ) ;
90+ expect ( resolved ) . toContain ( "favicon.svg" ) ;
91+ } ) ,
92+ ) ;
93+
94+ it . effect ( "ignores invalid t3.json files" , ( ) =>
95+ Effect . gen ( function * ( ) {
96+ const resolver = yield * ProjectFaviconResolver . ProjectFaviconResolver ;
97+ const cwd = yield * makeTempDir ;
98+ yield * writeTextFile ( cwd , "t3.json" , "{ not json" ) ;
99+ yield * writeTextFile ( cwd , "favicon.svg" , "<svg>favicon</svg>" ) ;
100+
101+ const resolved = yield * resolver . resolvePath ( cwd ) ;
102+
103+ expect ( resolved ) . not . toBeNull ( ) ;
104+ expect ( resolved ) . toContain ( "favicon.svg" ) ;
105+ } ) ,
106+ ) ;
107+
108+ it . effect ( "does not resolve a t3.json iconPath outside the workspace root" , ( ) =>
109+ Effect . gen ( function * ( ) {
110+ const resolver = yield * ProjectFaviconResolver . ProjectFaviconResolver ;
111+ const parent = yield * makeTempDir ;
112+ const cwd = `${ parent } /app` ;
113+ yield * writeTextFile ( parent , "secret.svg" , "<svg>secret</svg>" ) ;
114+ yield * writeTextFile ( cwd , "t3.json" , '{ "iconPath": "../secret.svg" }' ) ;
115+
116+ const resolved = yield * resolver . resolvePath ( cwd ) ;
117+
118+ expect ( resolved ) . toBeNull ( ) ;
119+ } ) ,
120+ ) ;
121+
59122 it . effect ( "resolves icon hrefs from project source files" , ( ) =>
60123 Effect . gen ( function * ( ) {
61124 const resolver = yield * ProjectFaviconResolver . ProjectFaviconResolver ;
0 commit comments