Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing content flash #48

Merged
merged 6 commits into from
Jan 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG-ELM.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [1.1.2] - 2020-01-20

### Fixed
- "Missing content" message no longer flashes between pre-rendered HTML and the Elm app hydrating and taking over the page. See [#48](https://github.com/dillonkearns/elm-pages/pull/48).

## [1.1.1] - 2020-01-04

### Fixed
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG-NPM.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [1.1.8] - 2020-01-20

### Fixed
- "Missing content" message no longer flashes between pre-rendered HTML and the Elm app hydrating and taking over the page. See [#48](https://github.com/dillonkearns/elm-pages/pull/48).

## [1.1.7] - 2020-01-12

### Fixed
Expand Down
5 changes: 5 additions & 0 deletions generator/src/develop.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const webpack = require("webpack");
const middleware = require("webpack-dev-middleware");
const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const PrerenderSPAPlugin = require("prerender-spa-plugin");
const merge = require("webpack-merge");
Expand Down Expand Up @@ -159,6 +160,10 @@ function webpackOptions(
inject: "head",
template: path.resolve(__dirname, "template.html")
}),
new ScriptExtHtmlWebpackPlugin({
preload: /\.js$/,
defaultAttribute: 'defer'
}),
new FaviconsWebpackPlugin({
logo: path.resolve(process.cwd(), `./${manifestConfig.sourceIcon}`),
favicons: {
Expand Down
2 changes: 1 addition & 1 deletion generator/src/template.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="preload" href="content.json" as="fetch" crossorigin />
<link rel="preload" href="./content.json" as="fetch" crossorigin />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
Expand Down
20 changes: 19 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ module.exports = function pagesInit(
let prefetchedPages = [window.location.pathname];

document.addEventListener("DOMContentLoaded", function() {
httpGet(`${window.location.origin}${window.location.pathname}/content.json`, function (/** @type JSON */ contentJson) {

let app = mainElmModule.init({
flags: {
secrets: null
secrets: null,
isPrerendering: navigator.userAgent.indexOf("Headless") >= 0,
contentJson
}
});

Expand All @@ -33,6 +37,9 @@ module.exports = function pagesInit(

document.dispatchEvent(new Event("prerender-trigger"));
});

})

});

function setupLinkPrefetching() {
Expand Down Expand Up @@ -130,3 +137,14 @@ module.exports = function pagesInit(
document.getElementsByTagName("head")[0].appendChild(meta);
}
};

function httpGet(/** @type string */ theUrl, /** @type Function */ callback)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(JSON.parse(xmlHttp.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"node-sass": "^4.12.0",
"prerender-spa-plugin": "^3.4.0",
"sass-loader": "^8.0.0",
"script-ext-html-webpack-plugin": "^2.1.4",
"style-loader": "^1.0.0",
"webpack": "^4.41.5",
"webpack-dev-middleware": "^3.7.0",
Expand Down
74 changes: 42 additions & 32 deletions src/Pages/ContentCache.elm
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@ pagesWithErrors cache =
init :
Document metadata view
-> Content
-> Maybe { contentJson : ContentJson String, initialUrl : Url }
-> ContentCache metadata view
init document content =
parseMetadata document content
init document content maybeInitialPageContent =
parseMetadata maybeInitialPageContent document content
|> List.map
(\tuple ->
Tuple.mapSecond
Expand Down Expand Up @@ -149,39 +150,48 @@ createBuildError path decodeError =


parseMetadata :
Document metadata view
Maybe { contentJson : ContentJson String, initialUrl : Url }
-> Document metadata view
-> List ( List String, { extension : String, frontMatter : String, body : Maybe String } )
-> List ( List String, Result String (Entry metadata view) )
parseMetadata document content =
parseMetadata maybeInitialPageContent document content =
content
|> List.map
(Tuple.mapSecond
(\{ frontMatter, extension, body } ->
let
maybeDocumentEntry =
Document.get extension document
in
case maybeDocumentEntry of
Just documentEntry ->
frontMatter
|> documentEntry.frontmatterParser
|> Result.map
(\metadata ->
-- TODO do I need to handle this case?
-- case body of
-- Just presentBody ->
-- Parsed metadata
-- { body = parseContent extension presentBody document
-- , staticData = ""
-- }
--
-- Nothing ->
NeedContent extension metadata
)
(\( path, { frontMatter, extension, body } ) ->
let
maybeDocumentEntry =
Document.get extension document
in
case maybeDocumentEntry of
Just documentEntry ->
frontMatter
|> documentEntry.frontmatterParser
|> Result.map
(\metadata ->
let
renderer =
\value ->
parseContent extension value document
in
case maybeInitialPageContent of
Just { contentJson, initialUrl } ->
if initialUrl.path == ("/" ++ String.join "/" path) then
Parsed metadata
{ body = renderer contentJson.body
, staticData = contentJson.staticData
}

else
NeedContent extension metadata

Nothing ->
NeedContent extension metadata
)
|> Tuple.pair path

Nothing ->
Err ("Could not find extension '" ++ extension ++ "'")
)
Nothing ->
Err ("Could not find extension '" ++ extension ++ "'")
|> Tuple.pair path
)


Expand Down Expand Up @@ -327,8 +337,8 @@ lazyLoad document url cacheResult =
|> Task.map
(\downloadedContent ->
update cacheResult
(\thing ->
parseContent extension thing document
(\value ->
parseContent extension value document
)
url
downloadedContent
Expand Down
53 changes: 51 additions & 2 deletions src/Pages/Internal/Platform.elm
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ type alias Flags =
Decode.Value


type alias ContentJson =
{ body : String
, staticData : Dict String String
}


init :
pathKey
-> String
Expand Down Expand Up @@ -249,11 +255,33 @@ init :
init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel flags url key =
let
contentCache =
ContentCache.init document content
ContentCache.init document content (Maybe.map (\cj -> { contentJson = cj, initialUrl = url }) contentJson)

contentJson =
flags
|> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder)
|> Result.toMaybe

contentJsonDecoder : Decode.Decoder ContentJson
contentJsonDecoder =
Decode.map2 ContentJson
(Decode.field "body" Decode.string)
(Decode.field "staticData" (Decode.dict Decode.string))
in
case contentCache of
Ok okCache ->
let
phase =
case Decode.decodeValue (Decode.field "isPrerendering" Decode.bool) flags of
Ok True ->
Prerender

Ok False ->
Client

Err _ ->
Client

( userModel, userCmd ) =
initUserModel maybePagePath

Expand Down Expand Up @@ -284,6 +312,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
, url = url
, userModel = userModel
, contentCache = contentCache
, phase = phase
}
, cmd
)
Expand All @@ -297,6 +326,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla
, url = url
, userModel = userModel
, contentCache = contentCache
, phase = Client
}
, Cmd.batch
[ userCmd |> Cmd.map UserMsg
Expand Down Expand Up @@ -333,9 +363,15 @@ type alias ModelDetails userModel metadata view =
, url : Url.Url
, contentCache : ContentCache metadata view
, userModel : userModel
, phase : Phase
}


type Phase
= Prerender
| Client


update :
String
->
Expand Down Expand Up @@ -524,7 +560,20 @@ application config =
\msg outerModel ->
case outerModel of
Model model ->
update config.canonicalSiteUrl config.view config.pathKey config.onPageChange config.toJsPort config.document config.update msg model
let
userUpdate =
case model.phase of
Prerender ->
noOpUpdate

Client ->
config.update

noOpUpdate =
\userMsg userModel ->
( userModel, Cmd.none )
in
update config.canonicalSiteUrl config.view config.pathKey config.onPageChange config.toJsPort config.document userUpdate msg model
|> Tuple.mapFirst Model
|> Tuple.mapSecond (Cmd.map AppMsg)

Expand Down
2 changes: 1 addition & 1 deletion src/Pages/Internal/Platform/Cli.elm
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ cliApplication :
cliApplication cliMsgConstructor narrowMsg toModel fromModel config =
let
contentCache =
ContentCache.init config.document config.content
ContentCache.init config.document config.content Nothing

siteMetadata =
contentCache
Expand Down