mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-30 23:06:10 +03:00
Merge pull request #38 from dillonkearns/generate-files
Add hook for generating static files
This commit is contained in:
commit
dc6aac330e
@ -9,8 +9,12 @@
|
||||
"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",
|
||||
"dillonkearns/elm-rss": "1.0.0",
|
||||
"dillonkearns/elm-sitemap": "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",
|
||||
@ -42,7 +46,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": {
|
||||
|
72
examples/docs/src/Feed.elm
Normal file
72
examples/docs/src/Feed.elm
Normal file
@ -0,0 +1,72 @@
|
||||
module Feed exposing (fileToGenerate)
|
||||
|
||||
import Metadata exposing (Metadata(..))
|
||||
import Pages
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Rss
|
||||
|
||||
|
||||
fileToGenerate :
|
||||
{ siteTagline : String
|
||||
, siteUrl : String
|
||||
}
|
||||
->
|
||||
List
|
||||
{ path : PagePath Pages.PathKey
|
||||
, frontmatter : Metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
fileToGenerate config siteMetadata =
|
||||
{ path = [ "blog", "feed.xml" ]
|
||||
, content = generate config siteMetadata
|
||||
}
|
||||
|
||||
|
||||
generate :
|
||||
{ siteTagline : String
|
||||
, siteUrl : String
|
||||
}
|
||||
->
|
||||
List
|
||||
{ path : PagePath Pages.PathKey
|
||||
, frontmatter : Metadata
|
||||
, body : String
|
||||
}
|
||||
-> String
|
||||
generate { siteTagline, siteUrl } siteMetadata =
|
||||
Rss.generate
|
||||
{ title = "elm-pages Blog"
|
||||
, description = siteTagline
|
||||
, url = "https://elm-pages.com/blog"
|
||||
, lastBuildTime = Pages.builtAt
|
||||
, generator = Just "elm-pages"
|
||||
, items = siteMetadata |> List.filterMap metadataToRssItem
|
||||
, siteUrl = siteUrl
|
||||
}
|
||||
|
||||
|
||||
metadataToRssItem :
|
||||
{ path : PagePath Pages.PathKey
|
||||
, frontmatter : Metadata
|
||||
, body : String
|
||||
}
|
||||
-> Maybe Rss.Item
|
||||
metadataToRssItem page =
|
||||
case page.frontmatter of
|
||||
Article article ->
|
||||
Just
|
||||
{ title = article.title
|
||||
, description = article.description
|
||||
, url = PagePath.toString page.path
|
||||
, categories = []
|
||||
, author = article.author.name
|
||||
, pubDate = Rss.Date article.published
|
||||
, content = Nothing
|
||||
}
|
||||
|
||||
_ ->
|
||||
Nothing
|
@ -11,6 +11,7 @@ import Element.Border
|
||||
import Element.Events
|
||||
import Element.Font as Font
|
||||
import Element.Region
|
||||
import Feed
|
||||
import FontAwesome
|
||||
import Head
|
||||
import Head.Seo as Seo
|
||||
@ -21,6 +22,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
|
||||
@ -65,11 +67,31 @@ main =
|
||||
, documents = [ markdownDocument ]
|
||||
, manifest = manifest
|
||||
, canonicalSiteUrl = canonicalSiteUrl
|
||||
, generateFiles = generateFiles
|
||||
, onPageChange = OnPageChange
|
||||
, internals = Pages.internals
|
||||
}
|
||||
|
||||
|
||||
generateFiles :
|
||||
List
|
||||
{ path : PagePath Pages.PathKey
|
||||
, frontmatter : Metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
List
|
||||
(Result String
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
)
|
||||
generateFiles siteMetadata =
|
||||
[ Feed.fileToGenerate { siteTagline = siteTagline, siteUrl = canonicalSiteUrl } siteMetadata |> Ok
|
||||
, MySitemap.build { siteUrl = canonicalSiteUrl } siteMetadata |> Ok
|
||||
]
|
||||
|
||||
|
||||
markdownDocument : ( String, Pages.Document.DocumentHandler Metadata ( MarkdownRenderer.TableOfContents, List (Element Msg) ) )
|
||||
markdownDocument =
|
||||
Pages.Document.parser
|
||||
@ -462,6 +484,13 @@ highlightableLink currentPath linkDirectory displayName =
|
||||
}
|
||||
|
||||
|
||||
commonHeadTags : List (Head.Tag Pages.PathKey)
|
||||
commonHeadTags =
|
||||
[ Head.rssLink "/blog/feed.xml"
|
||||
, Head.sitemapLink "/sitemap.xml"
|
||||
]
|
||||
|
||||
|
||||
{-| <https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards>
|
||||
<https://htmlhead.dev>
|
||||
<https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names>
|
||||
@ -469,127 +498,129 @@ highlightableLink currentPath linkDirectory displayName =
|
||||
-}
|
||||
head : Metadata -> List (Head.Tag Pages.PathKey)
|
||||
head metadata =
|
||||
case metadata of
|
||||
Metadata.Page meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.website
|
||||
commonHeadTags
|
||||
++ (case metadata of
|
||||
Metadata.Page meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.website
|
||||
|
||||
Metadata.Doc meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, locale = Nothing
|
||||
, description = siteTagline
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.website
|
||||
Metadata.Doc meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, locale = Nothing
|
||||
, description = siteTagline
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.website
|
||||
|
||||
Metadata.Article meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = meta.image
|
||||
, alt = meta.description
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = meta.description
|
||||
, locale = Nothing
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.article
|
||||
{ tags = []
|
||||
, section = Nothing
|
||||
, publishedTime = Just (Date.toIsoString meta.published)
|
||||
, modifiedTime = Nothing
|
||||
, expirationTime = Nothing
|
||||
}
|
||||
Metadata.Article meta ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = meta.image
|
||||
, alt = meta.description
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = meta.description
|
||||
, locale = Nothing
|
||||
, title = meta.title
|
||||
}
|
||||
|> Seo.article
|
||||
{ tags = []
|
||||
, section = Nothing
|
||||
, publishedTime = Just (Date.toIsoString meta.published)
|
||||
, modifiedTime = Nothing
|
||||
, expirationTime = Nothing
|
||||
}
|
||||
|
||||
Metadata.Author meta ->
|
||||
let
|
||||
( firstName, lastName ) =
|
||||
case meta.name |> String.split " " of
|
||||
[ first, last ] ->
|
||||
( first, last )
|
||||
Metadata.Author meta ->
|
||||
let
|
||||
( firstName, lastName ) =
|
||||
case meta.name |> String.split " " of
|
||||
[ first, last ] ->
|
||||
( first, last )
|
||||
|
||||
[ first, middle, last ] ->
|
||||
( first ++ " " ++ middle, last )
|
||||
[ first, middle, last ] ->
|
||||
( first ++ " " ++ middle, last )
|
||||
|
||||
[] ->
|
||||
( "", "" )
|
||||
[] ->
|
||||
( "", "" )
|
||||
|
||||
_ ->
|
||||
( meta.name, "" )
|
||||
in
|
||||
Seo.summary
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = meta.avatar
|
||||
, alt = meta.name ++ "'s elm-pages articles."
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = meta.bio
|
||||
, locale = Nothing
|
||||
, title = meta.name ++ "'s elm-pages articles."
|
||||
}
|
||||
|> Seo.profile
|
||||
{ firstName = firstName
|
||||
, lastName = lastName
|
||||
, username = Nothing
|
||||
}
|
||||
_ ->
|
||||
( meta.name, "" )
|
||||
in
|
||||
Seo.summary
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = meta.avatar
|
||||
, alt = meta.name ++ "'s elm-pages articles."
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = meta.bio
|
||||
, locale = Nothing
|
||||
, title = meta.name ++ "'s elm-pages articles."
|
||||
}
|
||||
|> Seo.profile
|
||||
{ firstName = firstName
|
||||
, lastName = lastName
|
||||
, username = Nothing
|
||||
}
|
||||
|
||||
Metadata.BlogIndex ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = "elm-pages blog"
|
||||
}
|
||||
|> Seo.website
|
||||
Metadata.BlogIndex ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = "elm-pages blog"
|
||||
}
|
||||
|> Seo.website
|
||||
|
||||
Metadata.Showcase ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = "elm-pages sites showcase"
|
||||
}
|
||||
|> Seo.website
|
||||
Metadata.Showcase ->
|
||||
Seo.summaryLarge
|
||||
{ canonicalUrlOverride = Nothing
|
||||
, siteName = "elm-pages"
|
||||
, image =
|
||||
{ url = images.iconPng
|
||||
, alt = "elm-pages logo"
|
||||
, dimensions = Nothing
|
||||
, mimeType = Nothing
|
||||
}
|
||||
, description = siteTagline
|
||||
, locale = Nothing
|
||||
, title = "elm-pages sites showcase"
|
||||
}
|
||||
|> Seo.website
|
||||
)
|
||||
|
||||
|
||||
canonicalSiteUrl : String
|
||||
|
32
examples/docs/src/MySitemap.elm
Normal file
32
examples/docs/src/MySitemap.elm
Normal file
@ -0,0 +1,32 @@
|
||||
module MySitemap exposing (..)
|
||||
|
||||
import Metadata exposing (Metadata(..))
|
||||
import Pages
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Sitemap
|
||||
|
||||
|
||||
build :
|
||||
{ siteUrl : String
|
||||
}
|
||||
->
|
||||
List
|
||||
{ path : PagePath Pages.PathKey
|
||||
, frontmatter : Metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
build config siteMetadata =
|
||||
{ path = [ "sitemap.xml" ]
|
||||
, content =
|
||||
Sitemap.build config
|
||||
(siteMetadata
|
||||
|> List.map
|
||||
(\page ->
|
||||
{ path = PagePath.toString page.path, lastMod = Nothing }
|
||||
)
|
||||
)
|
||||
}
|
@ -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
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -16,11 +16,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);
|
||||
@ -66,12 +67,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) {
|
||||
@ -119,12 +121,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/**/*",
|
||||
|
@ -86,6 +86,7 @@ function run() {
|
||||
markdownContent,
|
||||
content,
|
||||
function(payload) {
|
||||
console.log('@@@@@@@@@ filesToGenerate', payload.filesToGenerate);
|
||||
if (contents.watch) {
|
||||
startWatchIfNeeded();
|
||||
if (!devServerRunning) {
|
||||
@ -95,6 +96,7 @@ function run() {
|
||||
debug: contents.debug,
|
||||
manifestConfig: payload.manifest,
|
||||
routesWithRequests: payload.pages,
|
||||
filesToGenerate: payload.filesToGenerate,
|
||||
customPort: contents.customPort
|
||||
});
|
||||
}
|
||||
@ -107,7 +109,8 @@ function run() {
|
||||
{
|
||||
routes,
|
||||
manifestConfig: payload.manifest,
|
||||
routesWithRequests: payload.pages
|
||||
routesWithRequests: payload.pages,
|
||||
filesToGenerate: payload.filesToGenerate
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ workbox.precaching.precacheAndRoute(self.__precacheManifest);
|
||||
workbox.routing.registerNavigationRoute(
|
||||
workbox.precaching.getCacheKeyForURL("/index.html"),
|
||||
{
|
||||
blacklist: [/admin/]
|
||||
blacklist: [/admin/, /\./]
|
||||
}
|
||||
);
|
||||
workbox.routing.registerRoute(
|
||||
|
44
src/Head.elm
44
src/Head.elm
@ -1,5 +1,6 @@
|
||||
module Head exposing
|
||||
( Tag, metaName, metaProperty
|
||||
, rssLink, sitemapLink
|
||||
, AttributeValue
|
||||
, currentPageFullUrl, fullImageUrl, fullPageUrl, raw
|
||||
, toJson, canonicalLink
|
||||
@ -15,6 +16,7 @@ But this module might be useful if you have a special use case, or if you are
|
||||
writing a plugin package to extend `elm-pages`.
|
||||
|
||||
@docs Tag, metaName, metaProperty
|
||||
@docs rssLink, sitemapLink
|
||||
|
||||
|
||||
## `AttributeValue`s
|
||||
@ -106,9 +108,49 @@ canonicalLink maybePath =
|
||||
]
|
||||
|
||||
|
||||
{-| Add a link to the site's RSS feed.
|
||||
|
||||
Example:
|
||||
|
||||
rssLink "/feed.xml"
|
||||
|
||||
```html
|
||||
<link rel="alternate" type="application/rss+xml" href="/rss.xml">
|
||||
```
|
||||
|
||||
-}
|
||||
rssLink : String -> Tag pathKey
|
||||
rssLink url =
|
||||
node "link"
|
||||
[ ( "rel", raw "alternate" )
|
||||
, ( "type", raw "application/rss+xml" )
|
||||
, ( "href", raw url )
|
||||
]
|
||||
|
||||
|
||||
{-| Add a link to the site's RSS feed.
|
||||
|
||||
Example:
|
||||
|
||||
sitemapLink "/feed.xml"
|
||||
|
||||
```html
|
||||
<link rel="sitemap" type="application/xml" href="/sitemap.xml">
|
||||
```
|
||||
|
||||
-}
|
||||
sitemapLink : String -> Tag pathKey
|
||||
sitemapLink url =
|
||||
node "link"
|
||||
[ ( "rel", raw "sitemap" )
|
||||
, ( "type", raw "application/xml" )
|
||||
, ( "href", raw url )
|
||||
]
|
||||
|
||||
|
||||
{-| Example:
|
||||
|
||||
Head.metaProperty "fb:app_id" ( Head.raw "123456789" )
|
||||
Head.metaProperty "fb:app_id" (Head.raw "123456789")
|
||||
|
||||
Results in `<meta property="fb:app_id" content="123456789" />`
|
||||
|
||||
|
@ -533,6 +533,19 @@ application :
|
||||
, content : Content
|
||||
, toJsPort : Json.Encode.Value -> Cmd Never
|
||||
, manifest : Manifest.Config pathKey
|
||||
, generateFiles :
|
||||
List
|
||||
{ path : PagePath pathKey
|
||||
, frontmatter : metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
List
|
||||
(Result String
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
)
|
||||
, canonicalSiteUrl : String
|
||||
, pathKey : pathKey
|
||||
, onPageChange : PagePath pathKey -> userMsg
|
||||
@ -613,6 +626,19 @@ cliApplication :
|
||||
, content : Content
|
||||
, toJsPort : Json.Encode.Value -> Cmd Never
|
||||
, manifest : Manifest.Config pathKey
|
||||
, generateFiles :
|
||||
List
|
||||
{ path : PagePath pathKey
|
||||
, frontmatter : metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
List
|
||||
(Result String
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
)
|
||||
, canonicalSiteUrl : String
|
||||
, pathKey : pathKey
|
||||
, onPageChange : PagePath pathKey -> userMsg
|
||||
|
@ -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
|
||||
|
||||
|
||||
@ -128,34 +150,50 @@ 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
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
List
|
||||
(Result String
|
||||
{ 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
|
||||
@ -177,7 +215,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
|
||||
|
||||
@ -248,21 +286,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 =
|
||||
@ -298,7 +322,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
|
||||
@ -324,6 +348,8 @@ init toModel contentCache siteMetadata config flags =
|
||||
staticResponsesInit []
|
||||
in
|
||||
updateAndSendPortIfDone
|
||||
config
|
||||
siteMetadata
|
||||
(Model
|
||||
staticResponses
|
||||
secrets
|
||||
@ -332,10 +358,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)
|
||||
@ -343,10 +370,11 @@ init toModel contentCache siteMetadata config flags =
|
||||
mode
|
||||
)
|
||||
toModel
|
||||
config.manifest
|
||||
|
||||
Err error ->
|
||||
updateAndSendPortIfDone
|
||||
config
|
||||
siteMetadata
|
||||
(Model Dict.empty
|
||||
SecretsDict.masked
|
||||
[ { title = "Internal Error"
|
||||
@ -357,20 +385,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
|
||||
@ -382,24 +415,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
|
||||
@ -456,7 +477,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
|
||||
@ -585,8 +606,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
|
||||
@ -737,18 +766,86 @@ sendStaticResponsesIfDone mode secrets allRawResponses errors staticResponses ma
|
||||
let
|
||||
updatedAllRawResponses =
|
||||
Dict.empty
|
||||
|
||||
generatedFiles =
|
||||
siteMetadata
|
||||
|> Result.withDefault []
|
||||
|> List.map
|
||||
(\( pagePath, metadata ) ->
|
||||
let
|
||||
contentForPage =
|
||||
config.content
|
||||
|> List.filterMap
|
||||
(\( path, { body } ) ->
|
||||
let
|
||||
pagePathToGenerate =
|
||||
PagePath.toString pagePath
|
||||
|
||||
currentContentPath =
|
||||
"/" ++ (path |> String.join "/")
|
||||
in
|
||||
if pagePathToGenerate == currentContentPath then
|
||||
Just body
|
||||
|
||||
else
|
||||
Nothing
|
||||
)
|
||||
|> List.head
|
||||
|> Maybe.andThen identity
|
||||
in
|
||||
{ path = pagePath
|
||||
, frontmatter = metadata
|
||||
, body = contentForPage |> Maybe.withDefault ""
|
||||
}
|
||||
)
|
||||
|> config.generateFiles
|
||||
|
||||
generatedOkayFiles =
|
||||
generatedFiles
|
||||
|> List.filterMap
|
||||
(\result ->
|
||||
case result of
|
||||
Ok ok ->
|
||||
Just ok
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
)
|
||||
|
||||
generatedFileErrors =
|
||||
generatedFiles
|
||||
|> List.filterMap
|
||||
(\result ->
|
||||
case result of
|
||||
Ok ok ->
|
||||
Nothing
|
||||
|
||||
Err error ->
|
||||
Just
|
||||
{ title = "Generate Files Error"
|
||||
, message =
|
||||
[ Terminal.text "I encountered an Err from your generateFiles function. Message:\n"
|
||||
, Terminal.text <| "Error: " ++ error
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
allErrors : List BuildError
|
||||
allErrors =
|
||||
errors ++ failedRequests ++ generatedFileErrors
|
||||
in
|
||||
( updatedAllRawResponses
|
||||
, SendJsData
|
||||
(if List.isEmpty errors && List.isEmpty failedRequests then
|
||||
(if List.isEmpty allErrors then
|
||||
Success
|
||||
(ToJsSuccessPayload
|
||||
(encodeStaticResponses mode staticResponses)
|
||||
manifest
|
||||
config.manifest
|
||||
generatedOkayFiles
|
||||
)
|
||||
|
||||
else
|
||||
Errors <| BuildError.errorsToString (failedRequests ++ errors)
|
||||
Errors <| BuildError.errorsToString allErrors
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -77,6 +77,19 @@ application :
|
||||
}
|
||||
, documents : List ( String, Document.DocumentHandler metadata view )
|
||||
, manifest : Pages.Manifest.Config pathKey
|
||||
, generateFiles :
|
||||
List
|
||||
{ path : PagePath pathKey
|
||||
, frontmatter : metadata
|
||||
, body : String
|
||||
}
|
||||
->
|
||||
List
|
||||
(Result String
|
||||
{ path : List String
|
||||
, content : String
|
||||
}
|
||||
)
|
||||
, onPageChange : PagePath pathKey -> userMsg
|
||||
, canonicalSiteUrl : String
|
||||
, internals : Pages.Internal.Internal pathKey
|
||||
@ -97,6 +110,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
|
||||
|
@ -564,9 +564,17 @@ start pages =
|
||||
Debug.todo "Couldn't find page"
|
||||
}
|
||||
in
|
||||
{-
|
||||
(Model -> model)
|
||||
-> ContentCache.ContentCache metadata view
|
||||
-> Result (List BuildError) (List ( PagePath pathKey, metadata ))
|
||||
-> Config pathKey userMsg userModel metadata view
|
||||
-> Decode.Value
|
||||
-> ( model, Effect pathKey )
|
||||
-}
|
||||
ProgramTest.createDocument
|
||||
{ init = Main.init identity contentCache siteMetadata config
|
||||
, update = Main.update config
|
||||
, update = Main.update siteMetadata config
|
||||
, view = \_ -> { title = "", body = [] }
|
||||
}
|
||||
|> ProgramTest.withSimulatedEffects simulateEffects
|
||||
|
Loading…
Reference in New Issue
Block a user