Send content.dat updates for hot reloading.

This commit is contained in:
Dillon Kearns 2022-02-03 11:51:17 -08:00
parent 82dea47a89
commit 159c075324
7 changed files with 68 additions and 35 deletions

View File

@ -12,6 +12,7 @@ import Browser
import Browser.Dom as Dom import Browser.Dom as Dom
import Browser.Navigation import Browser.Navigation
import BuildError exposing (BuildError) import BuildError exposing (BuildError)
import Bytes exposing (Bytes)
import Bytes.Decode import Bytes.Decode
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attr import Html.Attributes as Attr
@ -297,6 +298,7 @@ type Msg userMsg pageData
| UpdateCacheAndUrlNew Url (Result Http.Error pageData) | UpdateCacheAndUrlNew Url (Result Http.Error pageData)
| PageScrollComplete | PageScrollComplete
| HotReloadComplete ContentJson | HotReloadComplete ContentJson
| HotReloadCompleteNew Bytes
| ReloadCurrentPageData | ReloadCurrentPageData
| NoOp | NoOp
@ -617,6 +619,41 @@ update config appMsg model =
PageScrollComplete -> PageScrollComplete ->
( model, Cmd.none ) ( model, Cmd.none )
HotReloadCompleteNew pageDataBytes ->
let
route : route
route =
model.url
|> config.urlToRoute
newThing : Maybe pageData
newThing =
pageDataBytes
|> Bytes.Decode.decode
(config.byteDecodePageData route)
in
model.pageData
|> Result.map
(\pageData ->
let
updatedPageData : Result String { userModel : userModel, sharedData : sharedData, pageData : pageData }
updatedPageData =
Ok
{ userModel = pageData.userModel
, sharedData = pageData.sharedData
, pageData =
newThing
|> Maybe.withDefault pageData.pageData
}
in
( { model
| pageData = updatedPageData
}
, Cmd.none
)
)
|> Result.withDefault ( model, Cmd.none )
HotReloadComplete contentJson -> HotReloadComplete contentJson ->
let let
urls : { currentUrl : Url, basePath : List String } urls : { currentUrl : Url, basePath : List String }
@ -817,29 +854,17 @@ application config =
|> Sub.map UserMsg |> Sub.map UserMsg
, config.fromJsPort , config.fromJsPort
|> Sub.map |> Sub.map
(\decodeValue -> (\_ ->
case decodeValue |> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder) of
Ok contentJson ->
HotReloadComplete contentJson
Err _ ->
-- TODO should be no message here -- TODO should be no message here
ReloadCurrentPageData ReloadCurrentPageData
) )
, config.hotReloadData
|> Sub.map HotReloadCompleteNew
] ]
Err _ -> Err _ ->
config.fromJsPort config.hotReloadData
|> Sub.map |> Sub.map HotReloadCompleteNew
(\decodeValue ->
case decodeValue |> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder) of
Ok contentJson ->
HotReloadComplete contentJson
Err _ ->
-- TODO should be no message here
ReloadCurrentPageData
)
, onUrlChange = UrlChanged , onUrlChange = UrlChanged
, onUrlRequest = LinkClicked , onUrlRequest = LinkClicked
} }

View File

@ -196,7 +196,7 @@ async function start(options) {
elmMakeRunning = false; elmMakeRunning = false;
}); });
clients.forEach((client) => { clients.forEach((client) => {
client.response.write(`data: content.json\n\n`); client.response.write(`data: content.dat\n\n`);
}); });
} }
} else { } else {
@ -218,7 +218,7 @@ async function start(options) {
// } // }
// }); // });
clients.forEach((client) => { clients.forEach((client) => {
client.response.write(`data: content.json\n\n`); client.response.write(`data: content.dat\n\n`);
}); });
} }
}); });
@ -324,7 +324,7 @@ async function start(options) {
const reviewOutput = await runElmReview(); const reviewOutput = await runElmReview();
console.log(restoreColorSafe(reviewOutput)); console.log(restoreColorSafe(reviewOutput));
if (req.url.includes("content.json")) { if (req.url.includes("content.dat")) {
res.writeHead(500, { "Content-Type": "application/json" }); res.writeHead(500, { "Content-Type": "application/json" });
if (emptyReviewError(reviewOutput)) { if (emptyReviewError(reviewOutput)) {
res.end(error); res.end(error);
@ -337,7 +337,7 @@ async function start(options) {
} }
} else { } else {
console.log(restoreColorSafe(error)); console.log(restoreColorSafe(error));
if (req.url.includes("content.json")) { if (req.url.includes("content.dat")) {
res.writeHead(500, { "Content-Type": "application/json" }); res.writeHead(500, { "Content-Type": "application/json" });
res.end(error); res.end(error);
} else { } else {
@ -423,7 +423,7 @@ async function start(options) {
function (error) { function (error) {
console.log(restoreColorSafe(error)); console.log(restoreColorSafe(error));
if (req.url.includes("content.json")) { if (req.url.includes("content.dat")) {
res.writeHead(500, { "Content-Type": "application/json" }); res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify(error)); res.end(JSON.stringify(error));
} else { } else {

View File

@ -67,9 +67,9 @@ const appPromise = new Promise(function (resolve, reject) {
userInit.load(appPromise); userInit.load(appPromise);
if (typeof connect === "function") { if (typeof connect === "function") {
connect(function (newContentJson) { connect(function (bytesData) {
appPromise.then((app) => { appPromise.then((app) => {
app.ports.fromJsPort.send({ contentJson: newContentJson }); app.ports.hotReloadData.send(bytesData);
}); });
}); });
} }

View File

@ -470,9 +470,13 @@ main =
, sendPageData = sendPageData , sendPageData = sendPageData
, byteEncodePageData = byteEncodePageData , byteEncodePageData = byteEncodePageData
, byteDecodePageData = byteDecodePageData , byteDecodePageData = byteDecodePageData
, hotReloadData = hotReloadData identity
} }
port hotReloadData : (Bytes -> msg) -> Sub msg
byteEncodePageData : PageData -> Bytes.Encode.Encoder byteEncodePageData : PageData -> Bytes.Encode.Encoder
byteEncodePageData pageData = byteEncodePageData pageData =
case pageData of case pageData of

View File

@ -70,9 +70,9 @@ const appPromise = new Promise(function (resolve, reject) {
userInit.load(appPromise); userInit.load(appPromise);
if (typeof connect === "function") { if (typeof connect === "function") {
connect(function (newContentJson) { connect(function (bytesData) {
appPromise.then((app) => { appPromise.then((app) => {
app.ports.fromJsPort.send({ contentJson: newContentJson }); app.ports.hotReloadData.send(bytesData);
}); });
}); });
} }

View File

@ -9,7 +9,7 @@ function connect(sendContentJsonPort, initialErrorPage) {
eventSource = new EventSource("/stream"); eventSource = new EventSource("/stream");
window.reloadOnOk = initialErrorPage; window.reloadOnOk = initialErrorPage;
if (initialErrorPage) { if (initialErrorPage) {
handleEvent(sendContentJsonPort, { data: "content.json" }); handleEvent(sendContentJsonPort, { data: "content.dat" });
} }
eventSource.onmessage = async function (evt) { eventSource.onmessage = async function (evt) {
handleEvent(sendContentJsonPort, evt); handleEvent(sendContentJsonPort, evt);
@ -17,7 +17,7 @@ function connect(sendContentJsonPort, initialErrorPage) {
} }
async function handleEvent(sendContentJsonPort, evt) { async function handleEvent(sendContentJsonPort, evt) {
if (evt.data === "content.json") { if (evt.data === "content.dat") {
showCompiling(""); showCompiling("");
const elmJsRequest = elmJsFetch(); const elmJsRequest = elmJsFetch();
const fetchContentJson = fetchContentJsonForCurrentPage(); const fetchContentJson = fetchContentJsonForCurrentPage();
@ -32,7 +32,7 @@ async function handleEvent(sendContentJsonPort, evt) {
thenApplyHmr(elmJsResponse); thenApplyHmr(elmJsResponse);
} catch (errorJson) { } catch (errorJson) {
if (typeof errorJson === "string") { if (typeof errorJson === "string") {
errorJson = JSON.parse(errorJson) errorJson = JSON.parse(errorJson);
} }
if (errorJson.type) { if (errorJson.type) {
showError(errorJson); showError(errorJson);
@ -84,7 +84,7 @@ async function updateContentJsonWith(
} catch (errorJson) { } catch (errorJson) {
if (errorJson.type) { if (errorJson.type) {
showError(errorJson); showError(errorJson);
} else if (typeof errorJson === 'string') { } else if (typeof errorJson === "string") {
showError(JSON.parse(errorJson)); showError(JSON.parse(errorJson));
} else { } else {
showError(errorJson); showError(errorJson);
@ -98,10 +98,12 @@ function fetchContentJsonForCurrentPage() {
let currentPath = window.location.pathname.replace(/(\w)$/, "$1/"); let currentPath = window.location.pathname.replace(/(\w)$/, "$1/");
const contentJsonForPage = await fetch( const contentJsonForPage = await fetch(
`${window.location.origin}${currentPath}content.json` `${window.location.origin}${currentPath}content.dat`
); );
if (contentJsonForPage.ok || contentJsonForPage.status === 404) { if (contentJsonForPage.ok || contentJsonForPage.status === 404) {
resolve(await contentJsonForPage.json()); resolve(
new DataView(await (await contentJsonForPage.blob()).arrayBuffer())
);
} else { } else {
try { try {
reject(await contentJsonForPage.json()); reject(await contentJsonForPage.json());

View File

@ -2,6 +2,7 @@ module Pages.ProgramConfig exposing (ProgramConfig)
import ApiRoute import ApiRoute
import Browser.Navigation import Browser.Navigation
import Bytes exposing (Bytes)
import Bytes.Decode import Bytes.Decode
import Bytes.Encode import Bytes.Encode
import DataSource import DataSource
@ -61,6 +62,7 @@ type alias ProgramConfig userMsg userModel route siteData pageData sharedData =
, site : Maybe (SiteConfig siteData) , site : Maybe (SiteConfig siteData)
, toJsPort : Json.Encode.Value -> Cmd Never , toJsPort : Json.Encode.Value -> Cmd Never
, fromJsPort : Sub Decode.Value , fromJsPort : Sub Decode.Value
, hotReloadData : Sub Bytes
, onPageChange : , onPageChange :
{ protocol : Url.Protocol { protocol : Url.Protocol
, host : String , host : String