From daa128796d6c6ebde84e7ab3dc419d1b32309faf Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Fri, 9 Oct 2020 17:08:02 -0700 Subject: [PATCH] Split off beta build process and add snapshot test. --- elm.json | 1 + examples/simple/elm.json | 3 +- src/Pages/Internal/Platform/Cli.elm | 226 ++++++++++++------ src/Pages/Internal/Platform/Effect.elm | 1 + src/Pages/Internal/Platform/Mode.elm | 4 + .../Internal/Platform/StaticResponses.elm | 9 +- tests/StaticHttpRequestsTests.elm | 10 +- .../elm-to-html-output.test.js.snap | 3 + tests/elm-to-html-output.test.js | 65 +++++ 9 files changed, 242 insertions(+), 80 deletions(-) create mode 100644 tests/__snapshots__/elm-to-html-output.test.js.snap create mode 100644 tests/elm-to-html-output.test.js diff --git a/elm.json b/elm.json index bdcb4fa4..a9e773ba 100644 --- a/elm.json +++ b/elm.json @@ -21,6 +21,7 @@ ], "elm-version": "0.19.0 <= v < 0.20.0", "dependencies": { + "ThinkAlexandria/elm-html-in-elm": "1.0.1 <= v < 2.0.0", "avh4/elm-color": "1.0.0 <= v < 2.0.0", "elm/browser": "1.0.1 <= v < 2.0.0", "elm/core": "1.0.2 <= v < 2.0.0", diff --git a/examples/simple/elm.json b/examples/simple/elm.json index b6a472ff..527b4138 100644 --- a/examples/simple/elm.json +++ b/examples/simple/elm.json @@ -9,6 +9,7 @@ "elm-version": "0.19.1", "dependencies": { "direct": { + "ThinkAlexandria/elm-html-in-elm": "1.0.1", "avh4/elm-color": "1.0.0", "billstclair/elm-xml-eeue56": "1.0.1", "dillonkearns/elm-markdown": "4.0.2", @@ -58,4 +59,4 @@ }, "indirect": {} } -} \ No newline at end of file +} diff --git a/src/Pages/Internal/Platform/Cli.elm b/src/Pages/Internal/Platform/Cli.elm index c18dd0e7..d86b1f2c 100644 --- a/src/Pages/Internal/Platform/Cli.elm +++ b/src/Pages/Internal/Platform/Cli.elm @@ -12,6 +12,8 @@ module Pages.Internal.Platform.Cli exposing import BuildError exposing (BuildError) import Codec exposing (Codec) import Dict exposing (Dict) +import ElmHtml.InternalTypes exposing (decodeElmHtml) +import ElmHtml.ToString exposing (FormatOptions, defaultFormatOptions, nodeToStringWithOptions) import Head import Html exposing (Html) import Http @@ -103,8 +105,7 @@ type alias Config pathKey userMsg userModel metadata view = -> StaticHttp.Request (List - (Result - String + (Result String { path : List String , content : String } @@ -160,6 +161,40 @@ cliApplication cliMsgConstructor narrowMsg toModel fromModel config = } +viewRenderer : Html msg -> String +viewRenderer html = + let + options = + { defaultFormatOptions | newLines = False, indent = 0 } + in + viewDecoder options html + + +viewDecoder : FormatOptions -> Html msg -> String +viewDecoder options viewHtml = + case + Decode.decodeValue + (decodeElmHtml + (\_ _ -> + Decode.succeed () + ) + ) + (asJsonView + viewHtml + ) + of + Ok str -> + nodeToStringWithOptions options str + + Err err -> + "Error: " ++ Decode.errorToString err + + +asJsonView : Html msg -> Decode.Value +asJsonView x = + Json.Encode.string "REPLACE_ME_WITH_JSON_STRINGIFY" + + perform : (Msg -> msg) -> (Json.Encode.Value -> Cmd Never) -> Effect pathKey -> Cmd msg perform cliMsgConstructor toJsPort effect = case effect of @@ -208,6 +243,13 @@ perform cliMsgConstructor toJsPort effect = , tracker = Nothing } + Effect.SendSinglePage info -> + Json.Encode.object + [ ( "html", Json.Encode.string info.html ) + ] + |> toJsPort + |> Cmd.map never + flagsDecoder : Decode.Decoder @@ -244,76 +286,12 @@ init : init toModel contentCache siteMetadata config flags = case Decode.decodeValue flagsDecoder flags of Ok { secrets, mode, staticHttpCache } -> - case contentCache of - Ok _ -> - case ContentCache.pagesWithErrors contentCache of - [] -> - let - requests = - Result.andThen - (\metadata -> - staticResponseForPage metadata config.view - ) - siteMetadata + case mode of + Mode.ElmToHtmlBeta -> + elmToHtmlBetaInit { secrets = secrets, mode = mode, staticHttpCache = staticHttpCache } toModel contentCache siteMetadata config flags - staticResponses : StaticResponses - staticResponses = - case requests of - Ok okRequests -> - StaticResponses.init staticHttpCache siteMetadata config okRequests - - Err errors -> - -- TODO need to handle errors better? - StaticResponses.init staticHttpCache siteMetadata config [] - in - StaticResponses.nextStep config siteMetadata mode secrets staticHttpCache [] staticResponses - |> nextStepToEffect (Model staticResponses secrets [] staticHttpCache mode []) - |> Tuple.mapFirst toModel - - pageErrors -> - let - requests = - Result.andThen - (\metadata -> - staticResponseForPage metadata config.view - ) - siteMetadata - - staticResponses : StaticResponses - staticResponses = - case requests of - Ok okRequests -> - StaticResponses.init staticHttpCache siteMetadata config okRequests - - Err errors -> - -- TODO need to handle errors better? - StaticResponses.init staticHttpCache siteMetadata config [] - in - updateAndSendPortIfDone - config - siteMetadata - (Model - staticResponses - secrets - pageErrors - staticHttpCache - mode - [] - ) - toModel - - Err metadataParserErrors -> - updateAndSendPortIfDone - config - siteMetadata - (Model StaticResponses.error - secrets - (metadataParserErrors |> List.map Tuple.second) - staticHttpCache - mode - [] - ) - toModel + _ -> + initLegacy { secrets = secrets, mode = mode, staticHttpCache = staticHttpCache } toModel contentCache siteMetadata config flags Err error -> updateAndSendPortIfDone @@ -333,6 +311,109 @@ init toModel contentCache siteMetadata config flags = toModel +elmToHtmlBetaInit { secrets, mode, staticHttpCache } toModel contentCache siteMetadata config flags = + --case flags of + --init toModel contentCache siteMetadata config flags + --|> Tuple.mapSecond (perform cliMsgConstructor config.toJsPort) + --|> Tuple.mapSecond + -- (\cmd -> + --Cmd.map AppMsg + --Cmd.none + ( toModel + (Model StaticResponses.error + secrets + [] + --(metadataParserErrors |> List.map Tuple.second) + staticHttpCache + mode + [] + ) + , { html = + Html.div [] + [ Html.text "Hello!!!!!" ] + |> viewRenderer + } + |> Effect.SendSinglePage + ) + + + +--) + + +initLegacy { secrets, mode, staticHttpCache } toModel contentCache siteMetadata config flags = + case contentCache of + Ok _ -> + case ContentCache.pagesWithErrors contentCache of + [] -> + let + requests = + Result.andThen + (\metadata -> + staticResponseForPage metadata config.view + ) + siteMetadata + + staticResponses : StaticResponses + staticResponses = + case requests of + Ok okRequests -> + StaticResponses.init staticHttpCache siteMetadata config okRequests + + Err errors -> + -- TODO need to handle errors better? + StaticResponses.init staticHttpCache siteMetadata config [] + in + StaticResponses.nextStep config siteMetadata mode secrets staticHttpCache [] staticResponses + |> nextStepToEffect (Model staticResponses secrets [] staticHttpCache mode []) + |> Tuple.mapFirst toModel + + pageErrors -> + let + requests = + Result.andThen + (\metadata -> + staticResponseForPage metadata config.view + ) + siteMetadata + + staticResponses : StaticResponses + staticResponses = + case requests of + Ok okRequests -> + StaticResponses.init staticHttpCache siteMetadata config okRequests + + Err errors -> + -- TODO need to handle errors better? + StaticResponses.init staticHttpCache siteMetadata config [] + in + updateAndSendPortIfDone + config + siteMetadata + (Model + staticResponses + secrets + pageErrors + staticHttpCache + mode + [] + ) + toModel + + Err metadataParserErrors -> + updateAndSendPortIfDone + config + siteMetadata + (Model StaticResponses.error + secrets + (metadataParserErrors |> List.map Tuple.second) + staticHttpCache + mode + [] + ) + toModel + + updateAndSendPortIfDone : Config pathKey userMsg userModel metadata view -> Result (List BuildError) (List ( PagePath pathKey, metadata )) @@ -465,8 +546,7 @@ staticResponseForPage : } ) -> - Result - (List BuildError) + Result (List BuildError) (List ( PagePath pathKey , StaticHttp.Request diff --git a/src/Pages/Internal/Platform/Effect.elm b/src/Pages/Internal/Platform/Effect.elm index f8e6f6f8..818a6da3 100644 --- a/src/Pages/Internal/Platform/Effect.elm +++ b/src/Pages/Internal/Platform/Effect.elm @@ -9,3 +9,4 @@ type Effect pathKey | SendJsData (ToJsPayload pathKey) | FetchHttp { masked : RequestDetails, unmasked : RequestDetails } | Batch (List (Effect pathKey)) + | SendSinglePage { html : String } diff --git a/src/Pages/Internal/Platform/Mode.elm b/src/Pages/Internal/Platform/Mode.elm index 952b00de..2d190f55 100644 --- a/src/Pages/Internal/Platform/Mode.elm +++ b/src/Pages/Internal/Platform/Mode.elm @@ -6,6 +6,7 @@ import Json.Decode as Decode type Mode = Prod | Dev + | ElmToHtmlBeta modeDecoder = @@ -15,6 +16,9 @@ modeDecoder = if mode == "prod" then Decode.succeed Prod + else if mode == "elm-to-html-beta" then + Decode.succeed ElmToHtmlBeta + else Decode.succeed Dev ) diff --git a/src/Pages/Internal/Platform/StaticResponses.elm b/src/Pages/Internal/Platform/StaticResponses.elm index 532b5e4f..fd8957d2 100644 --- a/src/Pages/Internal/Platform/StaticResponses.elm +++ b/src/Pages/Internal/Platform/StaticResponses.elm @@ -51,8 +51,7 @@ init : -> StaticHttp.Request (List - (Result - String + (Result String { path : List String , content : String } @@ -175,6 +174,9 @@ encode requestsAndPending mode (StaticResponses staticResponses) = Mode.Prod -> StaticHttpRequest.strippedResponses ApplicationType.Cli request requestsAndPending + + Mode.ElmToHtmlBeta -> + StaticHttpRequest.strippedResponses ApplicationType.Cli request requestsAndPending ) @@ -201,8 +203,7 @@ nextStep : -> StaticHttp.Request (List - (Result - String + (Result String { path : List String , content : String } diff --git a/tests/StaticHttpRequestsTests.elm b/tests/StaticHttpRequestsTests.elm index 905e0212..84e035f4 100644 --- a/tests/StaticHttpRequestsTests.elm +++ b/tests/StaticHttpRequestsTests.elm @@ -786,8 +786,7 @@ startWithHttpCache = startLowLevel : StaticHttp.Request (List - (Result - String + (Result String { path : List String , content : String } @@ -953,6 +952,13 @@ simulateEffects effect = , tracker = Nothing } + Effect.SendSinglePage info -> + SimulatedEffect.Ports.send "toJsPort" + (Encode.object + [ ( "html", Encode.string info.html ) + ] + ) + expectErrorsPort : String -> List (ToJsPayload pathKey) -> Expect.Expectation expectErrorsPort expectedPlainString actualPorts = diff --git a/tests/__snapshots__/elm-to-html-output.test.js.snap b/tests/__snapshots__/elm-to-html-output.test.js.snap new file mode 100644 index 00000000..e0096fcc --- /dev/null +++ b/tests/__snapshots__/elm-to-html-output.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`one-page app 1`] = `"
Hello!!!!!
"`; diff --git a/tests/elm-to-html-output.test.js b/tests/elm-to-html-output.test.js new file mode 100644 index 00000000..5df054d4 --- /dev/null +++ b/tests/elm-to-html-output.test.js @@ -0,0 +1,65 @@ +const { + elmPagesCliFile, + elmPagesUiFile, +} = require("../generator/src/elm-file-constants.js"); +const generateRecords = require("../generator/src/generate-records.js"); + +test("one-page app", async () => { + process.chdir(__dirname); + const result = await doThing(); + console.log("result is", result); + expect(result).toMatchSnapshot(); +}); + +async function doThing() { + const fs = require("fs"); + const path = require("path"); + XMLHttpRequest = require("xhr2"); + + const DIR_PATH = path.join(process.cwd(), "../examples/simple/"); + const OUTPUT_FILE_NAME = "elm.js"; + + const ELM_FILE_PATH = path.join( + DIR_PATH, + "./elm-stuff/elm-pages", + OUTPUT_FILE_NAME + ); + const util = require("util"); + const exec = util.promisify(require("child_process").exec); + + const output = await exec( + "cd ../examples/simple/elm-stuff/elm-pages && elm-optimize-level-2 ../../src/Main.elm --output elm.js" + ); + console.log("shell", `${output.stdout}`); + + const elmFileContent = fs.readFileSync(ELM_FILE_PATH, "utf-8"); + fs.writeFileSync( + ELM_FILE_PATH, + elmFileContent.replace( + /return \$elm\$json\$Json\$Encode\$string\(.REPLACE_ME_WITH_JSON_STRINGIFY.\)/g, + "return x" + ) + ); + + function runElmApp() { + return new Promise((resolve, _) => { + const mode /** @type { "dev" | "prod" } */ = "elm-to-html-beta"; + const staticHttpCache = {}; + const app = require(ELM_FILE_PATH).Elm.Main.init({ + flags: { secrets: process.env, mode, staticHttpCache }, + }); + + app.ports.toJsPort.subscribe(( + /** @type { { head: SeoTag[], allRoutes: string[], html: string } } */ fromElm + ) => { + if (fromElm.html) { + console.log("@@@ fromElm", fromElm); + resolve(fromElm.html); + } else { + console.log("??? fromElm", fromElm); + } + }); + }); + } + return await runElmApp(); +}