Skip to content

Commit

Permalink
Merge pull request #4 from elm-land/layouts
Browse files Browse the repository at this point in the history
Feature: Layouts
  • Loading branch information
ryan-haskell committed May 31, 2022
2 parents ebf6e8e + c537a21 commit 7369e75
Show file tree
Hide file tree
Showing 18 changed files with 553 additions and 50 deletions.
2 changes: 1 addition & 1 deletion cli/package.json
@@ -1,6 +1,6 @@
{
"name": "elm-land",
"version": "0.12.0",
"version": "0.13.0",
"description": "Reliable web apps for everyone",
"main": "index.js",
"bin": {
Expand Down
1 change: 1 addition & 0 deletions cli/src/cli.js
Expand Up @@ -11,6 +11,7 @@ let subcommandList = [
'✨ elm-land init <folder-name> ...... create a new project',
'🚀 elm-land server ................ run a local dev server',
'📄 elm-land add page <url> ................ add a new page',
'🪆 elm-land add layout <name> ........... add a new layout',
''
]

Expand Down
23 changes: 20 additions & 3 deletions cli/src/codegen.js
@@ -1,11 +1,11 @@
let generateElmLandFiles = async ({ filepaths }) => {
let generateElmLandFiles = async ({ pages, layouts }) => {
let { Elm } = require('../dist/worker.js')

let newFiles = await new Promise((resolve, reject) => {
let app = Elm.Worker.init({
flags: {
tag: 'generate',
data: { filepaths }
data: { pages, layouts }
}
})
app.ports.onComplete.subscribe(resolve)
Expand All @@ -30,9 +30,26 @@ let addNewPage = async ({ url, filepath }) => {
return newFiles
}

let addNewLayout = async ({ name }) => {
let { Elm } = require('../dist/worker.js')

let newFiles = await new Promise((resolve, reject) => {
let app = Elm.Worker.init({
flags: {
tag: 'add-layout',
data: { name }
}
})
app.ports.onComplete.subscribe(resolve)
})

return newFiles
}

module.exports = {
Codegen: {
generateElmLandFiles,
addNewPage
addNewPage,
addNewLayout
}
}
10 changes: 8 additions & 2 deletions cli/src/codegen/elm.json
Expand Up @@ -10,12 +10,18 @@
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/json": "1.1.3"
"elm/json": "1.1.3",
"stil4m/elm-syntax": "7.2.9"
},
"indirect": {
"elm/parser": "1.1.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3"
"elm/virtual-dom": "1.0.3",
"elm-community/list-extra": "8.5.2",
"miniBill/elm-unicode": "1.0.2",
"rtfeldman/elm-hex": "1.0.0",
"stil4m/structured-writer": "1.0.3"
}
},
"test-dependencies": {
Expand Down
95 changes: 95 additions & 0 deletions cli/src/codegen/src/Commands/AddLayout.elm
@@ -0,0 +1,95 @@
module Commands.AddLayout exposing (run)

import CodeGen
import CodeGen.Annotation
import CodeGen.Argument
import CodeGen.Declaration
import CodeGen.Expression
import CodeGen.Import
import CodeGen.Module
import Json.Decode


run : Json.Decode.Value -> List CodeGen.Module
run json =
case Json.Decode.decodeValue decoder json of
Ok data ->
[ newLayoutModule data ]

Err _ ->
[]



-- DATA


type alias Data =
{ name : String
}


decoder : Json.Decode.Decoder Data
decoder =
Json.Decode.map Data
(Json.Decode.field "name" Json.Decode.string)



-- CODEGEN


{-|
module Layouts.Sidebar exposing (layout)
import Html exposing (Html)
import Html.Attributes as Attr
layout : { page : Html msg } -> Html msg
layout { page } =
Html.div
[ Attr.class "layout" ]
[ page ]
-}
newLayoutModule : Data -> CodeGen.Module
newLayoutModule data =
CodeGen.Module.new
{ name = [ "Layouts", data.name ]
, exposing_ = [ "layout" ]
, imports =
[ CodeGen.Import.new [ "Html" ]
|> CodeGen.Import.withExposing [ "Html" ]
, CodeGen.Import.new [ "Html", "Attributes" ]
|> CodeGen.Import.withAlias "Attr"
]
, declarations =
[ CodeGen.Declaration.function
{ name = "layout"
, annotation =
CodeGen.Annotation.function
[ CodeGen.Annotation.record
[ ( "page", CodeGen.Annotation.type_ "Html msg" )
]
, CodeGen.Annotation.type_ "Html msg"
]
, arguments = [ CodeGen.Argument.new "{ page }" ]
, expression =
CodeGen.Expression.multilineFunction
{ name = "Html.div"
, arguments =
[ CodeGen.Expression.list
[ CodeGen.Expression.function
{ name = "Attr.class"
, arguments = [ CodeGen.Expression.string "layout" ]
}
]
, CodeGen.Expression.list
[ CodeGen.Expression.value "page"
]
]
}
}
]
}
108 changes: 88 additions & 20 deletions cli/src/codegen/src/Commands/Generate.elm
Expand Up @@ -9,16 +9,24 @@ import CodeGen.Import
import CodeGen.Module
import Filepath exposing (Filepath)
import Json.Decode
import PageFile exposing (PageFile)


run : Json.Decode.Value -> List CodeGen.Module
run json =
case Json.Decode.decodeValue decoder json of
Ok data ->
[ mainElmModule data
, routeElmModule data
, notFoundModule
]
List.concat
[ [ mainElmModule data
, routeElmModule data
, notFoundModule
]
, if List.isEmpty data.layouts then
[]

else
[ elmLandLayoutsElmModule data ]
]

Err _ ->
[]
Expand All @@ -37,7 +45,12 @@ mainElmModule data =
|> CodeGen.Import.withExposing [ "Html" ]
, CodeGen.Import.new [ "Json", "Decode" ]
]
, data.filepaths
, data.layouts
|> List.map Filepath.toList
|> List.map (\pieces -> "Layouts" :: pieces)
|> List.map CodeGen.Import.new
, data.pages
|> List.map PageFile.toFilepath
|> List.map Filepath.toList
|> List.map (\pieces -> "Pages" :: pieces)
|> List.map CodeGen.Import.new
Expand Down Expand Up @@ -208,29 +221,54 @@ mainElmModule data =
}
, let
toViewBranch :
Filepath
PageFile
->
{ name : String
, arguments : List CodeGen.Argument.Argument
, expression : CodeGen.Expression.Expression
}
toViewBranch filepath =
toViewBranch pageFile =
let
filepath : Filepath
filepath =
PageFile.toFilepath pageFile

conditionallyWrapInLayout : CodeGen.Expression -> CodeGen.Expression
conditionallyWrapInLayout pageExpression =
case PageFile.toLayoutName pageFile of
Just layoutName ->
CodeGen.Expression.multilineFunction
{ name = "Layouts." ++ layoutName ++ ".layout"
, arguments =
[ CodeGen.Expression.multilineRecord
[ ( "page", pageExpression )
]
]
}

Nothing ->
pageExpression
in
if Filepath.hasDynamicParameters filepath then
{ name = "Route." ++ Filepath.toRouteVariantName filepath
, arguments = [ CodeGen.Argument.new "params" ]
, expression =
CodeGen.Expression.function
{ name = Filepath.toPageModuleName filepath ++ ".page"
, arguments =
[ CodeGen.Expression.value "params"
]
}
conditionallyWrapInLayout
(CodeGen.Expression.function
{ name = Filepath.toPageModuleName filepath ++ ".page"
, arguments =
[ CodeGen.Expression.value "params"
]
}
)
}

else
{ name = "Route." ++ Filepath.toRouteVariantName filepath
, arguments = []
, expression = CodeGen.Expression.value (Filepath.toPageModuleName filepath ++ ".page")
, expression =
conditionallyWrapInLayout
(CodeGen.Expression.value (Filepath.toPageModuleName filepath ++ ".page"))
}
in
CodeGen.Declaration.function
Expand All @@ -246,7 +284,7 @@ mainElmModule data =
{ value = CodeGen.Argument.new "Route.fromUrl model.url"
, branches =
List.concat
[ data.filepaths
[ data.pages
|> List.map toViewBranch
, [ { name = "Route.NotFound_"
, arguments = []
Expand Down Expand Up @@ -276,7 +314,8 @@ routeElmModule data =
{ name = "Route"
, variants =
List.concat
[ data.filepaths
[ data.pages
|> List.map PageFile.toFilepath
|> List.map Filepath.toRouteVariant
, [ ( "NotFound_", [] ) ]
]
Expand Down Expand Up @@ -314,7 +353,8 @@ routeElmModule data =
CodeGen.Expression.multilineFunction
{ name = "Url.Parser.oneOf"
, arguments =
[ data.filepaths
[ data.pages
|> List.map PageFile.toFilepath
|> List.map Filepath.toUrlParser
|> CodeGen.Expression.multilineList
]
Expand All @@ -324,6 +364,32 @@ routeElmModule data =
}


{-|
module ElmLand.Layout exposing (Layout(..))
type Layout
= Default
| Sidebar
-}
elmLandLayoutsElmModule : Data -> CodeGen.Module
elmLandLayoutsElmModule data =
CodeGen.Module.new
{ name = [ "ElmLand", "Layout" ]
, exposing_ = [ "Layout(..)" ]
, imports = []
, declarations =
[ CodeGen.Declaration.customType
{ name = "Layout"
, variants =
data.layouts
|> List.map Filepath.toRouteVariant
}
]
}


notFoundModule : CodeGen.Module
notFoundModule =
CodeGen.Module.new
Expand All @@ -349,11 +415,13 @@ notFoundModule =


type alias Data =
{ filepaths : List Filepath
{ pages : List PageFile
, layouts : List Filepath
}


decoder : Json.Decode.Decoder Data
decoder =
Json.Decode.map Data
(Json.Decode.field "filepaths" (Json.Decode.list Filepath.decoder))
Json.Decode.map2 Data
(Json.Decode.field "pages" (Json.Decode.list PageFile.decoder))
(Json.Decode.field "layouts" (Json.Decode.list Filepath.decoder))

0 comments on commit 7369e75

Please sign in to comment.