on master help sign in
Merge pull request #71 from obsidiansystems/overridable-static

Allow the static files directory to be overridden

commitd9a8c7bc29e62b47a5ce8194839111863427bc48 authorLuigy Leon <luichi.19@gmail.com> dateFri 19 Oct 2018, 10:24 (8 years ago) parent3a48e1dc67fe87
7 files changed+34−17
default.nix+7−7 lib/asset/manifest/src-bin/generate.hs+1 lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+9−1 lib/backend/src/Obelisk/Backend.hs+6−6 lib/command/src/Obelisk/Command/Run.hs+1−1 lib/run/obelisk-run.cabal+1 lib/run/src/Obelisk/Run.hs+9−2
default.nix+7−7
@@ -255,13 +255,12 @@ in rec {
255255 # An Obelisk project is a reflex-platform project with a predefined layout and role for each component
256256 project = base: projectDefinition:
257257 let configPath = base + "/config";
258− static = base + "/static";
259− processedStatic = processAssets { src = static; };
260258 projectOut = sys: (getReflexPlatform sys).project (args@{ nixpkgs, ... }:
261259 let mkProject = { android ? null #TODO: Better error when missing
262260 , ios ? null #TODO: Better error when missing
263261 , packages ? {}
264262 , overrides ? _: _: {}
263+ , staticFiles ? base + /static
265264 , tools ? _: []
266265 , shellToolOverrides ? _: _: {}
267266 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
@@ -270,7 +269,7 @@ in rec {
270269 backendName = "backend";
271270 commonName = "common";
272271 staticName = "obelisk-generated-static";
273− staticPath = base + "/static";
272+ processedStatic = processAssets { src = staticFiles; };
274273 # The packages whose names and roles are defined by this package
275274 predefinedPackages = lib.filterAttrs (_: x: x != null) {
276275 ${frontendName} = nullIfAbsent (base + "/frontend");
@@ -279,7 +278,7 @@ in rec {
279278 };
280279 combinedPackages = predefinedPackages // packages;
281280 projectOverrides = self: super: {
282− ${staticName} = haskellLib.dontHaddock (self.callCabal2nix "static" processedStatic.haskellManifest {});
281+ ${staticName} = haskellLib.dontHaddock (self.callCabal2nix staticName processedStatic.haskellManifest {});
283282 ${backendName} = haskellLib.addBuildDepend super.${backendName} self.obelisk-run;
284283 };
285284 totalOverrides = lib.composeExtensions projectOverrides overrides;
@@ -310,20 +309,21 @@ in rec {
310309 android = {
311310 ${if android == null then null else frontendName} = {
312311 executableName = "frontend";
313− ${if builtins.pathExists staticPath then "assets" else null} =
312+ ${if builtins.pathExists staticFiles then "assets" else null} =
314313 nixpkgs.obeliskExecutableConfig.platforms.android.inject injectableConfig processedStatic.symlinked;
315314 } // android;
316315 };
317316 ios = {
318317 ${if ios == null then null else frontendName} = {
319318 executableName = "frontend";
320− ${if builtins.pathExists staticPath then "staticSrc" else null} =
319+ ${if builtins.pathExists staticFiles then "staticSrc" else null} =
321320 nixpkgs.obeliskExecutableConfig.platforms.ios.inject injectableConfig processedStatic.symlinked;
322321 } // ios;
323322 };
323+ passthru = { inherit android ios packages overrides tools shellToolOverrides withHoogle staticFiles; };
324324 };
325325 in mkProject (projectDefinition args));
326− serverOn = sys: config: serverExe (projectOut sys).ghc.backend (projectOut system).ghcjs.frontend static config;
326+ serverOn = sys: config: serverExe (projectOut sys).ghc.backend (projectOut system).ghcjs.frontend (projectOut sys).passthru.staticFiles config;
327327 linuxExe = serverOn "x86_64-linux";
328328 in projectOut system // {
329329 linuxExeConfigurable = linuxExe;
lib/asset/manifest/src-bin/generate.hs+1
@@ -12,5 +12,6 @@ main = do
1212 writeStaticProject paths haskellTarget $ StaticConfig
1313 { _staticConfig_packageName = T.pack packageName
1414 , _staticConfig_moduleName = T.pack moduleName
15+ , _staticConfig_rootPath = root
1516 }
1617 copyAndSymlink paths root fileTarget
lib/asset/manifest/src/Obelisk/Asset/Promoted.hs+9−1
@@ -28,6 +28,7 @@ import qualified Data.Map as Map
2828data StaticConfig = StaticConfig
2929 { _staticConfig_packageName :: Text --TODO: Better type
3030 , _staticConfig_moduleName :: Text --TODO: Better type
31+ , _staticConfig_rootPath :: FilePath
3132 }
3233
3334writeStaticProject :: Map FilePath FilePath -> FilePath -> StaticConfig -> IO ()
@@ -40,7 +41,14 @@ writeStaticProject paths target cfg = do
4041 Just (name, parents) -> (name, target </> "src" </> T.unpack (T.intercalate "/" $ reverse parents))
4142 createDirectoryIfMissing True moduleDirPath
4243 modContents <- staticModuleFile modName paths
43− T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") modContents
44+ T.writeFile (moduleDirPath </> T.unpack modName' <.> "hs") $ appendRootPath (_staticConfig_rootPath cfg) modContents
45+
46+appendRootPath :: FilePath -> Text -> Text
47+appendRootPath fp txt = T.unlines
48+ [ txt
49+ , "staticRootPath :: FilePath"
50+ , "staticRootPath = \"" <> T.pack fp <> "\""
51+ ]
4452
4553staticCabalFile :: StaticConfig -> Text
4654staticCabalFile cfg = T.unlines
lib/backend/src/Obelisk/Backend.hs+6−6
@@ -55,8 +55,8 @@ data GhcjsApp route = GhcjsApp
5555
5656-- | Serve a frontend, which must be the same frontend that Obelisk has built and placed in the default location
5757--TODO: The frontend should be provided together with the asset paths so that this isn't so easily breakable; that will probably make this function obsolete
58−serveDefaultObeliskApp :: MonadSnap m => Frontend (R appRoute) -> R (ObeliskRoute appRoute) -> m ()
59−serveDefaultObeliskApp frontend = serveObeliskApp defaultStaticAssets frontendApp
58+serveDefaultObeliskApp :: MonadSnap m => ([Text] -> m ()) -> Frontend (R appRoute) -> R (ObeliskRoute appRoute) -> m ()
59+serveDefaultObeliskApp serveStaticAsset frontend = serveObeliskApp serveStaticAsset frontendApp
6060 where frontendApp = GhcjsApp
6161 { _ghcjsApp_compiled = defaultFrontendGhcjsAssets
6262 , _ghcjsApp_value = frontend
@@ -107,11 +107,11 @@ getRouteWith e = do
107107 pageName <- getPageName
108108 return $ tryDecode e pageName
109109
110−serveObeliskApp :: MonadSnap m => StaticAssets -> GhcjsApp (R appRoute) -> R (ObeliskRoute appRoute) -> m ()
111−serveObeliskApp staticAssets frontendApp = \case
110+serveObeliskApp :: MonadSnap m => ([Text] -> m ()) -> GhcjsApp (R appRoute) -> R (ObeliskRoute appRoute) -> m ()
111+serveObeliskApp serveStaticAsset frontendApp = \case
112112 ObeliskRoute_App appRouteComponent :=> Identity appRouteRest -> serveGhcjsApp frontendApp $ GhcjsAppRoute_App appRouteComponent :/ appRouteRest
113113 ObeliskRoute_Resource resComponent :=> Identity resRest -> case resComponent :=> Identity resRest of
114− ResourceRoute_Static :=> Identity pathSegments -> serveStaticAssets staticAssets pathSegments
114+ ResourceRoute_Static :=> Identity pathSegments -> serveStaticAsset pathSegments
115115 ResourceRoute_Ghcjs :=> Identity pathSegments -> serveGhcjsApp frontendApp $ GhcjsAppRoute_Resource :/ pathSegments
116116 ResourceRoute_JSaddleWarp :=> Identity _ -> do
117117 let msg = "Error: Obelisk.Backend received jsaddle request"
@@ -146,7 +146,7 @@ runBackend backend frontend = case checkEncoder $ _backend_routeEncoder backend
146146 getRouteWith validFullEncoder >>= \case
147147 Identity r -> case r of
148148 InL backendRoute :=> Identity a -> serveRoute $ backendRoute :/ a
149− InR obeliskRoute :=> Identity a -> serveDefaultObeliskApp frontend $ obeliskRoute :/ a
149+ InR obeliskRoute :=> Identity a -> serveDefaultObeliskApp (serveStaticAssets defaultStaticAssets) frontend $ obeliskRoute :/ a
150150
151151renderGhcjsFrontend :: route -> Frontend route -> IO ByteString
152152renderGhcjsFrontend route f = do
lib/command/src/Obelisk/Command/Run.hs+1−1
@@ -51,7 +51,7 @@ run = do
5151 pkgs <- getLocalPkgs
5252 withGhciScript pkgs $ \dotGhciPath -> do
5353 freePort <- getFreePort
54− runGhcid dotGhciPath $ Just $ unwords ["run", show freePort, "Backend.backend", "Frontend.frontend"]
54+ runGhcid dotGhciPath $ Just $ unwords ["run", show freePort, "(runServeAsset Obelisk.Generated.Static.staticRootPath)", "Backend.backend", "Frontend.frontend"]
5555
5656runRepl :: MonadObelisk m => m ()
5757runRepl = do
lib/run/obelisk-run.cabal+1
@@ -22,6 +22,7 @@ library
2222 , modern-uri
2323 , mtl
2424 , network
25+ , obelisk-asset-serve-snap
2526 , obelisk-backend
2627 , obelisk-executable-config
2728 , obelisk-executable-config-inject
lib/run/src/Obelisk/Run.hs+9−2
@@ -48,11 +48,13 @@ import Network.Wai.Handler.Warp
4848import Network.Wai.Handler.Warp.Internal (settingsHost, settingsPort)
4949import Network.WebSockets (ConnectionOptions)
5050import Network.WebSockets.Connection (defaultConnectionOptions)
51+import qualified Obelisk.Asset.Serve.Snap as Snap
5152import Obelisk.ExecutableConfig (get)
5253import Obelisk.ExecutableConfig.Inject (injectExecutableConfigs)
5354import Obelisk.Frontend
5455import Obelisk.Route.Frontend
5556import Reflex.Dom.Core
57+import Snap.Core (Snap)
5658import System.Environment
5759import System.IO
5860import System.Process
@@ -63,10 +65,11 @@ import Obelisk.Backend
6365
6466run
6567 :: Int -- ^ Port to run the backend
68+ -> ([Text] -> Snap ()) -- ^ Static asset handler
6669 -> Backend fullRoute frontendRoute -- ^ Backend
6770 -> Frontend (R frontendRoute) -- ^ Frontend
6871 -> IO ()
69−run port backend frontend = do
72+run port serveStaticAsset backend frontend = do
7073 prettifyOutput
7174 let handleBackendErr (e :: IOException) = hPutStrLn stderr $ "backend stopped; make a change to your code to reload - error " <> show e
7275 --TODO: Use Obelisk.Backend.runBackend; this will require separating the checking and running phases
@@ -79,10 +82,14 @@ run port backend frontend = do
7982 getRouteWith validFullEncoder >>= \case
8083 Identity r -> case r of
8184 InL backendRoute :=> Identity a -> serveRoute $ backendRoute :/ a
82− InR obeliskRoute :=> Identity a -> serveDefaultObeliskApp frontend $ obeliskRoute :/ a
85+ InR obeliskRoute :=> Identity a -> serveDefaultObeliskApp serveStaticAsset frontend $ obeliskRoute :/ a
8386 let conf = defRunConfig { _runConfig_redirectPort = port }
8487 runWidget conf frontend validFullEncoder `finally` killThread backendTid
8588
89+-- Convenience wrapper to handle path segments for 'Snap.serveAsset'
90+runServeAsset :: FilePath -> [Text] -> Snap ()
91+runServeAsset rootPath = Snap.serveAsset "" rootPath . T.unpack . T.intercalate "/"
92+
8693getConfigRoute :: IO (Maybe URI)
8794getConfigRoute = get "config/common/route" >>= \case
8895 Just r -> case URI.mkURI $ T.strip r of