From 815dec7d987831620f76de1813350e689917860a Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 13:54:55 -0800 Subject: [PATCH 01/54] Add hardcoded file generation in prep for programmatic file generation from elm. --- generator/src/add-files-plugin.js | 17 +++++++++++++++- generator/src/develop.js | 14 ++++++++------ generator/src/elm-pages.js | 7 +++++-- src/Pages/Internal/Platform/Cli.elm | 30 +++++++++++++++++++++++++++-- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/generator/src/add-files-plugin.js b/generator/src/add-files-plugin.js index 66ed8fcf..6594a310 100644 --- a/generator/src/add-files-plugin.js +++ b/generator/src/add-files-plugin.js @@ -20,8 +20,11 @@ function unpackFile(filePath) { } module.exports = class AddFilesPlugin { - constructor(data) { + constructor(data, filesToGenerate) { this.pagesWithRequests = data; + this.filesToGenerate = filesToGenerate; + console.log('this.filesToGenerate', this.filesToGenerate); + } apply(compiler) { compiler.hooks.emit.tap("AddFilesPlugin", compilation => { @@ -52,6 +55,18 @@ module.exports = class AddFilesPlugin { size: () => rawContents.length }; }); + + this.filesToGenerate.forEach(file => { + // Couldn't find this documented in the webpack docs, + // but I found the example code for it here: + // https://github.com/jantimon/html-webpack-plugin/blob/35a154186501fba3ecddb819b6f632556d37a58f/index.js#L470-L478 + compilation.assets[file.path] = { + source: () => file.content, + size: () => file.content.length + }; + }); + + }); } }; diff --git a/generator/src/develop.js b/generator/src/develop.js index 4382e1d2..0b1fd143 100644 --- a/generator/src/develop.js +++ b/generator/src/develop.js @@ -15,11 +15,12 @@ const ClosurePlugin = require("closure-webpack-plugin"); const readline = require("readline"); module.exports = { start, run }; -function start({ routes, debug, customPort, manifestConfig, routesWithRequests }) { +function start({ routes, debug, customPort, manifestConfig, routesWithRequests, filesToGenerate }) { const config = webpackOptions(false, routes, { debug, manifestConfig, - routesWithRequests + routesWithRequests, + filesToGenerate }); const compiler = webpack(config); @@ -65,12 +66,13 @@ function start({ routes, debug, customPort, manifestConfig, routesWithRequests } // app.use(express.static(__dirname + "/path-to-static-folder")); } -function run({ routes, manifestConfig, routesWithRequests }, callback) { +function run({ routes, manifestConfig, routesWithRequests, filesToGenerate }, callback) { webpack( webpackOptions(true, routes, { debug: false, manifestConfig, - routesWithRequests + routesWithRequests, + filesToGenerate }) ).run((err, stats) => { if (err) { @@ -118,12 +120,12 @@ function printProgress(progress, message) { function webpackOptions( production, routes, - { debug, manifestConfig, routesWithRequests } + { debug, manifestConfig, routesWithRequests, filesToGenerate } ) { const common = { mode: production ? "production" : "development", plugins: [ - new AddFilesPlugin(routesWithRequests), + new AddFilesPlugin(routesWithRequests, filesToGenerate), new CopyPlugin([ { from: "static/**/*", diff --git a/generator/src/elm-pages.js b/generator/src/elm-pages.js index 09a2c687..3904c83d 100755 --- a/generator/src/elm-pages.js +++ b/generator/src/elm-pages.js @@ -86,6 +86,7 @@ function run() { markdownContent, content, function(payload) { + console.log('@@@@@@@@@ filesToGenerate', payload.filesToGenerate); if (contents.watch) { startWatchIfNeeded(); if (!devServerRunning) { @@ -94,7 +95,8 @@ function run() { routes, debug: contents.debug, manifestConfig: payload.manifest, - routesWithRequests: payload.pages + routesWithRequests: payload.pages, + filesToGenerate: payload.filesToGenerate }); } } else { @@ -106,7 +108,8 @@ function run() { { routes, manifestConfig: payload.manifest, - routesWithRequests: payload.pages + routesWithRequests: payload.pages, + filesToGenerate: payload.filesToGenerate }, () => {} ); diff --git a/src/Pages/Internal/Platform/Cli.elm b/src/Pages/Internal/Platform/Cli.elm index ba87f3fb..d3681cd3 100644 --- a/src/Pages/Internal/Platform/Cli.elm +++ b/src/Pages/Internal/Platform/Cli.elm @@ -44,6 +44,13 @@ type ToJsPayload pathKey type alias ToJsSuccessPayload pathKey = { pages : Dict String (Dict String String) , manifest : Manifest.Config pathKey + , filesToGenerate : List FileToGenerate + } + + +type alias FileToGenerate = + { path : List String + , content : String } @@ -55,8 +62,8 @@ toJsCodec = Errors errorList -> errors errorList - Success { pages, manifest } -> - success (ToJsSuccessPayload pages manifest) + Success { pages, manifest, filesToGenerate } -> + success (ToJsSuccessPayload pages manifest filesToGenerate) ) |> Codec.variant1 "Errors" Errors Codec.string |> Codec.variant1 "Success" @@ -90,6 +97,21 @@ successCodec = |> Codec.field "manifest" .manifest (Codec.build Manifest.toJson (Decode.succeed stubManifest)) + |> Codec.field "filesToGenerate" + .filesToGenerate + (Codec.build + (\list -> + list + |> Json.Encode.list + (\item -> + Json.Encode.object + [ ( "path", item.path |> String.join "/" |> Json.Encode.string ) + , ( "content", item.content |> Json.Encode.string ) + ] + ) + ) + (Decode.succeed []) + ) |> Codec.buildObject @@ -745,6 +767,10 @@ sendStaticResponsesIfDone mode secrets allRawResponses errors staticResponses ma (ToJsSuccessPayload (encodeStaticResponses mode staticResponses) manifest + [ { path = [ "hello.txt" ] + , content = "Hello generated files!" + } + ] ) else From a0b735379aa9381162c2b1dab46ff564263e2023 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 16:36:50 -0800 Subject: [PATCH 02/54] Add generateFiles hook. --- examples/docs/elm.json | 1 + examples/docs/src/Feed.elm | 84 ++++++++++++++++ examples/docs/src/Main.elm | 17 ++++ examples/docs/src/RssFeed.elm | 131 ++++++++++++++++++++++++ src/Pages/Internal/Platform.elm | 20 ++++ src/Pages/Internal/Platform/Cli.elm | 150 +++++++++++++++------------- src/Pages/Platform.elm | 11 ++ 7 files changed, 345 insertions(+), 69 deletions(-) create mode 100644 examples/docs/src/Feed.elm create mode 100644 examples/docs/src/RssFeed.elm diff --git a/examples/docs/elm.json b/examples/docs/elm.json index 6422dfd0..b7a5c1cd 100644 --- a/examples/docs/elm.json +++ b/examples/docs/elm.json @@ -9,6 +9,7 @@ "dependencies": { "direct": { "avh4/elm-color": "1.0.0", + "billstclair/elm-xml-eeue56": "1.0.1", "dillonkearns/elm-markdown": "1.1.3", "dillonkearns/elm-oembed": "1.0.0", "elm/browser": "1.0.2", diff --git a/examples/docs/src/Feed.elm b/examples/docs/src/Feed.elm new file mode 100644 index 00000000..760ae961 --- /dev/null +++ b/examples/docs/src/Feed.elm @@ -0,0 +1,84 @@ +module Feed exposing (fileToGenerate) + +import Dict +import Metadata exposing (Metadata(..)) +import Pages +import Pages.PagePath as PagePath exposing (PagePath) +import RssFeed +import Time +import Xml +import Xml.Encode exposing (..) + + +fileToGenerate : + String + -> + List + { path : PagePath Pages.PathKey + , frontmatter : Metadata + } + -> + { path : List String + , content : String + } +fileToGenerate siteTagline siteMetadata = + { path = [ "feed.xml" ] + , content = + generate siteTagline siteMetadata |> Xml.Encode.encode 0 + } + + +generate : + String + -> + List + { path : PagePath Pages.PathKey + , frontmatter : Metadata + } + -> Xml.Value +generate siteTagline siteMetadata = + RssFeed.generate + { title = "elm-pages Blog" + , description = siteTagline + , url = "https://elm-pages.com/blog" + , lastBuildTime = Pages.builtAt + , generator = + Just + { name = "elm-pages" + , uri = Just "https://elm-pages.com" + , version = Nothing + } + , items = siteMetadata |> List.filterMap metadataToRssItem + } + + +metadataToRssItem : + { path : PagePath Pages.PathKey + , frontmatter : Metadata + } + -> Maybe RssFeed.Item +metadataToRssItem page = + case page.frontmatter of + Article article -> + Just + { title = article.title + , description = article.description + , url = PagePath.toString page.path + , guid = PagePath.toString page.path + , categories = [] + , author = article.author.name + , pubDate = article.published + , content = Nothing + } + + Page pageMetadata -> + Nothing + + Doc docMetadata -> + Nothing + + Author author -> + Nothing + + BlogIndex -> + Nothing diff --git a/examples/docs/src/Main.elm b/examples/docs/src/Main.elm index 1848b352..f99c31e6 100644 --- a/examples/docs/src/Main.elm +++ b/examples/docs/src/Main.elm @@ -10,6 +10,7 @@ import Element.Background import Element.Border import Element.Font as Font import Element.Region +import Feed import Head import Head.Seo as Seo import Html exposing (Html) @@ -62,11 +63,27 @@ main = , documents = [ markdownDocument ] , manifest = manifest , canonicalSiteUrl = canonicalSiteUrl + , generateFiles = generateFiles , onPageChange = OnPageChange , internals = Pages.internals } +generateFiles : + List + { path : PagePath Pages.PathKey + , frontmatter : Metadata + } + -> + List + { path : List String + , content : String + } +generateFiles siteMetadata = + [ Feed.fileToGenerate siteTagline siteMetadata + ] + + markdownDocument : ( String, Pages.Document.DocumentHandler Metadata ( MarkdownRenderer.TableOfContents, List (Element Msg) ) ) markdownDocument = Pages.Document.parser diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm new file mode 100644 index 00000000..293f44af --- /dev/null +++ b/examples/docs/src/RssFeed.elm @@ -0,0 +1,131 @@ +module RssFeed exposing (Item, generate) + +import Date exposing (Date) +import Dict +import Time +import Xml +import Xml.Encode exposing (..) + + +type alias Item = + { title : String + , description : String + , url : String + , guid : String + , categories : List String + , author : String + , pubDate : Date + , content : Maybe String + + {- + lat optional number The latitude coordinate of the item. + long optional number The longitude coordinate of the item. + custom_elements optional array Put additional elements in the item (node-xml syntax) + enclosure optional object An enclosure object + -} + } + + +generate : + { title : String + , description : String + , url : String + , lastBuildTime : Time.Posix + , generator : + Maybe + { name : String + , uri : Maybe String + , version : Maybe String + } + , items : List Item + } + -> Xml.Value +generate feed = + let + lastBuildTimeString = + -- TODO + --feed.lastBuildTime + "" + in + object + [ ( "rss" + , Dict.fromList + [ ( "xmlns", string "http://www.w3.org/2005/Atom" ) + , ( "xmlns:dc", string "http://purl.org/dc/elements/1.1/" ) + , ( "xmlns:content", string "http://purl.org/rss/1.0/modules/content/" ) + , ( "xmlns:atom", string "http://www.w3.org/2005/Atom" ) + , ( "version", string "2.0" ) + ] + , object + [ ( "channel" + , Dict.empty + , list + ([ keyValue "title" feed.title + , keyValue "description" feed.description + , keyValue "link" feed.url + , keyValue "lastBuildDate" lastBuildTimeString + ] + ++ List.map itemXml feed.items + ++ ([ feed.generator |> Maybe.map generatorXml + ] + |> List.filterMap identity + ) + ) + ) + ] + ) + ] + + +itemXml : Item -> Xml.Value +itemXml item = + object + [ ( "item" + , Dict.empty + , list + ([ keyValue "title" item.title + , keyValue "description" item.description + , keyValue "link" item.url + , keyValue "guid" item.guid + , keyValue "pubDate" (formatDate item.pubDate) + ] + ++ ([ item.content |> Maybe.map (\content -> keyValue "content" content) + ] + |> List.filterMap identity + ) + ) + ) + ] + + +formatDate : Date -> String +formatDate date = + Date.toIsoString date + + + +--Date.format "EE, dd MM yyyy" date + + +generatorXml : + { name : String + , uri : Maybe String + , version : Maybe String + } + -> Xml.Value +generatorXml generator = + Xml.Encode.object + [ ( "generator" + , [ generator.uri |> Maybe.map (\uri -> ( "uri", string uri )) + , generator.version |> Maybe.map (\version -> ( "version", string version )) + ] + |> List.filterMap identity + |> Dict.fromList + , Xml.Encode.string generator.name + ) + ] + + +keyValue : String -> String -> Xml.Value +keyValue key value = + object [ ( key, Dict.empty, string value ) ] diff --git a/src/Pages/Internal/Platform.elm b/src/Pages/Internal/Platform.elm index 5876f213..e8237152 100644 --- a/src/Pages/Internal/Platform.elm +++ b/src/Pages/Internal/Platform.elm @@ -497,6 +497,16 @@ application : , content : Content , toJsPort : Json.Encode.Value -> Cmd Never , manifest : Manifest.Config pathKey + , generateFiles : + List + { path : PagePath pathKey + , frontmatter : metadata + } + -> + List + { path : List String + , content : String + } , canonicalSiteUrl : String , pathKey : pathKey , onPageChange : PagePath pathKey -> userMsg @@ -564,6 +574,16 @@ cliApplication : , content : Content , toJsPort : Json.Encode.Value -> Cmd Never , manifest : Manifest.Config pathKey + , generateFiles : + List + { path : PagePath pathKey + , frontmatter : metadata + } + -> + List + { path : List String + , content : String + } , canonicalSiteUrl : String , pathKey : pathKey , onPageChange : PagePath pathKey -> userMsg diff --git a/src/Pages/Internal/Platform/Cli.elm b/src/Pages/Internal/Platform/Cli.elm index d3681cd3..b317c42b 100644 --- a/src/Pages/Internal/Platform/Cli.elm +++ b/src/Pages/Internal/Platform/Cli.elm @@ -150,34 +150,47 @@ type Msg = GotStaticHttpResponse { request : { masked : RequestDetails, unmasked : RequestDetails }, response : Result Http.Error String } +type alias Config pathKey userMsg userModel metadata view = + { init : Maybe (PagePath pathKey) -> ( userModel, Cmd userMsg ) + , update : userMsg -> userModel -> ( userModel, Cmd userMsg ) + , subscriptions : userModel -> Sub userMsg + , view : + List ( PagePath pathKey, metadata ) + -> + { path : PagePath pathKey + , frontmatter : metadata + } + -> + StaticHttp.Request + { view : userModel -> view -> { title : String, body : Html userMsg } + , head : List (Head.Tag pathKey) + } + , document : Pages.Document.Document metadata view + , content : Content + , toJsPort : Json.Encode.Value -> Cmd Never + , manifest : Manifest.Config pathKey + , generateFiles : + List + { path : PagePath pathKey + , frontmatter : metadata + } + -> + List + { path : List String + , content : String + } + , canonicalSiteUrl : String + , pathKey : pathKey + , onPageChange : PagePath pathKey -> userMsg + } + + cliApplication : (Msg -> msg) -> (msg -> Maybe Msg) -> (Model -> model) -> (model -> Maybe Model) - -> - { init : Maybe (PagePath pathKey) -> ( userModel, Cmd userMsg ) - , update : userMsg -> userModel -> ( userModel, Cmd userMsg ) - , subscriptions : userModel -> Sub userMsg - , view : - List ( PagePath pathKey, metadata ) - -> - { path : PagePath pathKey - , frontmatter : metadata - } - -> - StaticHttp.Request - { view : userModel -> view -> { title : String, body : Html userMsg } - , head : List (Head.Tag pathKey) - } - , document : Pages.Document.Document metadata view - , content : Content - , toJsPort : Json.Encode.Value -> Cmd Never - , manifest : Manifest.Config pathKey - , canonicalSiteUrl : String - , pathKey : pathKey - , onPageChange : PagePath pathKey -> userMsg - } + -> Config pathKey userMsg userModel metadata view -> Platform.Program Flags model msg cliApplication cliMsgConstructor narrowMsg toModel fromModel config = let @@ -199,7 +212,7 @@ cliApplication cliMsgConstructor narrowMsg toModel fromModel config = \msg model -> case ( narrowMsg msg, fromModel model ) of ( Just cliMsg, Just cliModel ) -> - update config cliMsg cliModel + update siteMetadata config cliMsg cliModel |> Tuple.mapSecond (perform cliMsgConstructor config.toJsPort) |> Tuple.mapFirst toModel @@ -270,21 +283,7 @@ init : (Model -> model) -> ContentCache.ContentCache metadata view -> Result (List BuildError) (List ( PagePath pathKey, metadata )) - -> - { config - | view : - List ( PagePath pathKey, metadata ) - -> - { path : PagePath pathKey - , frontmatter : metadata - } - -> - StaticHttp.Request - { view : userModel -> view -> { title : String, body : Html userMsg } - , head : List (Head.Tag pathKey) - } - , manifest : Manifest.Config pathKey - } + -> Config pathKey userMsg userModel metadata view -> Decode.Value -> ( model, Effect pathKey ) init toModel contentCache siteMetadata config flags = @@ -320,7 +319,7 @@ init toModel contentCache siteMetadata config flags = staticResponsesInit [] ( updatedRawResponses, effect ) = - sendStaticResponsesIfDone mode secrets Dict.empty [] staticResponses config.manifest + sendStaticResponsesIfDone config siteMetadata mode secrets Dict.empty [] staticResponses in ( Model staticResponses secrets [] updatedRawResponses mode |> toModel , effect @@ -346,6 +345,8 @@ init toModel contentCache siteMetadata config flags = staticResponsesInit [] in updateAndSendPortIfDone + config + siteMetadata (Model staticResponses secrets @@ -354,10 +355,11 @@ init toModel contentCache siteMetadata config flags = mode ) toModel - config.manifest Err metadataParserErrors -> updateAndSendPortIfDone + config + siteMetadata (Model Dict.empty secrets (metadataParserErrors |> List.map Tuple.second) @@ -365,10 +367,11 @@ init toModel contentCache siteMetadata config flags = mode ) toModel - config.manifest Err error -> updateAndSendPortIfDone + config + siteMetadata (Model Dict.empty SecretsDict.masked [ { title = "Internal Error" @@ -379,20 +382,25 @@ init toModel contentCache siteMetadata config flags = Dev ) toModel - config.manifest -updateAndSendPortIfDone : Model -> (Model -> model) -> Manifest.Config pathKey -> ( model, Effect pathKey ) -updateAndSendPortIfDone model toModel manifest = +updateAndSendPortIfDone : + Config pathKey userMsg userModel metadata view + -> Result (List BuildError) (List ( PagePath pathKey, metadata )) + -> Model + -> (Model -> model) + -> ( model, Effect pathKey ) +updateAndSendPortIfDone config siteMetadata model toModel = let ( updatedAllRawResponses, effect ) = sendStaticResponsesIfDone + config + siteMetadata model.mode model.secrets model.allRawResponses model.errors model.staticResponses - manifest in ( { model | allRawResponses = updatedAllRawResponses } |> toModel , effect @@ -404,24 +412,12 @@ type alias PageErrors = update : - { config - | view : - List ( PagePath pathKey, metadata ) - -> - { path : PagePath pathKey - , frontmatter : metadata - } - -> - StaticHttp.Request - { view : userModel -> view -> { title : String, body : Html userMsg } - , head : List (Head.Tag pathKey) - } - , manifest : Manifest.Config pathKey - } + Result (List BuildError) (List ( PagePath pathKey, metadata )) + -> Config pathKey userMsg userModel metadata view -> Msg -> Model -> ( Model, Effect pathKey ) -update config msg model = +update siteMetadata config msg model = case msg of GotStaticHttpResponse { request, response } -> let @@ -478,7 +474,7 @@ update config msg model = } ( updatedAllRawResponses, effect ) = - sendStaticResponsesIfDone updatedModel.mode updatedModel.secrets updatedModel.allRawResponses updatedModel.errors updatedModel.staticResponses config.manifest + sendStaticResponsesIfDone config siteMetadata updatedModel.mode updatedModel.secrets updatedModel.allRawResponses updatedModel.errors updatedModel.staticResponses in ( { updatedModel | allRawResponses = updatedAllRawResponses } , effect @@ -607,8 +603,16 @@ isJust maybeValue = False -sendStaticResponsesIfDone : Mode -> SecretsDict -> Dict String (Maybe String) -> List BuildError -> StaticResponses -> Manifest.Config pathKey -> ( Dict String (Maybe String), Effect pathKey ) -sendStaticResponsesIfDone mode secrets allRawResponses errors staticResponses manifest = +sendStaticResponsesIfDone : + Config pathKey userMsg userModel metadata view + -> Result (List BuildError) (List ( PagePath pathKey, metadata )) + -> Mode + -> SecretsDict + -> Dict String (Maybe String) + -> List BuildError + -> StaticResponses + -> ( Dict String (Maybe String), Effect pathKey ) +sendStaticResponsesIfDone config siteMetadata mode secrets allRawResponses errors staticResponses = let pendingRequests = staticResponses @@ -759,6 +763,17 @@ sendStaticResponsesIfDone mode secrets allRawResponses errors staticResponses ma let updatedAllRawResponses = Dict.empty + + generatedFiles = + siteMetadata + |> Result.withDefault [] + |> List.map + (\( pagePath, metadata ) -> + { path = pagePath + , frontmatter = metadata + } + ) + |> config.generateFiles in ( updatedAllRawResponses , SendJsData @@ -766,11 +781,8 @@ sendStaticResponsesIfDone mode secrets allRawResponses errors staticResponses ma Success (ToJsSuccessPayload (encodeStaticResponses mode staticResponses) - manifest - [ { path = [ "hello.txt" ] - , content = "Hello generated files!" - } - ] + config.manifest + generatedFiles ) else diff --git a/src/Pages/Platform.elm b/src/Pages/Platform.elm index da0862ca..1b8afda8 100644 --- a/src/Pages/Platform.elm +++ b/src/Pages/Platform.elm @@ -77,6 +77,16 @@ application : } , documents : List ( String, Document.DocumentHandler metadata view ) , manifest : Pages.Manifest.Config pathKey + , generateFiles : + List + { path : PagePath pathKey + , frontmatter : metadata + } + -> + List + { path : List String + , content : String + } , onPageChange : PagePath pathKey -> userMsg , canonicalSiteUrl : String , internals : Pages.Internal.Internal pathKey @@ -97,6 +107,7 @@ application config = , subscriptions = config.subscriptions , document = Document.fromList config.documents , content = config.internals.content + , generateFiles = config.generateFiles , toJsPort = config.internals.toJsPort , manifest = config.manifest , canonicalSiteUrl = config.canonicalSiteUrl From ad32b3f2194ff5b85c1bd07894513d000d2f590a Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 16:56:00 -0800 Subject: [PATCH 03/54] Update rss formatting. --- examples/docs/src/RssFeed.elm | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index 293f44af..ebf74796 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -50,8 +50,7 @@ generate feed = object [ ( "rss" , Dict.fromList - [ ( "xmlns", string "http://www.w3.org/2005/Atom" ) - , ( "xmlns:dc", string "http://purl.org/dc/elements/1.1/" ) + [ ( "xmlns:dc", string "http://purl.org/dc/elements/1.1/" ) , ( "xmlns:content", string "http://purl.org/rss/1.0/modules/content/" ) , ( "xmlns:atom", string "http://www.w3.org/2005/Atom" ) , ( "version", string "2.0" ) @@ -100,11 +99,8 @@ itemXml item = formatDate : Date -> String formatDate date = - Date.toIsoString date - - - ---Date.format "EE, dd MM yyyy" date + Date.format "EEE, dd MMM yyyy" date + ++ " UTC" generatorXml : From 7c1cfb80e565cd6a30cc0b48f9c09944cc0832d3 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 20:25:25 -0800 Subject: [PATCH 04/54] Change date format to include date. --- examples/docs/src/RssFeed.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index ebf74796..9e28602b 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -100,7 +100,7 @@ itemXml item = formatDate : Date -> String formatDate date = Date.format "EEE, dd MMM yyyy" date - ++ " UTC" + ++ " 00:00:00 UTC" generatorXml : From 1729723b7d454ec2f7906aadec568c5e7383fcbc Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 21:01:40 -0800 Subject: [PATCH 05/54] Add last built to RSS. --- examples/docs/elm.json | 6 +++++- examples/docs/src/RssFeed.elm | 9 ++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/docs/elm.json b/examples/docs/elm.json index b7a5c1cd..b887e497 100644 --- a/examples/docs/elm.json +++ b/examples/docs/elm.json @@ -12,6 +12,7 @@ "billstclair/elm-xml-eeue56": "1.0.1", "dillonkearns/elm-markdown": "1.1.3", "dillonkearns/elm-oembed": "1.0.0", + "dmy/elm-imf-date-time": "1.0.1", "elm/browser": "1.0.2", "elm/core": "1.0.2", "elm/html": "1.0.0", @@ -43,7 +44,10 @@ "elm/regex": "1.0.0", "elm/virtual-dom": "1.0.2", "fredcy/elm-parseint": "2.0.1", - "mgold/elm-nonempty-list": "4.0.2" + "justinmimbs/time-extra": "1.1.0", + "lazamar/dict-parser": "1.0.2", + "mgold/elm-nonempty-list": "4.0.2", + "ryannhg/date-format": "2.3.0" } }, "test-dependencies": { diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index 9e28602b..39846aee 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -2,6 +2,7 @@ module RssFeed exposing (Item, generate) import Date exposing (Date) import Dict +import Imf.DateTime import Time import Xml import Xml.Encode exposing (..) @@ -41,12 +42,6 @@ generate : } -> Xml.Value generate feed = - let - lastBuildTimeString = - -- TODO - --feed.lastBuildTime - "" - in object [ ( "rss" , Dict.fromList @@ -62,7 +57,7 @@ generate feed = ([ keyValue "title" feed.title , keyValue "description" feed.description , keyValue "link" feed.url - , keyValue "lastBuildDate" lastBuildTimeString + , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime ] ++ List.map itemXml feed.items ++ ([ feed.generator |> Maybe.map generatorXml From 611e8f84a0e41af7806fe4e167e6034b9092bf98 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 5 Jan 2020 21:08:20 -0800 Subject: [PATCH 06/54] Fix link formatting for RSS. --- examples/docs/src/Feed.elm | 16 ++++++++++------ examples/docs/src/Main.elm | 2 +- examples/docs/src/RssFeed.elm | 12 ++++++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/examples/docs/src/Feed.elm b/examples/docs/src/Feed.elm index 760ae961..e1b4bcf7 100644 --- a/examples/docs/src/Feed.elm +++ b/examples/docs/src/Feed.elm @@ -11,7 +11,9 @@ import Xml.Encode exposing (..) fileToGenerate : - String + { siteTagline : String + , siteUrl : String + } -> List { path : PagePath Pages.PathKey @@ -21,22 +23,24 @@ fileToGenerate : { path : List String , content : String } -fileToGenerate siteTagline siteMetadata = +fileToGenerate config siteMetadata = { path = [ "feed.xml" ] , content = - generate siteTagline siteMetadata |> Xml.Encode.encode 0 + generate config siteMetadata |> Xml.Encode.encode 0 } generate : - String + { siteTagline : String + , siteUrl : String + } -> List { path : PagePath Pages.PathKey , frontmatter : Metadata } -> Xml.Value -generate siteTagline siteMetadata = +generate { siteTagline, siteUrl } siteMetadata = RssFeed.generate { title = "elm-pages Blog" , description = siteTagline @@ -49,6 +53,7 @@ generate siteTagline siteMetadata = , version = Nothing } , items = siteMetadata |> List.filterMap metadataToRssItem + , siteUrl = siteUrl } @@ -64,7 +69,6 @@ metadataToRssItem page = { title = article.title , description = article.description , url = PagePath.toString page.path - , guid = PagePath.toString page.path , categories = [] , author = article.author.name , pubDate = article.published diff --git a/examples/docs/src/Main.elm b/examples/docs/src/Main.elm index f99c31e6..354feb2c 100644 --- a/examples/docs/src/Main.elm +++ b/examples/docs/src/Main.elm @@ -80,7 +80,7 @@ generateFiles : , content : String } generateFiles siteMetadata = - [ Feed.fileToGenerate siteTagline siteMetadata + [ Feed.fileToGenerate { siteTagline = siteTagline, siteUrl = canonicalSiteUrl } siteMetadata ] diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index 39846aee..55b7c874 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -12,7 +12,6 @@ type alias Item = { title : String , description : String , url : String - , guid : String , categories : List String , author : String , pubDate : Date @@ -39,6 +38,7 @@ generate : , version : Maybe String } , items : List Item + , siteUrl : String } -> Xml.Value generate feed = @@ -59,7 +59,7 @@ generate feed = , keyValue "link" feed.url , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime ] - ++ List.map itemXml feed.items + ++ List.map (itemXml feed.siteUrl) feed.items ++ ([ feed.generator |> Maybe.map generatorXml ] |> List.filterMap identity @@ -71,16 +71,16 @@ generate feed = ] -itemXml : Item -> Xml.Value -itemXml item = +itemXml : String -> Item -> Xml.Value +itemXml siteUrl item = object [ ( "item" , Dict.empty , list ([ keyValue "title" item.title , keyValue "description" item.description - , keyValue "link" item.url - , keyValue "guid" item.guid + , keyValue "link" (siteUrl ++ item.url) + , keyValue "guid" (siteUrl ++ item.url) , keyValue "pubDate" (formatDate item.pubDate) ] ++ ([ item.content |> Maybe.map (\content -> keyValue "content" content) From 97218f871485120e1d7e15cb3e3b2744f2326bd8 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 08:52:15 -0800 Subject: [PATCH 07/54] Update rss format. --- examples/docs/src/Feed.elm | 7 +------ examples/docs/src/RssFeed.elm | 29 +++++------------------------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/examples/docs/src/Feed.elm b/examples/docs/src/Feed.elm index e1b4bcf7..3daa469f 100644 --- a/examples/docs/src/Feed.elm +++ b/examples/docs/src/Feed.elm @@ -46,12 +46,7 @@ generate { siteTagline, siteUrl } siteMetadata = , description = siteTagline , url = "https://elm-pages.com/blog" , lastBuildTime = Pages.builtAt - , generator = - Just - { name = "elm-pages" - , uri = Just "https://elm-pages.com" - , version = Nothing - } + , generator = Just "elm-pages" , items = siteMetadata |> List.filterMap metadataToRssItem , siteUrl = siteUrl } diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index 55b7c874..ba60f06a 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -31,12 +31,7 @@ generate : , description : String , url : String , lastBuildTime : Time.Posix - , generator : - Maybe - { name : String - , uri : Maybe String - , version : Maybe String - } + , generator : Maybe String , items : List Item , siteUrl : String } @@ -60,7 +55,7 @@ generate feed = , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime ] ++ List.map (itemXml feed.siteUrl) feed.items - ++ ([ feed.generator |> Maybe.map generatorXml + ++ ([ feed.generator |> Maybe.map (keyValue "generator") ] |> List.filterMap identity ) @@ -95,26 +90,12 @@ itemXml siteUrl item = formatDate : Date -> String formatDate date = Date.format "EEE, dd MMM yyyy" date - ++ " 00:00:00 UTC" + ++ " 00:00:00 GMT" -generatorXml : - { name : String - , uri : Maybe String - , version : Maybe String - } - -> Xml.Value +generatorXml : String -> Xml.Value generatorXml generator = - Xml.Encode.object - [ ( "generator" - , [ generator.uri |> Maybe.map (\uri -> ( "uri", string uri )) - , generator.version |> Maybe.map (\version -> ( "version", string version )) - ] - |> List.filterMap identity - |> Dict.fromList - , Xml.Encode.string generator.name - ) - ] + Xml.Encode.object [ ( "generator", Dict.empty, Xml.Encode.string generator ) ] keyValue : String -> String -> Xml.Value From 178ef8fdd90da3fc2fed54e0f1a85e9d16dc4074 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 09:08:45 -0800 Subject: [PATCH 08/54] Change rss feed ordering. --- examples/docs/src/RssFeed.elm | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index ba60f06a..8f7db062 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -48,18 +48,16 @@ generate feed = , object [ ( "channel" , Dict.empty - , list - ([ keyValue "title" feed.title - , keyValue "description" feed.description - , keyValue "link" feed.url - , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime - ] - ++ List.map (itemXml feed.siteUrl) feed.items - ++ ([ feed.generator |> Maybe.map (keyValue "generator") - ] - |> List.filterMap identity - ) - ) + , [ [ keyValue "title" feed.title + , keyValue "description" feed.description + , keyValue "link" feed.url + , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime + ] + , [ feed.generator |> Maybe.map (keyValue "generator") ] |> List.filterMap identity + , List.map (itemXml feed.siteUrl) feed.items + ] + |> List.concat + |> list ) ] ) From 92a67363c239002e6c0537738cb80652f719547b Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 09:16:29 -0800 Subject: [PATCH 09/54] Add doc comment. --- examples/docs/src/RssFeed.elm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/docs/src/RssFeed.elm b/examples/docs/src/RssFeed.elm index 8f7db062..d46ad3af 100644 --- a/examples/docs/src/RssFeed.elm +++ b/examples/docs/src/RssFeed.elm @@ -1,5 +1,9 @@ module RssFeed exposing (Item, generate) +{-| Build a feed following the RSS 2.0 format . + +-} + import Date exposing (Date) import Dict import Imf.DateTime @@ -51,6 +55,8 @@ generate feed = , [ [ keyValue "title" feed.title , keyValue "description" feed.description , keyValue "link" feed.url + + -- , keyValue "lastBuildDate" <| Imf.DateTime.fromPosix Time.utc feed.lastBuildTime ] , [ feed.generator |> Maybe.map (keyValue "generator") ] |> List.filterMap identity From a0a99dab5280e759d408bf4f0e5f831efd090f16 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 09:50:51 -0800 Subject: [PATCH 10/54] Ignore files with extensions for service worker shell fallback. --- generator/src/service-worker-template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/src/service-worker-template.js b/generator/src/service-worker-template.js index 5404bcea..70e8971d 100644 --- a/generator/src/service-worker-template.js +++ b/generator/src/service-worker-template.js @@ -4,7 +4,7 @@ workbox.precaching.precacheAndRoute(self.__precacheManifest); workbox.routing.registerNavigationRoute( workbox.precaching.getCacheKeyForURL("/index.html"), { - blacklist: [/admin/] + blacklist: [/admin/, /\./] } ); workbox.routing.registerRoute( From 4f6f1430856f4675dd9f4571c2e73b73ed71b2ce Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 10:29:37 -0800 Subject: [PATCH 11/54] Add rssLink function to Head module. --- src/Head.elm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Head.elm b/src/Head.elm index c29e86e1..bbc8cab7 100644 --- a/src/Head.elm +++ b/src/Head.elm @@ -3,6 +3,7 @@ module Head exposing , AttributeValue , currentPageFullUrl, fullImageUrl, fullPageUrl, raw , toJson, canonicalLink + , rssLink ) {-| This module contains low-level functions for building up @@ -106,6 +107,26 @@ canonicalLink maybePath = ] +{-| Add a link to the site's RSS feed. + +```html + +``` + +Example: + + rssLink "/feed.xml" + +-} +rssLink : PagePath pathKey -> Tag pathKey +rssLink url = + node "link" + [ ( "rel", raw "alternate" ) + , ( "type", raw "application/rss+xml" ) + , ( "href", fullPageUrl url ) + ] + + {-| Example: Head.metaProperty "fb:app_id" ( Head.raw "123456789" ) From 40e89ccec238c3c1d3672c427ae471ede8e0f8d0 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 6 Jan 2020 10:29:46 -0800 Subject: [PATCH 12/54] Reformat. --- src/Head.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Head.elm b/src/Head.elm index bbc8cab7..d67533f4 100644 --- a/src/Head.elm +++ b/src/Head.elm @@ -129,7 +129,7 @@ rssLink url = {-| Example: - Head.metaProperty "fb:app_id" ( Head.raw "123456789" ) + Head.metaProperty "fb:app_id" (Head.raw "123456789") Results in `` From 5785d45f663eddb006e1aefb807897e154cab3a4 Mon Sep 17 00:00:00 2001 From: Luke Westby Date: Tue, 7 Jan 2020 09:33:40 -0800 Subject: [PATCH 13/54] escape frontmatter content so it handles newlines --- generator/src/generate-raw-content.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/src/generate-raw-content.js b/generator/src/generate-raw-content.js index d7e6c716..8aa84438 100644 --- a/generator/src/generate-raw-content.js +++ b/generator/src/generate-raw-content.js @@ -19,8 +19,8 @@ function toEntry(entry, includeBody) { return ` ( [${fullPath.join(", ")}] - , { frontMatter = """${entry.metadata} -""" , body = ${body(entry, includeBody)} + , { frontMatter = ${JSON.stringify(entry.metadata)} + , body = ${body(entry, includeBody)} , extension = "${extension}" } ) `; From 43f494a84d9ede808428a504f43604d55a6a4b2a Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Tue, 7 Jan 2020 17:10:00 -0800 Subject: [PATCH 14/54] Add sitemap. --- examples/docs/src/Main.elm | 2 ++ examples/docs/src/MySitemap.elm | 37 ++++++++++++++++++++++++++++ examples/docs/src/Sitemap.elm | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 examples/docs/src/MySitemap.elm create mode 100644 examples/docs/src/Sitemap.elm diff --git a/examples/docs/src/Main.elm b/examples/docs/src/Main.elm index 354feb2c..13eafc8d 100644 --- a/examples/docs/src/Main.elm +++ b/examples/docs/src/Main.elm @@ -20,6 +20,7 @@ import Json.Decode as Decode exposing (Decoder) import Json.Decode.Exploration as D import MarkdownRenderer import Metadata exposing (Metadata) +import MySitemap import Pages exposing (images, pages) import Pages.Directory as Directory exposing (Directory) import Pages.Document @@ -81,6 +82,7 @@ generateFiles : } generateFiles siteMetadata = [ Feed.fileToGenerate { siteTagline = siteTagline, siteUrl = canonicalSiteUrl } siteMetadata + , MySitemap.build { siteUrl = canonicalSiteUrl } siteMetadata ] diff --git a/examples/docs/src/MySitemap.elm b/examples/docs/src/MySitemap.elm new file mode 100644 index 00000000..50a8044a --- /dev/null +++ b/examples/docs/src/MySitemap.elm @@ -0,0 +1,37 @@ +module MySitemap exposing (..) + +import Dict +import Metadata exposing (Metadata(..)) +import Pages +import Pages.PagePath as PagePath exposing (PagePath) +import RssFeed +import Sitemap +import Time +import Xml +import Xml.Encode exposing (..) + + +build : + { siteUrl : String + } + -> + List + { path : PagePath Pages.PathKey + , frontmatter : Metadata + } + -> + { path : List String + , content : String + } +build config siteMetadata = + { path = [ "sitemap.xml" ] + , content = + Sitemap.build config + (siteMetadata + |> List.map + (\page -> + page.path + |> PagePath.toString + ) + ) + } diff --git a/examples/docs/src/Sitemap.elm b/examples/docs/src/Sitemap.elm new file mode 100644 index 00000000..0842f962 --- /dev/null +++ b/examples/docs/src/Sitemap.elm @@ -0,0 +1,43 @@ +module Sitemap exposing (build) + +import Date exposing (Date) +import Dict +import Imf.DateTime +import Time +import Xml +import Xml.Encode exposing (..) + + +build : + { siteUrl : String + } + -> List String + -> String +build { siteUrl } urls = + object + [ ( "urlset" + , Dict.fromList + [ ( "xmlns", string "http://www.sitemaps.org/schemas/sitemap/0.9" ) + ] + , urls + |> List.map (urlXml siteUrl) + |> list + ) + ] + |> encode 0 + + +urlXml siteUrl url = + object + [ ( "url" + , Dict.empty + , list + [ keyValue "loc" <| string (siteUrl ++ url) + ] + ) + ] + + +keyValue : String -> Xml.Value -> Xml.Value +keyValue key value = + object [ ( key, Dict.empty, value ) ] From ed7d4aadd37a4755b6f4b53c0ea2bc5e04e49f4b Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Wed, 8 Jan 2020 09:53:39 -0800 Subject: [PATCH 15/54] Update sitemap. --- examples/docs/src/MySitemap.elm | 3 +-- examples/docs/src/Sitemap.elm | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/examples/docs/src/MySitemap.elm b/examples/docs/src/MySitemap.elm index 50a8044a..d7273b85 100644 --- a/examples/docs/src/MySitemap.elm +++ b/examples/docs/src/MySitemap.elm @@ -30,8 +30,7 @@ build config siteMetadata = (siteMetadata |> List.map (\page -> - page.path - |> PagePath.toString + { path = PagePath.toString page.path, lastMod = Nothing } ) ) } diff --git a/examples/docs/src/Sitemap.elm b/examples/docs/src/Sitemap.elm index 0842f962..2edb0eb3 100644 --- a/examples/docs/src/Sitemap.elm +++ b/examples/docs/src/Sitemap.elm @@ -1,5 +1,8 @@ module Sitemap exposing (build) +{-| +-} + import Date exposing (Date) import Dict import Imf.DateTime @@ -8,10 +11,16 @@ import Xml import Xml.Encode exposing (..) +type alias Entry = + { path : String + , lastMod : Maybe String + } + + build : { siteUrl : String } - -> List String + -> List Entry -> String build { siteUrl } urls = object @@ -27,13 +36,19 @@ build { siteUrl } urls = |> encode 0 -urlXml siteUrl url = +urlXml siteUrl entry = object [ ( "url" , Dict.empty - , list - [ keyValue "loc" <| string (siteUrl ++ url) - ] + , [ string (siteUrl ++ entry.path) + |> keyValue "loc" + |> Just + , entry.lastMod + |> Maybe.map string + |> Maybe.map (keyValue "lastmod") + ] + |> List.filterMap identity + |> list ) ] From 79e68efba89891787b9f7c9570cbf2eb33c38902 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Wed, 8 Jan 2020 09:55:31 -0800 Subject: [PATCH 16/54] Restore --port functionality in dev mode. --- generator/src/elm-pages.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generator/src/elm-pages.js b/generator/src/elm-pages.js index 09a2c687..11fdb1ff 100755 --- a/generator/src/elm-pages.js +++ b/generator/src/elm-pages.js @@ -94,7 +94,8 @@ function run() { routes, debug: contents.debug, manifestConfig: payload.manifest, - routesWithRequests: payload.pages + routesWithRequests: payload.pages, + customPort: contents.customPort }); } } else { From a4390b35de2afe52275f4113ee4f00b557633c73 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 12 Jan 2020 18:58:30 -0800 Subject: [PATCH 17/54] Update CHANGELOG-NPM.md --- CHANGELOG-NPM.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG-NPM.md b/CHANGELOG-NPM.md index 2840e4ea..e1eb1582 100644 --- a/CHANGELOG-NPM.md +++ b/CHANGELOG-NPM.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [1.1.7] - 2020-01-12 + +### Fixed +- Newlines and escaped double quotes (`"`s) are handled properly in content frontmatter now. See [#41](https://github.com/dillonkearns/elm-pages/pull/41). Thank you [Luke](https://github.com/lukewestby)! 🎉🙏 + ## [1.1.6] - 2020-01-04 ### Added From acbf63132b5e8121b4c330fc8723913df8d07c4f Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sun, 12 Jan 2020 18:59:12 -0800 Subject: [PATCH 18/54] 1.1.7 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ceffa007..9fe9efe4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "elm-pages", - "version": "1.1.6", + "version": "1.1.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index af75f3de..38b7a9c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "elm-pages", - "version": "1.1.6", + "version": "1.1.7", "homepage": "http://elm-pages.com", "description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.", "main": "index.js", From fe6e04661038391bf4a9f52affbb49d8cdb4be45 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 20 Jan 2020 07:59:29 -0800 Subject: [PATCH 19/54] Add script defer tag. --- generator/src/develop.js | 5 +++++ package-lock.json | 8 ++++++++ package.json | 1 + 3 files changed, 14 insertions(+) diff --git a/generator/src/develop.js b/generator/src/develop.js index 4382e1d2..3b4b6eca 100644 --- a/generator/src/develop.js +++ b/generator/src/develop.js @@ -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"); @@ -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: { diff --git a/package-lock.json b/package-lock.json index 9fe9efe4..e19b102c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9220,6 +9220,14 @@ "ajv-keywords": "^3.1.0" } }, + "script-ext-html-webpack-plugin": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/script-ext-html-webpack-plugin/-/script-ext-html-webpack-plugin-2.1.4.tgz", + "integrity": "sha512-7MAv3paAMfh9y2Rg+yQKp9jEGC5cEcmdge4EomRqri10qoczmliYEVPVNz0/5e9QQ202e05qDll9B8zZlY9N1g==", + "requires": { + "debug": "^4.1.1" + } + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", diff --git a/package.json b/package.json index 38b7a9c9..d51979ca 100644 --- a/package.json +++ b/package.json @@ -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", From defe0beb33796fb2e6fb679665c51cb3fedfb207 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 20 Jan 2020 08:02:46 -0800 Subject: [PATCH 20/54] Don't call user's init function during pre-rendering phase. --- index.js | 3 ++- src/Pages/Internal/Platform.elm | 34 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 6a9d6f79..f89b1338 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,8 @@ module.exports = function pagesInit( document.addEventListener("DOMContentLoaded", function() { let app = mainElmModule.init({ flags: { - secrets: null + secrets: null, + isPrerendering: navigator.userAgent.indexOf("Headless") >= 0, } }); diff --git a/src/Pages/Internal/Platform.elm b/src/Pages/Internal/Platform.elm index 5876f213..575c6055 100644 --- a/src/Pages/Internal/Platform.elm +++ b/src/Pages/Internal/Platform.elm @@ -254,6 +254,17 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla 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 @@ -284,6 +295,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla , url = url , userModel = userModel , contentCache = contentCache + , phase = phase } , cmd ) @@ -297,6 +309,7 @@ init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel fla , url = url , userModel = userModel , contentCache = contentCache + , phase = Client } , Cmd.batch [ userCmd |> Cmd.map UserMsg @@ -333,9 +346,15 @@ type alias ModelDetails userModel metadata view = , url : Url.Url , contentCache : ContentCache metadata view , userModel : userModel + , phase : Phase } +type Phase + = Prerender + | Client + + update : String -> @@ -524,7 +543,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) From 07e445b2cc515255e92b8577d227920df002eb49 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 20 Jan 2020 08:03:42 -0800 Subject: [PATCH 21/54] Pass in content.json for current page to ensure there is no "Missing content" message flash. --- index.js | 17 +++++++ src/Pages/ContentCache.elm | 70 ++++++++++++++++------------- src/Pages/Internal/Platform.elm | 19 +++++++- src/Pages/Internal/Platform/Cli.elm | 2 +- 4 files changed, 74 insertions(+), 34 deletions(-) diff --git a/index.js b/index.js index f89b1338..67601391 100644 --- a/index.js +++ b/index.js @@ -7,10 +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, isPrerendering: navigator.userAgent.indexOf("Headless") >= 0, + contentJson } }); @@ -34,6 +37,9 @@ module.exports = function pagesInit( document.dispatchEvent(new Event("prerender-trigger")); }); + + }) + }); function setupLinkPrefetching() { @@ -131,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); +} diff --git a/src/Pages/ContentCache.elm b/src/Pages/ContentCache.elm index cfced161..826cb044 100644 --- a/src/Pages/ContentCache.elm +++ b/src/Pages/ContentCache.elm @@ -111,9 +111,10 @@ pagesWithErrors cache = init : Document metadata view -> Content + -> Maybe (ContentJson String) -> ContentCache metadata view -init document content = - parseMetadata document content +init document content maybeInitialPageContent = + parseMetadata maybeInitialPageContent document content |> List.map (\tuple -> Tuple.mapSecond @@ -149,39 +150,44 @@ createBuildError path decodeError = parseMetadata : - Document metadata view + Maybe (ContentJson String) + -> 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 initialPageContent -> + Parsed metadata + { body = renderer initialPageContent.body + , staticData = initialPageContent.staticData + } - Nothing -> - Err ("Could not find extension '" ++ extension ++ "'") - ) + Nothing -> + NeedContent extension metadata + ) + |> Tuple.pair path + + Nothing -> + Err ("Could not find extension '" ++ extension ++ "'") + |> Tuple.pair path ) @@ -327,8 +333,8 @@ lazyLoad document url cacheResult = |> Task.map (\downloadedContent -> update cacheResult - (\thing -> - parseContent extension thing document + (\value -> + parseContent extension value document ) url downloadedContent diff --git a/src/Pages/Internal/Platform.elm b/src/Pages/Internal/Platform.elm index 575c6055..84672ec7 100644 --- a/src/Pages/Internal/Platform.elm +++ b/src/Pages/Internal/Platform.elm @@ -217,6 +217,12 @@ type alias Flags = Decode.Value +type alias ContentJson = + { body : String + , staticData : Dict String String + } + + init : pathKey -> String @@ -249,7 +255,18 @@ init : init pathKey canonicalSiteUrl document toJsPort viewFn content initUserModel flags url key = let contentCache = - ContentCache.init document content + ContentCache.init document content 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 -> diff --git a/src/Pages/Internal/Platform/Cli.elm b/src/Pages/Internal/Platform/Cli.elm index ba87f3fb..a0e74544 100644 --- a/src/Pages/Internal/Platform/Cli.elm +++ b/src/Pages/Internal/Platform/Cli.elm @@ -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 From 3a4881d002305e9fa49d1e23ec47fbc67bb8f3b2 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Mon, 20 Jan 2020 08:03:59 -0800 Subject: [PATCH 22/54] Change relative path reference for content.json preload tag. --- generator/src/template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/src/template.html b/generator/src/template.html index 5859a472..08520225 100644 --- a/generator/src/template.html +++ b/generator/src/template.html @@ -1,7 +1,7 @@ - +