@@ -26,6 +26,7 @@ import _root_.io.circe.*
2626import _root_ .io .circe .Encoder
2727
2828import ProxyConfig .Equilibrium
29+ import scribe .Scribe
2930
3031sealed trait FrontendEvent derives Encoder .AsObject
3132
5051 weight: 5
5152"""
5253
54+ case class IndexHtmlConfig (
55+ indexHtmlatPath : Option [IndexHtmlDir ],
56+ stylesOnly : Option [StyleDir ]
57+ )
58+
59+ type StyleDir = os.Path
60+ type IndexHtmlDir = os.Path
61+
5362object LiveServer
5463 extends CommandIOApp (
5564 name = " LiveServer" ,
@@ -104,14 +113,6 @@ object LiveServer
104113 )
105114 .validate(" Must be a directory" )(s => os.isDir(os.Path (s)))
106115
107- val stylesDirOpt = Opts
108- .option[String ](
109- " styles-dir" ,
110- " A fully qualified path to your styles directory with LESS files in - e.g. c:/temp/helloScalaJS/styles"
111- )
112- .orNone
113- .validate(" Must be a directory" )(sOpt => sOpt.fold(true )(s => os.isDir(os.Path (s))))
114-
115116 val portOpt = Opts
116117 .option[Int ](" port" , " The port you want to run the server on - e.g. 3000" )
117118 .withDefault(3000 )
@@ -149,35 +150,46 @@ object LiveServer
149150 )
150151 .orEmpty
151152
152- val indexHtmlTemplateOpt : Opts [Option [String ]] = Opts
153+ val stylesDirOpt : Opts [Option [StyleDir ]] = Opts
154+ .option[String ](
155+ " styles-dir" ,
156+ " A fully qualified path to your styles directory with LESS files in - e.g. c:/temp/helloScalaJS/styles"
157+ )
158+ .orNone
159+ .validate(" The styles-dir must be a directory, it should have index.less at it's root" )(
160+ sOpt => sOpt.fold(true )(s => os.isDir(os.Path (s)))
161+ )
162+ .map(_.map(os.Path (_)))
163+
164+ val indexHtmlTemplateOpt : Opts [Option [IndexHtmlDir ]] = Opts
153165 .option[String ](
154- " path-to-index-html-template " ,
155- " a path to a file which contains the index.html template you want to use. "
166+ " path-to-index-html" ,
167+ " a path to a directory which contains index.html. The entire directory will be served as static assets "
156168 )
169+ .orNone
157170 .validate(
158- " index. html must be a file, with a .html extension, and must contain <head> </head> and <body> </body> tags"
171+ " The path-to- index- html must be a directory. The directory must contain an index .html file. index.html must contain <head> </head> and <body> </body> tags"
159172 ) {
160- path =>
161- os.isFile(os.Path (path)) match
162- case false => false
163- case true =>
164- val f = os.Path (path)
165- f.ext match
166- case " html" =>
167- val content = os.read(f)
168- content.contains(" </head>" ) && content.contains(" </body>" ) && content.contains(" <head>" ) && content
169- .contains(
170- " <body>"
171- )
172- case _ => false
173- end match
173+ pathOpt =>
174+ pathOpt.forall {
175+ path =>
176+ os.isDir(os.Path (path)) match
177+ case false => false
178+ case true =>
179+ val f = os.Path (path) / " index.html"
180+ os.exists(f) match
181+ case false => false
182+ case true =>
183+ val content = os.read(f)
184+ content.contains(" </head>" ) && content.contains(" </body>" ) && content.contains(" <head>" ) && content
185+ .contains(
186+ " <body>"
187+ )
188+ end match
189+ }
174190
175191 }
176- .map {
177- path =>
178- os.read(os.Path (path))
179- }
180- .orNone
192+ .map(_.map(os.Path (_)))
181193
182194 val millModuleNameOpt : Opts [Option [String ]] = Opts
183195 .option[String ](
@@ -190,31 +202,22 @@ object LiveServer
190202 }
191203 .orNone
192204
193- override def main : Opts [IO [ExitCode ]] =
194- given R : Random [IO ] = Random .javaUtilConcurrentThreadLocalRandom[IO ]
195- def makeProxyRoutes (
196- client : Client [IO ],
197- pathPrefix : Option [String ],
198- proxyConfig : Resource [IO , Option [Equilibrium ]]
199- ): Resource [IO , HttpRoutes [IO ]] =
200- proxyConfig.flatMap {
201- case Some (pc) =>
202- {
203- logger.debug(" setup proxy server" ) >>
204- IO (HttpProxy .servers[IO ](pc, client, pathPrefix.getOrElse(??? )).head._2)
205- }.toResource
206-
207- case None =>
208- (
209- logger.debug(" no proxy set" ) >>
210- IO (HttpRoutes .empty[IO ])
211- ).toResource
212- }
205+ val indexOpts = (indexHtmlTemplateOpt, stylesDirOpt)
206+ .mapN(IndexHtmlConfig .apply)
207+ .validate(" You must provide either a styles directory or an index.html template directory, or neither" ) {
208+ c => c.indexHtmlatPath.isDefined || c.stylesOnly.isDefined || (c.indexHtmlatPath.isEmpty && c.stylesOnly.isEmpty)
209+ }
210+ .map(
211+ c =>
212+ c match
213+ case IndexHtmlConfig (None , None ) => None
214+ case _ => Some (c)
215+ )
213216
217+ override def main : Opts [IO [ExitCode ]] =
214218 (
215219 baseDirOpt,
216220 outDirOpt,
217- stylesDirOpt,
218221 portOpt,
219222 proxyPortTargetOpt,
220223 proxyPathMatchPrefixOpt,
@@ -223,12 +226,11 @@ object LiveServer
223226 openBrowserAtOpt,
224227 extraBuildArgsOpt,
225228 millModuleNameOpt,
226- indexHtmlTemplateOpt
229+ indexOpts
227230 ).mapN {
228231 (
229232 baseDir,
230233 outDir,
231- stylesDir,
232234 port,
233235 proxyTarget,
234236 pathPrefix,
@@ -237,7 +239,7 @@ object LiveServer
237239 openBrowserAt,
238240 extraBuildArgs,
239241 millModuleName,
240- externalIndexHtmlTemplate
242+ indexOpts
241243 ) =>
242244
243245 scribe
@@ -258,7 +260,7 @@ object LiveServer
258260 val server = for
259261 _ <- logger
260262 .debug(
261- s " baseDir: $baseDir \n outDir: $outDir \n stylesDir : $stylesDir \n port: $port \n proxyTarget: $proxyTarget \n pathPrefix: $pathPrefix \n extraBuildArgs: $extraBuildArgs"
263+ s " baseDir: $baseDir \n outDir: $outDir \n indexOpts : $indexOpts \n port: $port \n proxyTarget: $proxyTarget \n pathPrefix: $pathPrefix \n extraBuildArgs: $extraBuildArgs"
262264 )
263265 .toResource
264266
@@ -268,7 +270,7 @@ object LiveServer
268270 linkingTopic <- Topic [IO , Unit ].toResource
269271 client <- EmberClientBuilder .default[IO ].build
270272
271- proxyRoutes : HttpRoutes [IO ] <- makeProxyRoutes(client, pathPrefix, proxyConfig)
273+ proxyRoutes : HttpRoutes [IO ] <- makeProxyRoutes(client, pathPrefix, proxyConfig)(logger)
272274
273275 _ <- buildRunner(
274276 buildTool,
@@ -279,11 +281,7 @@ object LiveServer
279281 millModuleName
280282 )(logger)
281283
282- indexHtmlTemplate = externalIndexHtmlTemplate.getOrElse(vanillaTemplate(stylesDir.isDefined).render)
283-
284- app <- routes(outDir.toString(), refreshTopic, stylesDir, proxyRoutes, indexHtmlTemplate, fileToHashRef)(
285- logger
286- )
284+ app <- routes(outDir.toString(), refreshTopic, indexOpts, proxyRoutes, fileToHashRef)(logger)
287285
288286 _ <- seedMapOnStart(outDir, fileToHashMapRef)(logger)
289287 // _ <- stylesDir.fold(Resource.unit)(sd => seedMapOnStart(sd, mr))
0 commit comments