move things around, begin work on cli

This commit is contained in:
Ryan Haskell-Glatz 2019-11-12 16:27:56 -08:00
parent 5319cb1847
commit 50b9c78fd9
76 changed files with 955 additions and 118 deletions

5
.gitignore vendored
View File

@ -1,5 +1,4 @@
.DS_Store
dist
./dist
elm-stuff/0.19.1
example/elm-stuff/0.19.1
node_modules
./node_modules

3
cli/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dist
elm-stuff
node_modules

View File

@ -1,4 +1,4 @@
# cli
# elm-spa/cli
> the thing that types the stuff
__Note:__ I will not implement this until I understand

24
cli/elm.json Normal file
View File

@ -0,0 +1,24 @@
{
"type": "application",
"source-directories": [
"src/elm"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/json": "1.1.3"
},
"indirect": {
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

403
cli/package-lock.json generated Normal file
View File

@ -0,0 +1,403 @@
{
"name": "elm-spa",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ajv": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"dev": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"elm": {
"version": "0.19.1-3",
"resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-3.tgz",
"integrity": "sha512-6y36ewCcVmTOx8lj7cKJs3bhI5qMfoVEigePZ9PhEUNKpwjjML/pU2u2YSpHVAznuCcojoF6KIsrS1Ci7GtVaQ==",
"dev": true,
"requires": {
"request": "^2.88.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"dev": true,
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"dev": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.42.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
"integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==",
"dev": true
},
"mime-types": {
"version": "2.1.25",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
"integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
"dev": true,
"requires": {
"mime-db": "1.42.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
"psl": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
"integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
},
"request": {
"version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.0",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.4.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"dev": true,
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"dev": true,
"requires": {
"psl": "^1.1.24",
"punycode": "^1.4.1"
},
"dependencies": {
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
"dev": true
}
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"dev": true
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
}
}
}

27
cli/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "elm-spa",
"version": "1.0.0",
"description": "the cli companion tool for ryannhg/elm-spa",
"main": "src/index.js",
"scripts": {
"start": "npm run build && node src/index.js",
"build": "npm run elm:compile",
"elm:compile": "elm make src/elm/Main.elm --output=dist/elm.compiled.js --optimize"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ryannhg/elm-spa.git"
},
"keywords": [
"elm",
"spa",
"cli",
"framework"
],
"author": "Ryan Haskell-Glatz",
"license": "ISC",
"bugs": {
"url": "https://github.com/ryannhg/elm-spa/issues"
},
"homepage": "https://github.com/ryannhg/elm-spa#readme"
}

106
cli/src/elm/File.elm Normal file
View File

@ -0,0 +1,106 @@
module File exposing
( File
, encode
, params
)
import Json.Encode as Json
type alias File =
{ filepath : List String
, contents : String
}
encode : File -> Json.Value
encode file =
Json.object
[ ( "filepath", Json.list Json.string file.filepath )
, ( "contents", Json.string file.contents )
]
-- PARAMS TEMPLATE
params :
{ moduleName : String
, paths : List (List String)
}
-> String
params options =
"""
module {{moduleName}} exposing (..)
{{paramsTypeAliases}}
"""
|> String.replace "{{moduleName}}"
(paramsModuleName options.moduleName)
|> String.replace "{{paramsTypeAliases}}"
(paramsTypeAliases options.paths)
|> String.trim
paramsModuleName : String -> String
paramsModuleName name =
[ "Generated", name, "Params" ]
|> List.filter (String.isEmpty >> not)
|> String.join "."
paramsTypeAliases : List (List String) -> String
paramsTypeAliases =
List.map paramsTypeAlias >> String.join "\n\n\n"
paramsTypeAlias : List String -> String
paramsTypeAlias filepath =
"""
type alias {{last}} =
{{paramsRecord}}
"""
|> String.replace "{{last}}"
(last filepath |> Maybe.withDefault "")
|> String.replace "{{paramsRecord}}"
(paramsRecord filepath |> indent 1)
|> String.trim
paramsRecord : List String -> String
paramsRecord path =
let
dynamicCount =
path
|> List.filter ((==) "Dynamic")
|> List.length
in
if dynamicCount < 1 then
"{}"
else
List.range 1 dynamicCount
|> List.map String.fromInt
|> List.map (\num -> [ "param", num, " : String" ])
|> List.map String.concat
|> String.join "\n, "
|> (\str -> "{ " ++ str ++ "\n}")
-- UTILS
last : List a -> Maybe a
last list =
List.drop (List.length list - 1) list |> List.head
indent : Int -> String -> String
indent tabs str =
str
|> String.split "\n"
|> List.map (\s -> String.concat (List.repeat tabs " " ++ [ s ]))
|> String.join "\n"

77
cli/src/elm/Main.elm Normal file
View File

@ -0,0 +1,77 @@
module Main exposing (main)
import Dict exposing (Dict)
import File exposing (File)
import Json.Encode as Json
import Ports
type alias Flags =
List Filepath
type alias Filepath =
List String
main : Program Flags () Never
main =
Platform.worker
{ init = \json -> ( (), parse json )
, update = \_ model -> ( model, Cmd.none )
, subscriptions = always Sub.none
}
-- ACTUAL CODE
parse : List Filepath -> Cmd msg
parse =
List.foldl groupByFolder Dict.empty
>> (\dict ->
[ paramsFiles dict
]
)
>> List.concat
>> Ports.sendFiles
folderOf : Filepath -> String
folderOf =
List.reverse
>> List.drop 1
>> List.reverse
>> String.join "."
groupByFolder :
Filepath
-> Dict String (List Filepath)
-> Dict String (List Filepath)
groupByFolder items =
Dict.update
(folderOf items)
(Maybe.map ((::) items)
>> Maybe.withDefault [ items ]
>> Just
)
paramsFiles :
Dict String (List Filepath)
-> List File
paramsFiles =
Dict.toList
>> List.map
(\( moduleName, paths ) ->
{ filepath =
String.split "." moduleName ++ [ "Params" ]
, contents =
File.params
{ moduleName = moduleName
, paths = paths
}
}
)

19
cli/src/elm/Ports.elm Normal file
View File

@ -0,0 +1,19 @@
port module Ports exposing (sendFiles)
import File exposing (File)
import Json.Encode as Json
port outgoing :
{ message : String
, data : Json.Value
}
-> Cmd msg
sendFiles : List File -> Cmd msg
sendFiles files =
outgoing
{ message = "sendFiles"
, data = Json.list File.encode files
}

11
cli/src/index.js Normal file
View File

@ -0,0 +1,11 @@
const path = require('path')
const cwd = process.cwd()
const { File, Elm } = require('./utils.js')
const start = () =>
File.paths(path.join(cwd, '..', 'examples', 'complex', 'src', 'Pages'))
.then(Elm.run)
.then(console.info)
.catch(console.error)
start(process.argv.slice(2))

87
cli/src/utils.js Normal file
View File

@ -0,0 +1,87 @@
const File = (_ => {
const path = require('path')
const fs = require('fs')
const mkdir = (filepath) =>
new Promise((resolve, reject) =>
fs.mkdir(filepath, { recursive: true }, (err) => err ? reject(err) : resolve(filepath))
)
const create = (filepath, contents) => {
const write = (filepath, contents) =>
new Promise((resolve, reject) =>
fs.writeFile(filepath, contents, { encoding: 'utf8' }, (err) =>
err ? reject(err) : resolve(filepath)
)
)
return fs.existsSync(folderOf(filepath))
? write(filepath, contents)
: mkdir(folderOf(filepath)).then(_ => write(filepath, contents))
}
const paths = (filepath) => {
const ls = (filepath) =>
new Promise((resolve, reject) =>
fs.readdir(filepath, (err, files) =>
err ? reject(err) : resolve(files))
)
const reduce = (fn, init) => (items) =>
items.reduce(fn, init)
const concat = (a, b) =>
a.concat(b)
const all = (fn) => (items) =>
Promise.all(items.map(fn))
const isFile = (name) =>
name.indexOf('.') > -1
const toPath = (filepath) => (name) =>
isFile(name)
? Promise.resolve([[ name.split('.')[0] ]])
: paths(path.join(filepath, name))
.then(files => files.map(file => [ name ].concat(file)))
return ls(filepath)
.then(all(toPath(filepath)))
.then(reduce(concat, []))
}
return {
paths,
mkdir,
create
}
})()
const Elm = (_ => {
const { Elm } = require('../dist/elm.compiled.js')
const handlers = {
sendFiles: (data) =>
data.forEach(a => console.log(a.filepath) || console.log(a.contents))
}
const run = paths =>
new Promise((resolve, reject) => {
const app = Elm.Main.init({ flags: paths })
app.ports.outgoing.subscribe(({ message, data }) =>
handlers[message]
? Promise.resolve(handlers[message](data)).then(resolve).catch(reject)
: reject(`Didn't recognize message "${message}" Yell at @ryannhg on the internet!\n`)
)
})
return {
run
}
})()
module.exports = {
Elm,
File
}

View File

@ -1,10 +0,0 @@
# elm-spa/example
> what a project might look like!
## running things
```
npm install
npm run dev
```

View File

@ -1,6 +0,0 @@
# elm-stuff/.elm-spa
> this is where all the generated code goes!
Normally, this whole directory should be `.gitignore`d,
but I'm keeping it around so I can better understand
what `elm-spa build` should be generating!

10
examples/README.md Normal file
View File

@ -0,0 +1,10 @@
# elm-spa/examples
> example projects using elm-spa
### how do I get started?
Check out the [intro example](./intro) to see what a project looks like.
### how does elm-spa handle XYZ?
There's a more [complex example](./complex) to answer those questions.

View File

@ -1,3 +1,4 @@
.DS_Store
dist
elm-stuff/0.19.1
node_modules

View File

@ -0,0 +1,10 @@
# elm-spa/examples/complex
> an example of using more complex features
## running things
```
npm install
npm run dev
```

View File

@ -0,0 +1,6 @@
# elm-stuff/.elm-spa
> this is where all the generated code goes!
Normally, this whole directory should __not__ be in `git`,
but I'm keeping it around for now to better understand
all the edge cases that the CLI should generate!

View File

@ -2,7 +2,7 @@
"type": "application",
"source-directories": [
"src",
"../src",
"../../src",
"elm-stuff/.elm-spa"
],
"elm-version": "0.19.1",

View File

@ -1,5 +1,5 @@
{
"name": "elm-spa-elm-ui",
"name": "complex-elm-spa-example",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,5 +1,5 @@
{
"name": "your-elm-spa",
"name": "complex-elm-spa-example",
"version": "1.0.0",
"description": "an example for elm-spa!",
"main": "src/index.js",

View File

@ -34,18 +34,23 @@ type alias Bundle msg appMsg =
App.Types.Bundle msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
layout config =
App.Page.layout
{ map = Element.map
, view = config.view
, recipe = config.recipe
}
type alias Layout params model msg appMsg =
App.Types.Layout params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
recipe config =
App.Page.recipe
{ map = Element.map
, page = config.page
, toModel = config.toModel
, toMsg = config.toMsg
}
type alias Upgrade params model msg layoutModel layoutMsg appMsg =
App.Types.Upgrade params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
layout :
Layout params model msg appMsg
-> Page params model msg layoutModel layoutMsg appMsg
layout =
App.Page.layout Element.map
recipe :
Upgrade params model msg layoutModel layoutMsg appMsg
-> Recipe params model msg layoutModel layoutMsg appMsg
recipe =
App.Page.recipe Element.map

View File

@ -2,11 +2,14 @@
"name": "elm-spa",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"main": "cli/src/index.js",
"scripts": {
"start": "npm run dev",
"example": "npm run dev",
"dev": "(cd example && npm install && npm run dev)"
"build": "(cd cli && npm run build)",
"dev": "npm run example",
"example": "npm run examples:complex",
"examples:intro": "(cd examples/intro && npm install && npm run dev)",
"examples:complex": "(cd examples/complex && npm install && npm run dev)"
},
"dependencies": {},
"devDependencies": {},

View File

@ -14,12 +14,10 @@ as the entrypoint to your app.
module Main exposing (main)
import App
import Element
import Global
import Pages
import Routes
main : App.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg
main =
App.create
{ ui = App.usingHtml
@ -140,11 +138,10 @@ create :
create config =
let
page =
Page.upgrade
Page.upgrade (always identity)
{ toModel = identity
, toMsg = identity
, page = config.page
, map = always identity
}
in
Browser.application

View File

@ -61,36 +61,11 @@ these are for!
## layout
A page that is comprimised of smaller pages, that is
able to share a common layout (maybe a something like a sidebar!)
page =
Page.layout
{ map = Html.map
, layout = Layout.view
, pages =
{ init = init
, update = update
, bundle = bundle
}
}
@docs layout
## recipe
Implementing the `init`, `update` and `bundle` functions is much easier
when you turn a `Page` type into `Recipe`.
A `Recipe` contains a record waiting for page specific data.
- `init`: just needs a `route`
- `upgrade` : just needs a `msg` and `model`
- `bundle` (`view`/`subscriptions`) : just needs a `model`
@docs recipe
@ -151,36 +126,41 @@ type alias Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui
Internals.Page.Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Turns a page and some upgrade information into a recipe,
for use in a layout's `init`, `update`, and `bundle` functions!
{-| Implementing the `init`, `update` and `bundle` functions is much easier
when you turn a `Page` type into `Recipe`.
import Utils.Spa as Spa
A `Recipe` contains a record waiting for page specific data.
recipes : Recipes msg
recipes =
{ top =
Spa.recipe
{ page = Top.page
, toModel = TopModel
, toMsg = TopMsg
}
, counter =
Spa.recipe
{ page = Counter.page
, toModel = CounterModel
, toMsg = CounterMsg
}
- `init`: just needs a `route`
-- ...
}
- `upgrade` : just needs a `msg` and `model`
- `bundle` (`view`/`subscriptions`) : just needs a `model`
import Utils.Spa as Spa
recipes : Recipes msg
recipes =
{ top =
Spa.recipe
{ page = Top.page
, toModel = TopModel
, toMsg = TopMsg
}
, counter =
Spa.recipe
{ page = Counter.page
, toModel = CounterModel
, toMsg = CounterMsg
}
-- ...
}
-}
recipe :
{ page : Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
, toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg
}
((pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg)
-> Upgrade pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
-> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
recipe =
Internals.Page.upgrade
@ -508,17 +488,10 @@ send =
-}
layout :
{ map : (pageMsg -> msg) -> ui_pageMsg -> ui_msg
, view :
{ page : ui_msg
, global : globalModel
, toMsg : globalMsg -> msg
}
-> ui_msg
, recipe : Recipe pageParams pageModel pageMsg pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
}
((pageMsg -> msg) -> ui_pageMsg -> ui_msg)
-> Layout pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
-> Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
layout options =
layout map options =
Page
(\{ toModel, toMsg } ->
{ init =
@ -539,7 +512,7 @@ layout options =
{ fromGlobalMsg = context.fromGlobalMsg
, fromPageMsg = toMsg >> context.fromPageMsg
, global = context.global
, map = options.map
, map = map
}
in
{ title = bundle.title

View File

@ -4,6 +4,7 @@ module App.Types exposing
, Init
, Update
, Bundle
, Layout, Upgrade
)
{-|
@ -48,6 +49,11 @@ doing things by hand.
@docs Bundle
# layouts and recipes
@docs Layout, Upgrade
-}
import Internals.Page as Page
@ -225,3 +231,83 @@ type alias Update layoutModel layoutMsg globalModel globalMsg =
-}
type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Page.Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-|
## creating your alias
**`src/Utils/Spa.elm`**
-- if using mdgriffith/elm-ui
import App.Types
import Element exposing (Element)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
-- if using elm/html
import App.Types
import Html exposing (Html)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg)
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
bundle : Model -> Spa.Bundle Msg msg
bundle model_ =
case model_ of
-- ...
-}
type alias Layout pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg =
Page.Layout pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
{-|
## creating your alias
**`src/Utils/Spa.elm`**
-- if using mdgriffith/elm-ui
import App.Types
import Element exposing (Element)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg)
-- if using elm/html
import App.Types
import Html exposing (Html)
type alias Bundle msg appMsg =
App.Types.Bundle msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg)
## using your alias
**`.elm-spa/Generated/Pages.elm`**
import Utils.Spa as Spa
bundle : Model -> Spa.Bundle Msg msg
bundle model_ =
case model_ of
-- ...
-}
type alias Upgrade pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Page.Upgrade pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg

View File

@ -1,15 +1,14 @@
module Internals.Page exposing
( Bundle
, Init
, Layout
, Page(..)
, Recipe
, Update
, Upgrade
, upgrade
)
{-| Page docs
-}
type Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
= Page (Page_ pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg)
@ -23,8 +22,6 @@ type alias Page_ pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg u
-> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
{-| Recipe docs
-}
type alias Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ init : pageParams -> Init layoutModel layoutMsg globalModel globalMsg
, update : pageMsg -> pageModel -> Update layoutModel layoutMsg globalModel globalMsg
@ -32,14 +29,18 @@ type alias Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMs
}
upgrade :
type alias Upgrade pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ page : Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
, toModel : pageModel -> layoutModel
, toMsg : pageMsg -> layoutMsg
, map : (pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg
}
upgrade :
((pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg)
-> Upgrade pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
-> Recipe pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
upgrade config =
upgrade map config =
let
(Page page) =
config.page
@ -47,26 +48,20 @@ upgrade config =
page
{ toModel = config.toModel
, toMsg = config.toMsg
, map = config.map
, map = map
}
{-| Init docs
-}
type alias Init layoutModel layoutMsg globalModel globalMsg =
{ global : globalModel }
-> ( layoutModel, Cmd layoutMsg, Cmd globalMsg )
{-| Update docs
-}
type alias Update layoutModel layoutMsg globalModel globalMsg =
{ global : globalModel }
-> ( layoutModel, Cmd layoutMsg, Cmd globalMsg )
{-| Bundle docs
-}
type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ global : globalModel
, fromGlobalMsg : globalMsg -> msg
@ -78,3 +73,14 @@ type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
, view : ui_msg
, subscriptions : Sub msg
}
type alias Layout pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg =
{ view :
{ page : ui_msg
, global : globalModel
, toMsg : globalMsg -> msg
}
-> ui_msg
, recipe : Recipe pageParams pageModel pageMsg pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
}