diff --git a/examples/pokedex/adapter.js b/examples/pokedex/adapter.js index d5fd0a90..c23b1bd0 100644 --- a/examples/pokedex/adapter.js +++ b/examples/pokedex/adapter.js @@ -49,10 +49,10 @@ async function run({ .map((route) => { if (route.kind === "prerender-with-fallback") { return `${route.pathPattern} /.netlify/builders/render 200 -${route.pathPattern}/content.json /.netlify/builders/render 200`; +${route.pathPattern}/content.dat /.netlify/builders/render 200`; } else { return `${route.pathPattern} /.netlify/functions/server-render 200 -${route.pathPattern}/content.json /.netlify/functions/server-render 200`; +${route.pathPattern}/content.dat /.netlify/functions/server-render 200`; } }) .join("\n") + diff --git a/generator/src/Pages/Internal/Platform.elm b/generator/src/Pages/Internal/Platform.elm index dd3e1ebf..9599bb77 100644 --- a/generator/src/Pages/Internal/Platform.elm +++ b/generator/src/Pages/Internal/Platform.elm @@ -19,10 +19,8 @@ import Html.Attributes as Attr import Http import Json.Decode as Decode import Json.Encode -import PageServerResponse exposing (PageServerResponse) -import Pages.ContentCache as ContentCache exposing (ContentCache, ContentJson, contentJsonDecoder) +import Pages.ContentCache as ContentCache exposing (ContentCache, ContentJson) import Pages.Flags -import Pages.Internal.ApplicationType as ApplicationType import Pages.Internal.NotFoundReason exposing (NotFoundReason) import Pages.Internal.ResponseSketch as ResponseSketch exposing (ResponseSketch) import Pages.Internal.String as String @@ -133,170 +131,109 @@ init : -> ( Model userModel pageData sharedData, Cmd (Msg userMsg pageData sharedData) ) init config flags url key = let - contentCache : ContentCache - contentCache = - ContentCache.init - (Maybe.map - (\cj -> - let - currentPath : List String - currentPath = - flags - |> Decode.decodeValue - (Decode.at [ "contentJson", "path" ] - (Decode.string - |> Decode.map Path.fromString - |> Decode.map Path.toSegments - ) - ) - |> Result.mapError Decode.errorToString - |> Result.toMaybe - |> Maybe.withDefault [] - in - ( currentPath - , cj - ) - ) - contentJson - ) - - contentJson : Maybe ContentJson - contentJson = + pageDataResult : Result BuildError ( pageData, sharedData ) + pageDataResult = flags - |> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder) + |> Decode.decodeValue (Decode.field "pageDataBase64" Decode.string) |> Result.toMaybe + |> Maybe.andThen Base64.toBytes + |> Maybe.andThen + (\justBytes -> + case + Bytes.Decode.decode + -- TODO should this use byteDecodePageData, or should it be decoding ResponseSketch data? + config.decodeResponse + justBytes + of + Just (ResponseSketch.RenderPage pageData) -> + Nothing + + Just (ResponseSketch.HotUpdate pageData shared) -> + Just ( pageData, shared ) + + _ -> + Nothing + ) + |> Result.fromMaybe + (StaticHttpRequest.DecoderError "Bytes decode error" + |> StaticHttpRequest.toBuildError url.path + ) in - case contentJson |> Maybe.map .staticData of - Just justContentJson -> + case pageDataResult of + Ok ( pageData, sharedData ) -> let - pageDataResult : Result BuildError pageData - pageDataResult = + urls : { currentUrl : Url, basePath : List String } + urls = + { currentUrl = url + , basePath = config.basePath + } + + pagePath : Path + pagePath = + urlsToPagePath urls + + userFlags : Pages.Flags.Flags + userFlags = flags - |> Decode.decodeValue (Decode.field "pageDataBase64" Decode.string) - |> Result.toMaybe - |> Maybe.andThen Base64.toBytes - |> Maybe.andThen - (\justBytes -> - case - Bytes.Decode.decode - -- TODO should this use byteDecodePageData, or should it be decoding ResponseSketch data? - config.decodeResponse - justBytes - of - Just (ResponseSketch.RenderPage pageData) -> - Just pageData + |> Decode.decodeValue + (Decode.field "userFlags" Decode.value) + |> Result.withDefault Json.Encode.null + |> Pages.Flags.BrowserFlags - Just (ResponseSketch.HotUpdate pageData shared) -> - Just pageData - - _ -> - Nothing - ) - |> Result.fromMaybe - (StaticHttpRequest.DecoderError "Bytes decode error" - |> StaticHttpRequest.toBuildError url.path - ) - - sharedDataResult : Result BuildError sharedData - sharedDataResult = - StaticHttpRequest.resolve ApplicationType.Browser - config.sharedData - justContentJson - |> Result.mapError (StaticHttpRequest.toBuildError url.path) - in - case Result.map2 Tuple.pair sharedDataResult pageDataResult of - Ok ( sharedData, pageData ) -> - let - urls : { currentUrl : Url, basePath : List String } - urls = - { currentUrl = url - , basePath = config.basePath + ( userModel, userCmd ) = + Just + { path = + { path = pagePath + , query = url.query + , fragment = url.fragment } - - pagePath : Path - pagePath = - urlsToPagePath urls - - userFlags : Pages.Flags.Flags - userFlags = - flags - |> Decode.decodeValue - (Decode.field "userFlags" Decode.value) - |> Result.withDefault Json.Encode.null - |> Pages.Flags.BrowserFlags - - ( userModel, userCmd ) = + , metadata = config.urlToRoute url + , pageUrl = Just - { path = - { path = pagePath - , query = url.query - , fragment = url.fragment - } - , metadata = config.urlToRoute url - , pageUrl = - Just - { protocol = url.protocol - , host = url.host - , port_ = url.port_ - , path = pagePath - , query = url.query |> Maybe.map QueryParams.fromString - , fragment = url.fragment - } + { protocol = url.protocol + , host = url.host + , port_ = url.port_ + , path = pagePath + , query = url.query |> Maybe.map QueryParams.fromString + , fragment = url.fragment } - |> config.init userFlags sharedData pageData (Just key) + } + |> config.init userFlags sharedData pageData (Just key) - cmd : Cmd (Msg userMsg pageData sharedData) - cmd = - [ userCmd - |> Cmd.map UserMsg - |> Just - , contentCache - |> ContentCache.lazyLoad urls - |> Task.attempt UpdateCache - |> Just - ] - |> List.filterMap identity - |> Cmd.batch + cmd : Cmd (Msg userMsg pageData sharedData) + cmd = + [ userCmd + |> Cmd.map UserMsg + |> Just + ] + |> List.filterMap identity + |> Cmd.batch - initialModel : Model userModel pageData sharedData - initialModel = - { key = key - , url = url - , contentCache = contentCache - , pageData = - Ok - { pageData = pageData - , sharedData = sharedData - , userModel = userModel - } - , ariaNavigationAnnouncement = "" - , userFlags = flags - , notFound = Nothing + initialModel : Model userModel pageData sharedData + initialModel = + { key = key + , url = url + , pageData = + Ok + { pageData = pageData + , sharedData = sharedData + , userModel = userModel } - in - ( { initialModel - | ariaNavigationAnnouncement = mainView config initialModel |> .title - } - , cmd - ) + , ariaNavigationAnnouncement = "" + , userFlags = flags + , notFound = Nothing + } + in + ( { initialModel + | ariaNavigationAnnouncement = mainView config initialModel |> .title + } + , cmd + ) - Err error -> - ( { key = key - , url = url - , contentCache = contentCache - , pageData = BuildError.errorToString error |> Err - , ariaNavigationAnnouncement = "Error" - , userFlags = flags - , notFound = Nothing - } - , Cmd.none - ) - - Nothing -> + Err error -> ( { key = key , url = url - , contentCache = contentCache - , pageData = Err "TODO" + , pageData = BuildError.errorToString error |> Err , ariaNavigationAnnouncement = "Error" , userFlags = flags , notFound = Nothing @@ -310,11 +247,8 @@ type Msg userMsg pageData sharedData = LinkClicked Browser.UrlRequest | UrlChanged Url | UserMsg userMsg - | UpdateCache (Result Http.Error ( Url, ContentJson, ContentCache )) - | UpdateCacheAndUrl Url (Result Http.Error ( Url, ContentJson, ContentCache )) | UpdateCacheAndUrlNew Url (Result Http.Error (ResponseSketch pageData sharedData)) | PageScrollComplete - | HotReloadComplete ContentJson | HotReloadCompleteNew Bytes | ReloadCurrentPageData | NoOp @@ -324,7 +258,6 @@ type Msg userMsg pageData sharedData type alias Model userModel pageData sharedData = { key : Browser.Navigation.Key , url : Url - , contentCache : ContentCache , ariaNavigationAnnouncement : String , pageData : Result @@ -442,9 +375,11 @@ update config appMsg model = } in ( model - , model.contentCache - |> ContentCache.eagerLoad urls - |> Task.attempt (UpdateCacheAndUrl model.url) + , Cmd.none + -- @@@ TODO re-implement with Bytes decoding + --model.contentCache + -- |> ContentCache.eagerLoad urls + -- |> Task.attempt (UpdateCacheAndUrl model.url) ) UserMsg userMsg -> @@ -463,19 +398,6 @@ update config appMsg model = Err _ -> ( model, Cmd.none ) - UpdateCache cacheUpdateResult -> - case cacheUpdateResult of - -- TODO can there be race conditions here? Might need to set something in the model - -- to keep track of the last url change - Ok ( _, _, updatedCache ) -> - ( { model | contentCache = updatedCache } - , Cmd.none - ) - - Err _ -> - -- TODO handle error - ( model, Cmd.none ) - UpdateCacheAndUrlNew url cacheUpdateResult -> case Result.map2 Tuple.pair (cacheUpdateResult |> Result.mapError (\_ -> "Http error")) model.pageData of Ok ( newData, previousPageData ) -> @@ -533,100 +455,7 @@ update config appMsg model = Err error -> {- - When there is an error loading the content.json, we are either - 1) in the dev server, and should show the relevant DataSource error for the page - we're navigating to. This could be done more cleanly, but it's simplest to just - do a fresh page load and use the code path for presenting an error for a fresh page. - 2) In a production app. That means we had a successful build, so there were no DataSource failures, - so the app must be stale (unless it's in some unexpected state from a bug). In the future, - it probably makes sense to include some sort of hash of the app version we are fetching, match - it with the current version that's running, and perform this logic when we see there is a mismatch. - But for now, if there is any error we do a full page load (not a single-page navigation), which - gives us a fresh version of the app to make sure things are in sync. - - -} - ( model - , url - |> Url.toString - |> Browser.Navigation.load - ) - - UpdateCacheAndUrl url cacheUpdateResult -> - case - Result.map2 Tuple.pair (cacheUpdateResult |> Result.mapError (\_ -> "Http error")) model.pageData - of - -- TODO can there be race conditions here? Might need to set something in the model - -- to keep track of the last url change - Ok ( ( _, contentJson, updatedCache ), pageData ) -> - let - updatedPageData : Result String { userModel : userModel, sharedData : sharedData, pageData : pageData } - updatedPageData = - updatedPageStaticData - |> Result.map - (\pageStaticData -> - { userModel = userModel - , sharedData = pageData.sharedData - , pageData = pageStaticData - } - ) - - updatedPageStaticData : Result String pageData - updatedPageStaticData = - StaticHttpRequest.resolve ApplicationType.Browser - (config.data (config.urlToRoute url)) - contentJson.staticData - |> Result.mapError - (\error -> - error - |> StaticHttpRequest.toBuildError "" - |> BuildError.errorToString - ) - |> Result.andThen - (\pageResponse -> - case pageResponse of - PageServerResponse.RenderPage _ renderPagePageData -> - Ok renderPagePageData - - PageServerResponse.ServerResponse _ -> - Err "Unexpected ServerResponse - was expecting RenderPage response." - ) - - ( userModel, userCmd ) = - config.update - pageData.sharedData - (updatedPageStaticData |> Result.withDefault pageData.pageData) - (Just model.key) - (config.onPageChange - { protocol = model.url.protocol - , host = model.url.host - , port_ = model.url.port_ - , path = url |> urlPathToPath config - , query = url.query - , fragment = url.fragment - , metadata = config.urlToRoute url - } - ) - pageData.userModel - - updatedModel : Model userModel pageData sharedData - updatedModel = - { model - | url = url - , contentCache = updatedCache - } - in - ( { updatedModel - | ariaNavigationAnnouncement = mainView config updatedModel |> .title - } - , Cmd.batch - [ userCmd |> Cmd.map UserMsg - , Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0) - ] - ) - - Err _ -> - {- - When there is an error loading the content.json, we are either + When there is an error loading the content.dat, we are either 1) in the dev server, and should show the relevant DataSource error for the page we're navigating to. This could be done more cleanly, but it's simplest to just do a fresh page load and use the code path for presenting an error for a fresh page. @@ -697,175 +526,6 @@ update config appMsg model = ) |> Result.withDefault ( model, Cmd.none ) - HotReloadComplete contentJson -> - let - urls : { currentUrl : Url, basePath : List String } - urls = - { currentUrl = model.url - , basePath = config.basePath - } - - pageDataResult : Result BuildError (PageServerResponse pageData) - pageDataResult = - StaticHttpRequest.resolve ApplicationType.Browser - (config.data (config.urlToRoute model.url)) - contentJson.staticData - |> Result.mapError (StaticHttpRequest.toBuildError model.url.path) - - sharedDataResult : Result BuildError sharedData - sharedDataResult = - StaticHttpRequest.resolve ApplicationType.Browser - config.sharedData - contentJson.staticData - |> Result.mapError (StaticHttpRequest.toBuildError model.url.path) - in - case Result.map2 Tuple.pair sharedDataResult pageDataResult of - Ok ( sharedData, pageData_ ) -> - case pageData_ of - PageServerResponse.RenderPage _ pageData -> - let - was404 : Bool - was404 = - ContentCache.is404 model.contentCache urls - - from404ToNon404 : Bool - from404ToNon404 = - not contentJson.is404 - && was404 - - updateResult : Maybe ( userModel, Cmd userMsg ) - updateResult = - if from404ToNon404 then - case model.pageData of - Ok pageData2_ -> - config.update - sharedData - pageData - (Just model.key) - (config.onPageChange - { protocol = model.url.protocol - , host = model.url.host - , port_ = model.url.port_ - , path = model.url |> urlPathToPath config - , query = model.url.query - , fragment = model.url.fragment - , metadata = config.urlToRoute model.url - } - ) - pageData2_.userModel - |> Just - - Err _ -> - Nothing - - else - Nothing - in - case updateResult of - Just ( userModel, userCmd ) -> - ( { model - | contentCache = - ContentCache.init - (Just - ( urls.currentUrl - |> config.urlToRoute - |> config.routeToPath - , contentJson - ) - ) - , pageData = - Ok - { pageData = pageData - , sharedData = sharedData - , userModel = userModel - } - } - , Cmd.batch - [ userCmd |> Cmd.map UserMsg - ] - ) - - Nothing -> - let - pagePath : Path - pagePath = - urlsToPagePath urls - - userFlags : Pages.Flags.Flags - userFlags = - model.userFlags - |> Decode.decodeValue - (Decode.field "userFlags" Decode.value) - |> Result.withDefault Json.Encode.null - |> Pages.Flags.BrowserFlags - - ( userModel, userCmd ) = - Just - { path = - { path = pagePath - , query = model.url.query - , fragment = model.url.fragment - } - , metadata = config.urlToRoute model.url - , pageUrl = - Just - { protocol = model.url.protocol - , host = model.url.host - , port_ = model.url.port_ - , path = pagePath - , query = model.url.query |> Maybe.map QueryParams.fromString - , fragment = model.url.fragment - } - } - |> config.init userFlags sharedData pageData (Just model.key) - in - ( { model - | contentCache = - ContentCache.init - (Just - ( urls.currentUrl - |> config.urlToRoute - |> config.routeToPath - , contentJson - ) - ) - , pageData = - model.pageData - |> Result.map - (\previousPageData -> - { pageData = pageData - , sharedData = sharedData - , userModel = previousPageData.userModel - } - ) - |> Result.withDefault - { pageData = pageData - , sharedData = sharedData - , userModel = userModel - } - |> Ok - } - , userCmd |> Cmd.map UserMsg - ) - - PageServerResponse.ServerResponse _ -> - ( model, Cmd.none ) - - Err _ -> - ( { model - | contentCache = - ContentCache.init - (Just - ( urls.currentUrl - |> config.urlToRoute - |> config.routeToPath - , contentJson - ) - ) - } - , Cmd.none - ) - NoOp -> ( model, Cmd.none ) diff --git a/generator/src/Pages/Internal/Platform/Cli.elm b/generator/src/Pages/Internal/Platform/Cli.elm index 6e632b4e..d1b89262 100644 --- a/generator/src/Pages/Internal/Platform/Cli.elm +++ b/generator/src/Pages/Internal/Platform/Cli.elm @@ -806,7 +806,10 @@ nextStepToEffect site contentCache config model ( updatedStaticResponsesModel, n byteEncodedPageData : Bytes byteEncodedPageData = - if model.isDevServer then + if True then + -- TODO want to encode both shared and page data in dev server and HTML-embedded data + -- but not for writing out the content.dat files - would be good to optimize this redundant data out + --if model.isDevServer then ResponseSketch.HotUpdate pageData sharedData__ |> config.encodeResponse @@ -821,11 +824,7 @@ nextStepToEffect site contentCache config model ( updatedStaticResponsesModel, n case includeHtml of RenderRequest.OnlyJson -> { route = payload.path |> Path.toRelative - , contentJson = - --toJsPayload.pages - -- |> Dict.get (Path.toRelative page) - -- |> Maybe.withDefault Dict.empty - Dict.empty + , contentJson = Dict.empty , html = "This page was not rendered because it is a JSON-only request." , errors = [] , head = [] @@ -840,11 +839,7 @@ nextStepToEffect site contentCache config model ( updatedStaticResponsesModel, n RenderRequest.HtmlAndJson -> { route = payload.path |> Path.toRelative - , contentJson = - --toJsPayload.pages - -- |> Dict.get (Path.toRelative page) - -- |> Maybe.withDefault Dict.empty - Dict.empty + , contentJson = Dict.empty , html = viewValue.body |> HtmlPrinter.htmlToString , errors = [] , head = config.view currentPage Nothing sharedData__ pageData |> .head @@ -1072,7 +1067,10 @@ sendSinglePageProgress site contentJson config model = Ok pageServerResponse -> case pageServerResponse of PageServerResponse.RenderPage _ pageData -> - if model.isDevServer then + -- TODO want to encode both shared and page data in dev server and HTML-embedded data + -- but not for writing out the content.dat files - would be good to optimize this redundant data out + --if model.isDevServer then + if True then sharedDataResult |> Result.map (ResponseSketch.HotUpdate pageData) |> Result.withDefault (ResponseSketch.RenderPage pageData) @@ -1094,7 +1092,7 @@ sendSinglePageProgress site contentJson config model = Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0) in { route = page |> Path.toRelative - , contentJson = contentJson + , contentJson = Dict.empty , html = rendered.view , errors = [] , head = rendered.head ++ site.head siteData @@ -1147,20 +1145,7 @@ render404Page config model path notFoundReason = |> Bytes.Encode.encode in { route = Path.toAbsolute path - , contentJson = - Dict.fromList - [ ( "notFoundReason" - , Json.Encode.encode 0 - (Codec.encoder Pages.Internal.NotFoundReason.codec - { path = path - , reason = notFoundReason - } - ) - ) - , ( "path", Path.toAbsolute path ) - ] - - -- TODO include the needed info for content.json? + , contentJson = Dict.empty , html = HtmlPrinter.htmlToString notFoundDocument.body , errors = [] , head = [] diff --git a/generator/src/elm-pages-js-minified.js b/generator/src/elm-pages-js-minified.js index c636a6fe..37902a5f 100644 --- a/generator/src/elm-pages-js-minified.js +++ b/generator/src/elm-pages-js-minified.js @@ -16,9 +16,7 @@ function loadContentAndInitializeApp() { isPrerendering: false, isDevServer: false, isElmDebugMode: false, - contentJson: JSON.parse( - document.getElementById("__ELM_PAGES_DATA__").innerHTML - ), + contentJson: {}, pageDataBase64: document.getElementById("__ELM_PAGES_BYTES_DATA__").innerHTML, userFlags: userInit.flags(), }, diff --git a/generator/src/pre-render-html.js b/generator/src/pre-render-html.js index 4a0a0ad1..4dd21a59 100644 --- a/generator/src/pre-render-html.js +++ b/generator/src/pre-render-html.js @@ -57,13 +57,6 @@ ${elmPagesJsMinified} ${seoData.headTags} - diff --git a/generator/src/render.js b/generator/src/render.js index a3744a42..4e7a18b2 100755 --- a/generator/src/render.js +++ b/generator/src/render.js @@ -85,7 +85,6 @@ function runElmApp( let killApp; return new Promise((resolve, reject) => { const isBytes = pagePath.match(/content\.dat\/?$/); - const isJson = pagePath.match(/content\.json\/?$/); const route = pagePath .replace(/content\.json\/?$/, "") .replace(/content\.dat\/?$/, ""); @@ -100,7 +99,7 @@ function runElmApp( request: { payload: modifiedRequest, kind: "single-page", - jsonOnly: !!isJson, + jsonOnly: !!isBytes, }, }, }); @@ -154,18 +153,6 @@ function runElmApp( headers: args.headers, contentDatPayload, }); - } else if (isJson) { - resolve({ - kind: "json", - is404: args.is404, - contentJson: JSON.stringify({ - staticData: args.contentJson, - is404: args.is404, - }), - statusCode: args.statusCode, - headers: args.headers, - contentDatPayload, - }); } else { resolve( outputString(basePath, fromElm, isDevServer, contentDatPayload) diff --git a/generator/static-code/elm-pages.js b/generator/static-code/elm-pages.js index 9e7924fb..f62b94d4 100644 --- a/generator/static-code/elm-pages.js +++ b/generator/static-code/elm-pages.js @@ -17,9 +17,7 @@ function loadContentAndInitializeApp() { isPrerendering: false, isDevServer: false, isElmDebugMode: false, - contentJson: JSON.parse( - document.getElementById("__ELM_PAGES_DATA__").innerHTML - ), + contentJson: {}, pageDataBase64: document.getElementById("__ELM_PAGES_BYTES_DATA__") .innerHTML, userFlags: userInit.flags(),