diff --git a/.gitignore b/.gitignore index 84d1fa7..c32621b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.DS_Store -./dist elm-stuff -node_modules \ No newline at end of file +node_modules +dist +Generated \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c9419ee..0000000 --- a/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2019-present, Ryan Haskell-Glatz - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Ryan Haskell-Glatz nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index 8e992e4..e3a232a 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,20 @@ # elm-spa single page apps made easy -## deprecated +### try it out -this is a deprecated version of elm-spa, so please check out the latest API -docs at [elm-spa.dev](https://elm-spa.dev) +``` +npx elm-spa init new-project +``` -(the old docs site is available at [v3.elm-spa.dev](https://v3.elm-spa.dev)) +### or just install the elm package -### what is elm-spa? +``` +elm install ryannhg/elm-spa +``` -__elm-spa__ is a framework for building web apps in Elm. It's not a "framework" like React or VueJS, it's more of a framework like NextJS, Rails, or .NET MVC. +### or run the example -It provides simple tooling and a convention for creating single page apps, so you can focus on the cool thing you want to build! - -__Learn more at [elm-spa.dev](https://v3.elm-spa.dev)__ - -### looking for the website's code? - -the website project is available over here: -https://github.com/ryannhg/elm-spa-dev - -(that's the one we build together at [elm-spa.dev/guide](https://v3.elm-spa.dev/guide)) +``` +cd example && npm start +``` \ No newline at end of file diff --git a/cli/.gitignore b/cli/.gitignore deleted file mode 100644 index 2111ae7..0000000 --- a/cli/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -elm-stuff -node_modules \ No newline at end of file diff --git a/cli/.npmignore b/cli/.npmignore index 19c7c51..762d69b 100644 --- a/cli/.npmignore +++ b/cli/.npmignore @@ -1,5 +1,5 @@ elm-stuff -src/elm -tests +/src +/tests /elm.json !.gitignore \ No newline at end of file diff --git a/cli/README.md b/cli/README.md deleted file mode 100644 index fb03839..0000000 --- a/cli/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# elm-spa/cli -> the thing that types the stuff - diff --git a/cli/elm.json b/cli/elm.json index 9a2d7b7..7468f5e 100644 --- a/cli/elm.json +++ b/cli/elm.json @@ -1,18 +1,17 @@ { "type": "application", "source-directories": [ - "src/elm" + "src" ], "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", - "elm-community/list-extra": "8.2.2" + "elm/core": "1.0.5", + "elm/html": "1.0.0" }, "indirect": { + "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.2" diff --git a/cli/index.js b/cli/index.js new file mode 100755 index 0000000..e52ad84 --- /dev/null +++ b/cli/index.js @@ -0,0 +1,184 @@ +#!/usr/bin/env node +const path = require('path') +const fs = require('fs') +const { Elm } = require('./dist/elm.worker.js') +const package = require('./package.json') + +// File stuff +const folders = { + src: (dir) => path.join(process.cwd(), dir, 'src'), + pages: (dir) => path.join(process.cwd(), dir, 'src', 'Pages'), + generated: (dir) => path.join(process.cwd(), dir, 'src', 'Generated') +} + +const rejectIfMissing = (dir) => new Promise((resolve, reject) => + fs.existsSync(dir) ? resolve(true) : reject(false) +) + +const cp = (src, dest) => { + const exists = fs.existsSync(src) + const stats = exists && fs.statSync(src) + if (stats && stats.isDirectory()) { + fs.mkdirSync(dest) + fs.readdirSync(src).forEach(child => + cp(path.join(src, child), path.join(dest, child)) + ) + } else { + fs.copyFileSync(src, dest) + } +} + +const listFiles = (dir) => + fs.readdirSync(dir) + .reduce((files, file) => + fs.statSync(path.join(dir, file)).isDirectory() ? + files.concat(listFiles(path.join(dir, file))) : + files.concat(path.join(dir, file)), + []) + +const ensureDirectory = (dir) => + fs.mkdirSync(dir, { recursive: true }) + +const saveToFolder = (prefix) => ({ filepath, content }) => + fs.writeFileSync(path.join(prefix, filepath), content, { encoding: 'utf8' }) + +// Formatting output +const bold = (str) => '\033[1m' + str + '\033[0m' +const toFilepath = name => path.join(folders.pages('.'), `${name.split('.').join('/')}.elm`) + +// Flags + Validation +const flags = { command: '', name: '', pageType: '', filepaths: [] } + +const isValidPageType = type => + [ 'static', 'sandbox', 'element', 'component' ].some(x => x === type) + +const isValidModuleName = (name = '') => { + const isAlphaOnly = word => word.match(/[A-Z|a-z]+/)[0] === word + const isCapitalized = word => word[0].toUpperCase() === word[0] + return name && + name.length && + name.split('.').every(word => isAlphaOnly(word) && isCapitalized(word)) +} + +// Help commands +const help = { + general: ` + ${bold('elm-spa')} – version ${package.version} + + ${bold('elm-spa init')} – create a new project + ${bold('elm-spa add')} – add a new page + ${bold('elm-spa build')} – generate files + + ${bold('elm-spa help')} – get detailed help for a command + ${bold('elm-spa -v')} – print version number +`, + + init: ` + ${bold('elm-spa init')} + + Create a new elm-spa app in the + folder specified. + + ${bold('examples:')} + elm-spa init . + elm-spa init my-app +`, + + add: ` + ${bold('elm-spa add')} + + Create a new page of type + with the module name . + + ${bold('examples:')} + elm-spa add static Top + elm-spa add sandbox Posts.Top + elm-spa add element Posts.Dynamic + elm-spa add component SignIn +`, + + build: ` + ${bold('elm-spa build')} [dir] + + Generate "Generated.Route" and "Generated.Pages" for + this project, based on the files in src/Pages/* + + Optionally, you can specify a different directory. + + ${bold('examples:')} + elm-spa build + elm-spa build ../some/other-folder + elm-spa build ./help +` + +} + +// Available commands +const commands = { + + 'init': ([ folder ]) => + folder && folder !== 'help' + ? Promise.resolve() + .then(_ => cp(path.join(__dirname, 'projects', 'new'), path.join(process.cwd(), folder))) + .then(_ => `\ncreated a new project in ${path.join(process.cwd(), folder)}\n`) + .catch(_ => `\nUnable to initialize a project at ${path.join(process.cwd(), folder)}\n`) + : Promise.resolve(help.init), + + 'add': ([ type, name ]) => + (type && name) && type !== 'help' && isValidPageType(type) && isValidModuleName(name) + ? rejectIfMissing(folders.pages('.')) + .then(_ => new Promise( + Elm.Main.init({ flags: { ...flags, command: 'add', name: name, pageType: type } }).ports.addPort.subscribe) + ) + .then(file => { + const containingFolder = path.join(folders.pages('.'), file.filepath.split('/').slice(0, -1).join('/')) + ensureDirectory(containingFolder) + saveToFolder((folders.pages('.')))(file) + }) + .then(_ => `\nadded a new ${bold(type)} page at:\n${toFilepath(name)}\n`) + .catch(_ => `\nplease run ${bold('elm-spa add')} in the folder with ${bold('elm.json')}\n`) + : Promise.resolve(help.add), + + 'build': ([ dir = '.' ] = []) => + dir !== 'help' + ? Promise.resolve(folders.pages(dir)) + .then(listFiles) + .then(names => names.filter(name => name.endsWith('.elm'))) + .then(names => names.map(name => name.substring(folders.pages(dir).length))) + .then(filepaths => new Promise( + Elm.Main.init({ flags: { ...flags, command: 'build', filepaths } }).ports.buildPort.subscribe + )) + .then(files => { + ensureDirectory(folders.generated(dir)) + files.forEach(saveToFolder(folders.src(dir))) + return files + }) + .then(files => `\nelm-spa generated two files:\n${files.map(({ filepath }) => ' - ' + path.join(folders.src(dir), filepath)).join('\n')}\n`) + .catch(_ => `\nplease run ${bold('elm-spa build')} in the folder with ${bold('elm.json')}\n`) + : Promise.resolve(help.build), + + '-v': _ => Promise.resolve(package.version), + + 'help': _ => Promise.resolve(help.general) + +} + +const main = ([ command, ...args ] = []) => + (commands[command] || commands['help'])(args) + // .then(_ => args.data.slice) + .then(console.info) + .catch(reason => { + console.info(`\n${bold('Congratulations!')} - you've found a bug! + + If you'd like, open an issue here with the following output: + https://github.com/ryannhg/elm-spa/issues/new?labels=cli + + +${bold(`### terminal output`)} +`) +console.log('```') + console.error(reason) + console.log('```\n') + }) + +main([...process.argv.slice(2)]) diff --git a/cli/initial-projects/elm-ui/README.md b/cli/initial-projects/elm-ui/README.md deleted file mode 100644 index 68cd2f4..0000000 --- a/cli/initial-projects/elm-ui/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# your elm-spa -> learn more at [https://elm-spa.dev](https://elm-spa.dev) - -### local development - -``` -npm run dev -``` - -## folder structure - -```elm -README.md -- this file you're reading 👀 -elm.json -- has project dependencies -src/ - Main.elm -- the entrypoint to the app - Global.elm -- share state across pages - Transitions.elm -- smoothly animate between pages - Ports.elm -- communicate with JS - Pages/ -- where all your pages go - Layouts/ -- reusable views around pages - Components/ -- views shared across the site - Utils/ -- a place for helper functions -``` \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/elm-spa.json b/cli/initial-projects/elm-ui/elm-spa.json deleted file mode 100644 index 2d70f02..0000000 --- a/cli/initial-projects/elm-ui/elm-spa.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ui": "Element" -} \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/elm.json b/cli/initial-projects/elm-ui/elm.json deleted file mode 100644 index 327008b..0000000 --- a/cli/initial-projects/elm-ui/elm.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "application", - "source-directories": [ - "src", - "elm-stuff/.elm-spa" - ], - "elm-version": "0.19.1", - "dependencies": { - "direct": { - "elm/core": "1.0.4", - "elm/html": "1.0.0", - "elm/json": "1.1.3", - "elm/url": "1.0.0", - "mdgriffith/elm-ui": "1.1.5", - "ryannhg/elm-spa": "3.0.0" - }, - "indirect": { - "elm/browser": "1.0.2", - "elm/time": "1.0.0", - "elm/virtual-dom": "1.0.2" - } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - } -} diff --git a/cli/initial-projects/elm-ui/netlify.toml b/cli/initial-projects/elm-ui/netlify.toml deleted file mode 100644 index cab849f..0000000 --- a/cli/initial-projects/elm-ui/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -# sends all routes to /index.html -# (so you can handle 404s there!) -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/package.json b/cli/initial-projects/elm-ui/package.json deleted file mode 100644 index 2bb056c..0000000 --- a/cli/initial-projects/elm-ui/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "my-elm-spa-project", - "version": "1.0.0", - "description": "learn more at https://elm-spa.dev", - "scripts": { - "start": "npm install && npm run dev", - "dev": "npm run elm:spa:build && npm run build:watch", - "build": "npm run elm:spa:build && npm run elm:compile", - "build:watch": "concurrently --raw --kill-others \"npm run elm:spa:watch\" \"npm run elm:live\"", - "elm:compile": "elm make src/Main.elm --output=public/dist/elm.compiled.js --optimize", - "elm:live": "elm-live src/Main.elm --dir=public --start-page=index.html --open --pushstate --port=1234 -- --output=public/dist/elm.compiled.js --debug", - "elm:spa:build": "elm-spa build .", - "elm:spa:watch": "chokidar src/Pages -c \"npm run elm:spa:build\"" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": {}, - "devDependencies": { - "chokidar-cli": "2.1.0", - "concurrently": "5.0.0", - "elm": "0.19.1-3", - "elm-live": "4.0.1", - "elm-spa": "3.0.3" - } -} diff --git a/cli/initial-projects/elm-ui/public/index.html b/cli/initial-projects/elm-ui/public/index.html deleted file mode 100644 index 9a0d0cf..0000000 --- a/cli/initial-projects/elm-ui/public/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - our-elm-spa - - - - - - - - diff --git a/cli/initial-projects/elm-ui/public/ports.js b/cli/initial-projects/elm-ui/public/ports.js deleted file mode 100644 index 89c96ae..0000000 --- a/cli/initial-projects/elm-ui/public/ports.js +++ /dev/null @@ -1,17 +0,0 @@ -// On load, listen to Elm! -window.addEventListener('load', _ => { - window.ports = { - init: (app) => - app.ports.outgoing.subscribe(({ action, data }) => - actions[action] - ? actions[action](data) - : console.warn(`I didn't recognize action "${action}".`) - ) - } -}) - -// maps actions to functions! -const actions = { - 'LOG': (message) => - console.log(`From Elm:`, message) -} diff --git a/cli/initial-projects/elm-ui/public/styles.css b/cli/initial-projects/elm-ui/public/styles.css deleted file mode 100644 index 6d6ac7b..0000000 --- a/cli/initial-projects/elm-ui/public/styles.css +++ /dev/null @@ -1,5 +0,0 @@ -/* you can include CSS here */ -html, body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - height: 100%; -} \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/src/Components/README.md b/cli/initial-projects/elm-ui/src/Components/README.md deleted file mode 100644 index edac699..0000000 --- a/cli/initial-projects/elm-ui/src/Components/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Components -> views shared across the site \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/src/Global.elm b/cli/initial-projects/elm-ui/src/Global.elm deleted file mode 100644 index c5fd981..0000000 --- a/cli/initial-projects/elm-ui/src/Global.elm +++ /dev/null @@ -1,49 +0,0 @@ -module Global exposing - ( Flags - , Model - , Msg(..) - , init - , subscriptions - , update - ) - -import Generated.Routes as Routes exposing (Route) -import Ports - - -type alias Flags = - () - - -type alias Model = - {} - - -type Msg - = Msg - - -type alias Commands msg = - { navigate : Route -> Cmd msg - } - - -init : Commands msg -> Flags -> ( Model, Cmd Msg, Cmd msg ) -init _ _ = - ( {} - , Cmd.none - , Ports.log "Hello!" - ) - - -update : Commands msg -> Msg -> Model -> ( Model, Cmd Msg, Cmd msg ) -update _ _ model = - ( model - , Cmd.none - , Cmd.none - ) - - -subscriptions : Model -> Sub Msg -subscriptions _ = - Sub.none diff --git a/cli/initial-projects/elm-ui/src/Layout.elm b/cli/initial-projects/elm-ui/src/Layout.elm deleted file mode 100644 index 7130e60..0000000 --- a/cli/initial-projects/elm-ui/src/Layout.elm +++ /dev/null @@ -1,50 +0,0 @@ -module Layout exposing (view) - -import Element exposing (..) -import Element.Font as Font -import Generated.Routes as Routes exposing (Route, routes) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, route } = - column [ height fill, width fill ] - [ viewHeader route - , page - ] - - -viewHeader : Route -> Element msg -viewHeader currentRoute = - row - [ spacing 24 - , paddingEach { top = 32, left = 16, right = 16, bottom = 0 } - , centerX - , width (fill |> maximum 480) - ] - [ viewLink currentRoute ( "home", routes.top ) - , viewLink currentRoute ( "nowhere", routes.notFound ) - ] - - -viewLink : Route -> ( String, Route ) -> Element msg -viewLink currentRoute ( label, route ) = - if currentRoute == route then - el - [ Font.underline - , Font.color (rgb255 204 75 75) - , alpha 0.5 - , Font.size 16 - ] - (text label) - - else - link - [ Font.underline - , Font.color (rgb255 204 75 75) - , Font.size 16 - , mouseOver [ alpha 0.5 ] - ] - { label = text label - , url = Routes.toPath route - } diff --git a/cli/initial-projects/elm-ui/src/Layouts/README.md b/cli/initial-projects/elm-ui/src/Layouts/README.md deleted file mode 100644 index b8e461e..0000000 --- a/cli/initial-projects/elm-ui/src/Layouts/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Layouts -> where all your pages go \ No newline at end of file diff --git a/cli/initial-projects/elm-ui/src/Main.elm b/cli/initial-projects/elm-ui/src/Main.elm deleted file mode 100644 index e77ed03..0000000 --- a/cli/initial-projects/elm-ui/src/Main.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Main exposing (main) - -import Generated.Pages as Pages -import Generated.Routes as Routes exposing (routes) -import Global -import Spa -import Transitions - - -main : Spa.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg -main = - Spa.create - { ui = Spa.usingElmUi - , transitions = Transitions.transitions - , routing = - { routes = Routes.parsers - , toPath = Routes.toPath - , notFound = routes.notFound - , afterNavigate = Nothing - } - , global = - { init = Global.init - , update = Global.update - , subscriptions = Global.subscriptions - } - , page = Pages.page - } diff --git a/cli/initial-projects/elm-ui/src/Pages/NotFound.elm b/cli/initial-projects/elm-ui/src/Pages/NotFound.elm deleted file mode 100644 index e0aa2af..0000000 --- a/cli/initial-projects/elm-ui/src/Pages/NotFound.elm +++ /dev/null @@ -1,39 +0,0 @@ -module Pages.NotFound exposing (Model, Msg, page) - -import Element exposing (..) -import Element.Font as Font -import Generated.Params as Params -import Generated.Routes as Routes exposing (routes) -import Spa.Page -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.NotFound Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "not found | elm-spa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ centerX, centerY, spacing 16 ] - [ el [ Font.size 32, Font.semiBold ] (text "404 is life.") - , link [ Font.size 16, Font.underline, centerX, Font.color (rgb255 204 75 75), mouseOver [ alpha 0.5 ] ] - { label = text "back home?" - , url = Routes.toPath routes.top - } - ] diff --git a/cli/initial-projects/elm-ui/src/Pages/README.md b/cli/initial-projects/elm-ui/src/Pages/README.md deleted file mode 100644 index fb9be14..0000000 --- a/cli/initial-projects/elm-ui/src/Pages/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# src/Pages -> where all your pages go - diff --git a/cli/initial-projects/elm-ui/src/Pages/Top.elm b/cli/initial-projects/elm-ui/src/Pages/Top.elm deleted file mode 100644 index 48a631b..0000000 --- a/cli/initial-projects/elm-ui/src/Pages/Top.elm +++ /dev/null @@ -1,51 +0,0 @@ -module Pages.Top exposing (Model, Msg, page) - -import Element exposing (..) -import Element.Font as Font -import Generated.Params as Params -import Spa.Page -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "homepage" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column - [ centerX - , centerY - , spacing 24 - ] - [ column [ spacing 14 ] - [ el [ centerX, Font.size 48, Font.semiBold ] (text "elm-spa") - , el [ alpha 0.5 ] (text "(you're doing great already!)") - ] - , newTabLink - [ Font.underline - , centerX - , Font.color (rgb255 204 75 75) - , mouseOver [ alpha 0.5 ] - , Font.size 16 - ] - { label = text "learn more" - , url = "https://elm-spa.dev" - } - ] diff --git a/cli/initial-projects/elm-ui/src/Ports.elm b/cli/initial-projects/elm-ui/src/Ports.elm deleted file mode 100644 index 094a416..0000000 --- a/cli/initial-projects/elm-ui/src/Ports.elm +++ /dev/null @@ -1,14 +0,0 @@ -port module Ports exposing (log) - -import Json.Encode as Json - - -port outgoing : { action : String, data : Json.Value } -> Cmd msg - - -log : String -> Cmd msg -log message = - outgoing - { action = "LOG" - , data = Json.string message - } diff --git a/cli/initial-projects/elm-ui/src/Transitions.elm b/cli/initial-projects/elm-ui/src/Transitions.elm deleted file mode 100644 index 292708b..0000000 --- a/cli/initial-projects/elm-ui/src/Transitions.elm +++ /dev/null @@ -1,12 +0,0 @@ -module Transitions exposing (transitions) - -import Spa.Transition as Transition -import Utils.Spa as Spa - - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeElmUi 500 - , page = Transition.fadeElmUi 300 - , pages = [] - } diff --git a/cli/initial-projects/elm-ui/src/Utils/README.md b/cli/initial-projects/elm-ui/src/Utils/README.md deleted file mode 100644 index 05c12de..0000000 --- a/cli/initial-projects/elm-ui/src/Utils/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Utils -> a place for helper functions diff --git a/cli/initial-projects/elm-ui/src/Utils/Spa.elm b/cli/initial-projects/elm-ui/src/Utils/Spa.elm deleted file mode 100644 index ef4747d..0000000 --- a/cli/initial-projects/elm-ui/src/Utils/Spa.elm +++ /dev/null @@ -1,72 +0,0 @@ -module Utils.Spa exposing - ( Bundle - , Init - , LayoutContext - , Page - , PageContext - , Recipe - , Transitions - , Update - , layout - , recipe - ) - -import Element exposing (Element) -import Generated.Routes as Routes exposing (Route) -import Global -import Spa.Page -import Spa.Types - - -type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Init model msg = - Spa.Types.Init Route model msg Global.Model Global.Msg - - -type alias Update model msg = - Spa.Types.Update Route model msg Global.Model Global.Msg - - -type alias Bundle msg appMsg = - Spa.Types.Bundle Route msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias LayoutContext msg = - Spa.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg - - -type alias PageContext = - Spa.Types.PageContext Route Global.Model - - -type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -layout : - Layout params model msg appMsg - -> Page params model msg layoutModel layoutMsg appMsg -layout = - Spa.Page.layout Element.map - - -type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -recipe : - Upgrade params model msg layoutModel layoutMsg appMsg - -> Recipe params model msg layoutModel layoutMsg appMsg -recipe = - Spa.Page.recipe Element.map - - -type alias Transitions msg = - Spa.Types.Transitions (Element msg) diff --git a/cli/initial-projects/html/.gitignore b/cli/initial-projects/html/.gitignore deleted file mode 100644 index d8f6743..0000000 --- a/cli/initial-projects/html/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -dist -elm-stuff -node_modules \ No newline at end of file diff --git a/cli/initial-projects/html/README.md b/cli/initial-projects/html/README.md deleted file mode 100644 index 68cd2f4..0000000 --- a/cli/initial-projects/html/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# your elm-spa -> learn more at [https://elm-spa.dev](https://elm-spa.dev) - -### local development - -``` -npm run dev -``` - -## folder structure - -```elm -README.md -- this file you're reading 👀 -elm.json -- has project dependencies -src/ - Main.elm -- the entrypoint to the app - Global.elm -- share state across pages - Transitions.elm -- smoothly animate between pages - Ports.elm -- communicate with JS - Pages/ -- where all your pages go - Layouts/ -- reusable views around pages - Components/ -- views shared across the site - Utils/ -- a place for helper functions -``` \ No newline at end of file diff --git a/cli/initial-projects/html/elm-spa.json b/cli/initial-projects/html/elm-spa.json deleted file mode 100644 index 2f3a660..0000000 --- a/cli/initial-projects/html/elm-spa.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ui": "Html" -} \ No newline at end of file diff --git a/cli/initial-projects/html/netlify.toml b/cli/initial-projects/html/netlify.toml deleted file mode 100644 index cab849f..0000000 --- a/cli/initial-projects/html/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -# sends all routes to /index.html -# (so you can handle 404s there!) -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file diff --git a/cli/initial-projects/html/package-lock.json b/cli/initial-projects/html/package-lock.json deleted file mode 100644 index 7d862df..0000000 --- a/cli/initial-projects/html/package-lock.json +++ /dev/null @@ -1,1451 +0,0 @@ -{ - "name": "my-elm-spa-project", - "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" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "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 - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "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" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true - }, - "chokidar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", - "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", - "dev": true, - "requires": { - "anymatch": "^3.0.1", - "braces": "^3.0.2", - "fsevents": "^2.0.6", - "glob-parent": "^5.0.0", - "is-binary-path": "^2.1.0", - "is-glob": "^4.0.1", - "normalize-path": "^3.0.0", - "readdirp": "^3.1.1" - } - }, - "chokidar-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-2.1.0.tgz", - "integrity": "sha512-6n21AVpW6ywuEPoxJcLXMA2p4T+SLjWsXKny/9yTWFz0kKxESI3eUylpeV97LylING/27T/RVTY0f2/0QaWq9Q==", - "dev": true, - "requires": { - "chokidar": "^3.2.3", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "yargs": "^13.3.0" - }, - "dependencies": { - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "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" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "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 - }, - "crocks": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/crocks/-/crocks-0.12.1.tgz", - "integrity": "sha512-2qCRJwBmPlRQXzd50k9gt9PaItultOP8lj/cKSH2Eai9aeBuNqAnDuyolAm9TGn6Pw/4BgbxtPJLU1S+tQ4WMQ==", - "dev": true - }, - "cross-spawn": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.0.1.tgz", - "integrity": "sha1-o7uzAtsil8vqPATt82lB9GE6o5k=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "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" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.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 - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "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" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "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" - } - }, - "elm-hot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.1.tgz", - "integrity": "sha512-ZHjoHd2Ev6riNXNQirj3J+GKKXXwedAUikfFBYzlVL/+3CdGs96cpZ7nhAk4c5l//Qa9ymltrqX36mOlr0pPFA==", - "dev": true - }, - "elm-live": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-4.0.1.tgz", - "integrity": "sha512-IlonaC1pO/QoXlOrwwrJaxyvpJAT8QDSfzenkChbhU1PC1fJetkj2TwZfki+y1ZxpSMTnMSomMraOdWA6DO3VQ==", - "dev": true, - "requires": { - "chalk": "^1.1.1", - "chokidar": "3.0.2", - "commander": "2.17.1", - "crocks": "0.12.1", - "cross-spawn": "5.0.1", - "elm-hot": "1.1.1", - "finalhandler": "1.1.2", - "http-proxy": "1.17.0", - "internal-ip": "4.3.0", - "mime": "2.4.3", - "open": "6.4.0", - "pem": "1.14.2", - "serve-static": "1.14.1", - "ws": "7.1.1" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "es6-promisify": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz", - "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "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 - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", - "dev": true, - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "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" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "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" - } - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "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" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.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" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "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" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "dev": true, - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", - "dev": true - }, - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.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 - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "pem": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.2.tgz", - "integrity": "sha512-TOnPtq3ZFnCniOZ+rka4pk8UIze9xG1qI+wNE7EmkiR/cg+53uVvk5QbkWZ7M6RsuOxzz62FW1hlAobJr/lTOA==", - "dev": true, - "requires": { - "es6-promisify": "^6.0.0", - "md5": "^2.2.1", - "os-tmpdir": "^1.0.1", - "which": "^1.3.1" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", - "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "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 - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "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" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "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 - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "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" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "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 - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "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" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.1.tgz", - "integrity": "sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==", - "dev": true, - "requires": { - "async-limiter": "^1.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/cli/initial-projects/html/package.json b/cli/initial-projects/html/package.json deleted file mode 100644 index 2bb056c..0000000 --- a/cli/initial-projects/html/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "my-elm-spa-project", - "version": "1.0.0", - "description": "learn more at https://elm-spa.dev", - "scripts": { - "start": "npm install && npm run dev", - "dev": "npm run elm:spa:build && npm run build:watch", - "build": "npm run elm:spa:build && npm run elm:compile", - "build:watch": "concurrently --raw --kill-others \"npm run elm:spa:watch\" \"npm run elm:live\"", - "elm:compile": "elm make src/Main.elm --output=public/dist/elm.compiled.js --optimize", - "elm:live": "elm-live src/Main.elm --dir=public --start-page=index.html --open --pushstate --port=1234 -- --output=public/dist/elm.compiled.js --debug", - "elm:spa:build": "elm-spa build .", - "elm:spa:watch": "chokidar src/Pages -c \"npm run elm:spa:build\"" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": {}, - "devDependencies": { - "chokidar-cli": "2.1.0", - "concurrently": "5.0.0", - "elm": "0.19.1-3", - "elm-live": "4.0.1", - "elm-spa": "3.0.3" - } -} diff --git a/cli/initial-projects/html/public/index.html b/cli/initial-projects/html/public/index.html deleted file mode 100644 index 9a0d0cf..0000000 --- a/cli/initial-projects/html/public/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - our-elm-spa - - - - - - - - diff --git a/cli/initial-projects/html/public/ports.js b/cli/initial-projects/html/public/ports.js deleted file mode 100644 index 89c96ae..0000000 --- a/cli/initial-projects/html/public/ports.js +++ /dev/null @@ -1,17 +0,0 @@ -// On load, listen to Elm! -window.addEventListener('load', _ => { - window.ports = { - init: (app) => - app.ports.outgoing.subscribe(({ action, data }) => - actions[action] - ? actions[action](data) - : console.warn(`I didn't recognize action "${action}".`) - ) - } -}) - -// maps actions to functions! -const actions = { - 'LOG': (message) => - console.log(`From Elm:`, message) -} diff --git a/cli/initial-projects/html/public/styles.css b/cli/initial-projects/html/public/styles.css deleted file mode 100644 index 6d6ac7b..0000000 --- a/cli/initial-projects/html/public/styles.css +++ /dev/null @@ -1,5 +0,0 @@ -/* you can include CSS here */ -html, body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - height: 100%; -} \ No newline at end of file diff --git a/cli/initial-projects/html/src/Components/README.md b/cli/initial-projects/html/src/Components/README.md deleted file mode 100644 index edac699..0000000 --- a/cli/initial-projects/html/src/Components/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Components -> views shared across the site \ No newline at end of file diff --git a/cli/initial-projects/html/src/Global.elm b/cli/initial-projects/html/src/Global.elm deleted file mode 100644 index c5fd981..0000000 --- a/cli/initial-projects/html/src/Global.elm +++ /dev/null @@ -1,49 +0,0 @@ -module Global exposing - ( Flags - , Model - , Msg(..) - , init - , subscriptions - , update - ) - -import Generated.Routes as Routes exposing (Route) -import Ports - - -type alias Flags = - () - - -type alias Model = - {} - - -type Msg - = Msg - - -type alias Commands msg = - { navigate : Route -> Cmd msg - } - - -init : Commands msg -> Flags -> ( Model, Cmd Msg, Cmd msg ) -init _ _ = - ( {} - , Cmd.none - , Ports.log "Hello!" - ) - - -update : Commands msg -> Msg -> Model -> ( Model, Cmd Msg, Cmd msg ) -update _ _ model = - ( model - , Cmd.none - , Cmd.none - ) - - -subscriptions : Model -> Sub Msg -subscriptions _ = - Sub.none diff --git a/cli/initial-projects/html/src/Layout.elm b/cli/initial-projects/html/src/Layout.elm deleted file mode 100644 index 52e76d7..0000000 --- a/cli/initial-projects/html/src/Layout.elm +++ /dev/null @@ -1,39 +0,0 @@ -module Layout exposing (view) - -import Generated.Routes as Routes exposing (Route, routes) -import Html exposing (..) -import Html.Attributes as Attr -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Html msg -view { page, route } = - div [ Attr.class "app" ] - [ viewHeader route - , page - ] - - -viewHeader : Route -> Html msg -viewHeader currentRoute = - header - [ Attr.class "navbar" - ] - [ viewLink currentRoute ( "home", routes.top ) - , viewLink currentRoute ( "nowhere", routes.notFound ) - ] - - -viewLink : Route -> ( String, Route ) -> Html msg -viewLink currentRoute ( label, route ) = - if currentRoute == route then - span - [ Attr.class "link link--active" ] - [ text label ] - - else - a - [ Attr.class "link" - , Attr.href (Routes.toPath route) - ] - [ text label ] diff --git a/cli/initial-projects/html/src/Layouts/README.md b/cli/initial-projects/html/src/Layouts/README.md deleted file mode 100644 index b8e461e..0000000 --- a/cli/initial-projects/html/src/Layouts/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Layouts -> where all your pages go \ No newline at end of file diff --git a/cli/initial-projects/html/src/Main.elm b/cli/initial-projects/html/src/Main.elm deleted file mode 100644 index 56fb23e..0000000 --- a/cli/initial-projects/html/src/Main.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Main exposing (main) - -import Generated.Pages as Pages -import Generated.Routes as Routes exposing (routes) -import Global -import Spa -import Transitions - - -main : Spa.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg -main = - Spa.create - { ui = Spa.usingHtml - , transitions = Transitions.transitions - , routing = - { routes = Routes.parsers - , toPath = Routes.toPath - , notFound = routes.notFound - , afterNavigate = Nothing - } - , global = - { init = Global.init - , update = Global.update - , subscriptions = Global.subscriptions - } - , page = Pages.page - } diff --git a/cli/initial-projects/html/src/Pages/NotFound.elm b/cli/initial-projects/html/src/Pages/NotFound.elm deleted file mode 100644 index f2f6c87..0000000 --- a/cli/initial-projects/html/src/Pages/NotFound.elm +++ /dev/null @@ -1,42 +0,0 @@ -module Pages.NotFound exposing (Model, Msg, page) - -import Generated.Params as Params -import Generated.Routes as Routes exposing (routes) -import Html exposing (..) -import Html.Attributes as Attr -import Spa.Page -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.NotFound Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "not found | elm-spa" - , view = always view - } - - - --- VIEW - - -view : Html Msg -view = - div [ Attr.class "page" ] - [ h1 - [ Attr.class "page__title" ] - [ text "404 is life." ] - , a - [ Attr.class "page__link" - , Attr.href (Routes.toPath routes.top) - ] - [ text "back home?" ] - ] diff --git a/cli/initial-projects/html/src/Pages/README.md b/cli/initial-projects/html/src/Pages/README.md deleted file mode 100644 index fb9be14..0000000 --- a/cli/initial-projects/html/src/Pages/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# src/Pages -> where all your pages go - diff --git a/cli/initial-projects/html/src/Pages/Top.elm b/cli/initial-projects/html/src/Pages/Top.elm deleted file mode 100644 index 3b5619a..0000000 --- a/cli/initial-projects/html/src/Pages/Top.elm +++ /dev/null @@ -1,45 +0,0 @@ -module Pages.Top exposing (Model, Msg, page) - -import Generated.Params as Params -import Html exposing (..) -import Html.Attributes as Attr -import Spa.Page -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "homepage" - , view = always view - } - - - --- VIEW - - -view : Html Msg -view = - div [ Attr.class "page" ] - [ h1 - [ Attr.class "page__title" ] - [ text "404 is life." ] - , p [ Attr.class "page__subtitle" ] - [ text "(you're doing great already!)" ] - , a - [ Attr.class "page__link" - , Attr.target "_blank" - , Attr.href "https://elm-spa.dev" - ] - [ text "learn more" - ] - ] diff --git a/cli/initial-projects/html/src/Ports.elm b/cli/initial-projects/html/src/Ports.elm deleted file mode 100644 index 094a416..0000000 --- a/cli/initial-projects/html/src/Ports.elm +++ /dev/null @@ -1,14 +0,0 @@ -port module Ports exposing (log) - -import Json.Encode as Json - - -port outgoing : { action : String, data : Json.Value } -> Cmd msg - - -log : String -> Cmd msg -log message = - outgoing - { action = "LOG" - , data = Json.string message - } diff --git a/cli/initial-projects/html/src/Transitions.elm b/cli/initial-projects/html/src/Transitions.elm deleted file mode 100644 index 0e8d6de..0000000 --- a/cli/initial-projects/html/src/Transitions.elm +++ /dev/null @@ -1,12 +0,0 @@ -module Transitions exposing (transitions) - -import Spa.Transition as Transition -import Utils.Spa as Spa - - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeHtml 500 - , page = Transition.fadeHtml 300 - , pages = [] - } diff --git a/cli/initial-projects/html/src/Utils/README.md b/cli/initial-projects/html/src/Utils/README.md deleted file mode 100644 index 05c12de..0000000 --- a/cli/initial-projects/html/src/Utils/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Utils -> a place for helper functions diff --git a/cli/initial-projects/html/src/Utils/Spa.elm b/cli/initial-projects/html/src/Utils/Spa.elm deleted file mode 100644 index 1be8bf8..0000000 --- a/cli/initial-projects/html/src/Utils/Spa.elm +++ /dev/null @@ -1,72 +0,0 @@ -module Utils.Spa exposing - ( Bundle - , Init - , LayoutContext - , Page - , PageContext - , Recipe - , Transitions - , Update - , layout - , recipe - ) - -import Html exposing (Html) -import Generated.Routes as Routes exposing (Route) -import Global -import Spa.Page -import Spa.Types - - -type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Html msg) layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -type alias Init model msg = - Spa.Types.Init Route model msg Global.Model Global.Msg - - -type alias Update model msg = - Spa.Types.Update Route model msg Global.Model Global.Msg - - -type alias Bundle msg appMsg = - Spa.Types.Bundle Route msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg) - - -type alias LayoutContext msg = - Spa.Types.LayoutContext Route msg (Html msg) Global.Model Global.Msg - - -type alias PageContext = - Spa.Types.PageContext Route Global.Model - - -type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg) - - -layout : - Layout params model msg appMsg - -> Page params model msg layoutModel layoutMsg appMsg -layout = - Spa.Page.layout Html.map - - -type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Html msg) layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -recipe : - Upgrade params model msg layoutModel layoutMsg appMsg - -> Recipe params model msg layoutModel layoutMsg appMsg -recipe = - Spa.Page.recipe Html.map - - -type alias Transitions msg = - Spa.Types.Transitions (Html msg) diff --git a/cli/package-lock.json b/cli/package-lock.json index 8c93603..23b34da 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,1107 +1,5 @@ { "name": "elm-spa", - "version": "3.0.3", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "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" - } - }, - "ansi-styles": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", - "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "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.9.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", - "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "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" - } - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "dev": true, - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "binwrap": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.2.2.tgz", - "integrity": "sha512-Y+Wvypk3JhH5GPZAvlwJAWOVH/OsOhQMSj37vySuWHwQivoALplPxfBA8b973rFJI7OS+O+1YmmYXIiEXVMAcw==", - "dev": true, - "requires": { - "mustache": "^3.0.1", - "request": "^2.88.0", - "request-promise": "^4.2.4", - "tar": "^4.4.10", - "unzip-stream": "^0.3.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "dev": true, - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "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" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "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 - }, - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "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" - } - }, - "elm-test": { - "version": "0.19.1-revision2", - "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision2.tgz", - "integrity": "sha512-zVs2mVeyIE+K9y7/8b333h5xRMDWAoqbBDm7ThLDhyTi7ICxeL3t5uOS4KZCrRk9+4sP6+voSbcBlgr46Q+GiQ==", - "dev": true, - "requires": { - "chalk": "3.0.0", - "chokidar": "3.3.0", - "cross-spawn": "7.0.1", - "elmi-to-json": "1.3.0", - "find-parent-dir": "^0.3.0", - "firstline": "2.0.2", - "fs-extra": "8.1.0", - "glob": "7.1.6", - "lodash": "4.17.15", - "minimist": "^1.2.0", - "murmur-hash-js": "1.0.0", - "node-elm-compiler": "5.0.4", - "split": "1.0.1", - "supports-color": "7.1.0", - "temp": "0.9.1", - "which": "2.0.1", - "xmlbuilder": "^13.0.2" - } - }, - "elmi-to-json": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/elmi-to-json/-/elmi-to-json-1.3.0.tgz", - "integrity": "sha512-6m1D5/Pb5pUrSOOBgRG3fE2mK19nhmLgZ16jj2KWTVIhT+0GIBuDI1iV0Fee27CZH790J7uMcdGWJ7fnVvpsKg==", - "dev": true, - "requires": { - "binwrap": "0.2.2" - } - }, - "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.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-elm-dependencies": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.2.tgz", - "integrity": "sha512-nM5UCbccD1G8CGK2GsM7ykG3ksOAl9E+34jiDfl07CAl2OPnLpBVWY2hlxEmIkSBfdJjSopEowWHrO0cI8RhxQ==", - "dev": true, - "requires": { - "firstline": "1.2.0", - "lodash": "4.17.15" - }, - "dependencies": { - "firstline": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.2.0.tgz", - "integrity": "sha1-yfSIbn9/vwr8EtcZQdzgaxkq6gU=", - "dev": true - } - } - }, - "find-parent-dir": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", - "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", - "dev": true - }, - "firstline": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/firstline/-/firstline-2.0.2.tgz", - "integrity": "sha512-8KcmfI0jgSECnzdhucm0i7vrwef3BWwgjimW2YkRC5eSFwjb5DibVoA0YvgkYwwxuJi9c+7M7X3b3lX8o9B6wg==", - "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" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "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" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "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" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "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" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "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 - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "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" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "murmur-hash-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/murmur-hash-js/-/murmur-hash-js-1.0.0.tgz", - "integrity": "sha1-UEEEkmnJZjPIZjhpYLL0KJ515bA=", - "dev": true - }, - "mustache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.0.tgz", - "integrity": "sha512-n5de2nQ1g2iz3PO9cmq/ZZx3W7glqjf0kavThtqfuNlZRllgU2a2Q0jWoQy3BloT5A6no7sjCTHBVn1rEKjx1Q==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-elm-compiler": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.4.tgz", - "integrity": "sha512-VQsT8QSierYGkHzRed+b4MnccQVF1+qPHunE8jBoU7jD6YpuRqCDPzEoC2zfyEJS80qVnlMZrqobLnyjzX9lJg==", - "dev": true, - "requires": { - "cross-spawn": "6.0.5", - "find-elm-dependencies": "2.0.2", - "lodash": "4.17.15", - "temp": "^0.9.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "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 - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "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 - }, - "picomatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", - "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", - "dev": true - }, - "psl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", - "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==", - "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 - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "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" - } - }, - "request-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz", - "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==", - "dev": true, - "requires": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "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 - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, - "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" - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "temp": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz", - "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==", - "dev": true, - "requires": { - "rimraf": "~2.6.2" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.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 - } - } - }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "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 - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unzip-stream": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.0.tgz", - "integrity": "sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A==", - "dev": true, - "requires": { - "binary": "^0.3.0", - "mkdirp": "^0.5.1" - } - }, - "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" - } - }, - "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xmlbuilder": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", - "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } + "version": "4.0.0", + "lockfileVersion": 1 } diff --git a/cli/package.json b/cli/package.json index f415cc8..f2a96e8 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,15 +1,18 @@ { "name": "elm-spa", - "version": "3.0.3", - "description": "the cli companion tool for ryannhg/elm-spa", - "main": "src/index.js", - "bin": "src/index.js", + "version": "4.0.0", + "description": "single page apps made easy", + "main": "index.js", + "bin": "./index.js", + "directories": { + "test": "tests" + }, "scripts": { - "start": "npm run build && node src/index.js", - "build": "npm run elm:compile", + "build": "elm make src/Main.elm --optimize --output dist/elm.worker.js", + "publish": "npm run build && npm publish", "test": "elm-test", "test:watch": "elm-test --watch", - "elm:compile": "elm make src/elm/Main.elm --output=dist/elm.compiled.js --optimize" + "dev": "chokidar src -c \"(npm run build || true)\"" }, "repository": { "type": "git", @@ -18,7 +21,7 @@ "keywords": [ "elm", "spa", - "cli", + "web", "framework" ], "author": "Ryan Haskell-Glatz", @@ -27,8 +30,5 @@ "url": "https://github.com/ryannhg/elm-spa/issues" }, "homepage": "https://github.com/ryannhg/elm-spa#readme", - "devDependencies": { - "elm": "0.19.1-3", - "elm-test": "0.19.1-revision2" - } + "dependencies": {} } diff --git a/cli/initial-projects/elm-ui/.gitignore b/cli/projects/new/.gitignore similarity index 100% rename from cli/initial-projects/elm-ui/.gitignore rename to cli/projects/new/.gitignore diff --git a/cli/initial-projects/html/elm.json b/cli/projects/new/elm.json similarity index 65% rename from cli/initial-projects/html/elm.json rename to cli/projects/new/elm.json index d265a32..de259ec 100644 --- a/cli/initial-projects/html/elm.json +++ b/cli/projects/new/elm.json @@ -2,22 +2,20 @@ "type": "application", "source-directories": [ "src", - "elm-stuff/.elm-spa" + "../../../src" ], "elm-version": "0.19.1", "dependencies": { "direct": { "elm/browser": "1.0.2", - "elm/core": "1.0.4", + "elm/core": "1.0.5", "elm/html": "1.0.0", - "elm/json": "1.1.3", - "elm/url": "1.0.0", - "ryannhg/elm-spa": "3.0.0" + "elm/url": "1.0.0" }, "indirect": { + "elm/json": "1.1.3", "elm/time": "1.0.0", - "elm/virtual-dom": "1.0.2", - "mdgriffith/elm-ui": "1.1.5" + "elm/virtual-dom": "1.0.2" } }, "test-dependencies": { diff --git a/cli/projects/new/package.json b/cli/projects/new/package.json new file mode 100644 index 0000000..d024916 --- /dev/null +++ b/cli/projects/new/package.json @@ -0,0 +1,26 @@ +{ + "name": "elm-spa-app", + "version": "1.0.0", + "description": "my new elm-spa application", + "main": "public/index.html", + "scripts": { + "start": "npm install && npm run build && npm run dev", + "build": "npm run build:elm-spa && npm run build:elm", + "build:elm-spa": "elm-spa build .", + "build:elm": "elm make src/Main.elm --optimize --output public/dist/elm.js", + "dev": "npm run dev:elm-spa & npm run dev:elm", + "dev:elm-spa": "chokidar src/Pages -c \"npm run build:elm-spa\"", + "dev:elm": "elm-live src/Main.elm -u -d public -- --debug --output public/dist/elm.js" + }, + "keywords": [ + "elm", + "spa" + ], + "author": "", + "license": "ISC", + "devDependencies": { + "chokidar-cli": "2.1.0", + "elm": "0.19.1-3", + "elm-live": "4.0.2" + } +} diff --git a/cli/projects/new/public/index.html b/cli/projects/new/public/index.html new file mode 100644 index 0000000..dd155f6 --- /dev/null +++ b/cli/projects/new/public/index.html @@ -0,0 +1,15 @@ + + + + + + + elm-spa + + + + + + \ No newline at end of file diff --git a/cli/projects/new/src/Components.elm b/cli/projects/new/src/Components.elm new file mode 100644 index 0000000..7372d12 --- /dev/null +++ b/cli/projects/new/src/Components.elm @@ -0,0 +1,36 @@ +module Components exposing (layout) + +import Browser exposing (Document) +import Generated.Route as Route exposing (Route) +import Html exposing (..) +import Html.Attributes as Attr exposing (class, href, style) + + +layout : { page : Document msg } -> Document msg +layout { page } = + { title = page.title + , body = + [ div [ class "column spacing--large pad--medium container h--fill" ] + [ navbar + , div [ class "column", style "flex" "1 0 auto" ] page.body + , footer + ] + ] + } + + +navbar : Html msg +navbar = + header [ class "row center-y spacing--between" ] + [ a [ class "link font--h5", href (Route.toHref Route.Top) ] [ text "home" ] + , div [ class "row center-y spacing--medium" ] + [ a [ class "link", href (Route.toHref Route.Docs) ] [ text "docs" ] + , a [ class "link", href (Route.toHref Route.NotFound) ] [ text "a broken link" ] + , a [ class "button", href "https://twitter.com/intent/tweet?text=elm-spa is ez pz" ] [ text "tweet about it" ] + ] + ] + + +footer : Html msg +footer = + Html.footer [] [ text "built with elm ❤" ] diff --git a/cli/projects/new/src/Global.elm b/cli/projects/new/src/Global.elm new file mode 100644 index 0000000..1e0f213 --- /dev/null +++ b/cli/projects/new/src/Global.elm @@ -0,0 +1,98 @@ +module Global exposing + ( Flags + , Model + , Msg + , init + , navigate + , subscriptions + , update + , view + ) + +import Browser exposing (Document) +import Browser.Navigation as Nav +import Components +import Generated.Route as Route exposing (Route) +import Task +import Url exposing (Url) + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + { flags : Flags + , url : Url + , key : Nav.Key + } + + +init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) +init flags url key = + ( Model + flags + url + key + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = Navigate Route + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Navigate route -> + ( model + , Nav.pushUrl model.key (Route.toHref route) + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : + { page : Document msg + , global : Model + , toMsg : Msg -> msg + } + -> Document msg +view { page, global, toMsg } = + Components.layout + { page = page + } + + + +-- COMMANDS + + +send : msg -> Cmd msg +send = + Task.succeed >> Task.perform identity + + +navigate : Route -> Cmd Msg +navigate route = + send (Navigate route) diff --git a/cli/projects/new/src/Main.elm b/cli/projects/new/src/Main.elm new file mode 100644 index 0000000..4c9ca06 --- /dev/null +++ b/cli/projects/new/src/Main.elm @@ -0,0 +1,141 @@ +module Main exposing (main) + +import Browser exposing (Document) +import Browser.Navigation as Nav exposing (Key) +import Generated.Pages as Pages +import Generated.Route as Route exposing (Route) +import Global +import Html +import Url exposing (Url) + + +main : Program Flags Model Msg +main = + Browser.application + { init = init + , view = view + , update = update + , subscriptions = subscriptions + , onUrlRequest = LinkClicked + , onUrlChange = UrlChanged + } + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + { key : Key + , url : Url + , global : Global.Model + , page : Pages.Model + } + + +init : Flags -> Url -> Key -> ( Model, Cmd Msg ) +init flags url key = + let + ( global, globalCmd ) = + Global.init flags url key + + ( page, pageCmd, pageGlobalCmd ) = + Pages.init (fromUrl url) global + in + ( Model key url global page + , Cmd.batch + [ Cmd.map Global globalCmd + , Cmd.map Global pageGlobalCmd + , Cmd.map Page pageCmd + ] + ) + + +type Msg + = LinkClicked Browser.UrlRequest + | UrlChanged Url + | Global Global.Msg + | Page Pages.Msg + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + LinkClicked (Browser.Internal url) -> + ( model, Nav.pushUrl model.key (Url.toString url) ) + + LinkClicked (Browser.External href) -> + ( model, Nav.load href ) + + UrlChanged url -> + let + ( page, pageCmd, globalCmd ) = + Pages.init (fromUrl url) model.global + in + ( { model | url = url, page = page } + , Cmd.batch + [ Cmd.map Page pageCmd + , Cmd.map Global globalCmd + ] + ) + + Global globalMsg -> + let + ( global, globalCmd ) = + Global.update globalMsg model.global + in + ( { model | global = global } + , Cmd.map Global globalCmd + ) + + Page pageMsg -> + let + ( page, pageCmd, globalCmd ) = + Pages.update pageMsg model.page model.global + in + ( { model | page = page } + , Cmd.batch + [ Cmd.map Page pageCmd + , Cmd.map Global globalCmd + ] + ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.batch + [ model.global + |> Global.subscriptions + |> Sub.map Global + , model.page + |> (\page -> Pages.subscriptions page model.global) + |> Sub.map Page + ] + + +view : Model -> Browser.Document Msg +view model = + let + documentMap : + (msg1 -> msg2) + -> Document msg1 + -> Document msg2 + documentMap fn doc = + { title = doc.title + , body = List.map (Html.map fn) doc.body + } + in + Global.view + { page = Pages.view model.page model.global |> documentMap Page + , global = model.global + , toMsg = Global + } + + +fromUrl : Url -> Route +fromUrl = + Route.fromUrl >> Maybe.withDefault Route.NotFound diff --git a/cli/projects/new/src/Pages/Docs.elm b/cli/projects/new/src/Pages/Docs.elm new file mode 100644 index 0000000..d5b4260 --- /dev/null +++ b/cli/projects/new/src/Pages/Docs.elm @@ -0,0 +1,32 @@ +module Pages.Docs exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.static + { view = view + } + + +view : Document Msg +view = + { title = "Docs" + , body = [ Html.text "Docs" ] + } \ No newline at end of file diff --git a/cli/projects/new/src/Pages/NotFound.elm b/cli/projects/new/src/Pages/NotFound.elm new file mode 100644 index 0000000..0fd1164 --- /dev/null +++ b/cli/projects/new/src/Pages/NotFound.elm @@ -0,0 +1,32 @@ +module Pages.NotFound exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.static + { view = view + } + + +view : Document Msg +view = + { title = "NotFound" + , body = [ Html.text "NotFound" ] + } diff --git a/cli/projects/new/src/Pages/Top.elm b/cli/projects/new/src/Pages/Top.elm new file mode 100644 index 0000000..9e9f6ed --- /dev/null +++ b/cli/projects/new/src/Pages/Top.elm @@ -0,0 +1,32 @@ +module Pages.Top exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.static + { view = view + } + + +view : Document Msg +view = + { title = "Top" + , body = [ Html.text "Top" ] + } diff --git a/cli/src/Add/Component.elm b/cli/src/Add/Component.elm new file mode 100644 index 0000000..dff6643 --- /dev/null +++ b/cli/src/Add/Component.elm @@ -0,0 +1,64 @@ +module Add.Component exposing (create) + +import Path exposing (Path) + + +create : Path -> String +create path = + """ +module Pages.{{name}} exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + {{flags}} + + +type alias Model = + {} + + +type Msg + = NoOp + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.component + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + +init : Global.Model -> Flags -> ( Model, Cmd Msg, Cmd Global.Msg ) +init global flags = + ( {}, Cmd.none, Cmd.none ) + + +update : Global.Model -> Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +update global msg model = + case msg of + NoOp -> + ( model, Cmd.none, Cmd.none ) + + +subscriptions : Global.Model -> Model -> Sub Msg +subscriptions global model = + Sub.none + + +view : Global.Model -> Model -> Document Msg +view global model = + { title = "{{name}}" + , body = [ Html.text "{{name}}" ] + } +""" + |> String.replace "{{name}}" (Path.toModulePath path) + |> String.replace "{{flags}}" (Path.toFlags path) + |> String.trim diff --git a/cli/src/Add/Element.elm b/cli/src/Add/Element.elm new file mode 100644 index 0000000..d4decf4 --- /dev/null +++ b/cli/src/Add/Element.elm @@ -0,0 +1,64 @@ +module Add.Element exposing (create) + +import Path exposing (Path) + + +create : Path -> String +create path = + """ +module Pages.{{name}} exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + {{flags}} + + +type alias Model = + {} + + +type Msg + = NoOp + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.element + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + +init : Flags -> ( Model, Cmd Msg ) +init flags = + ( {}, Cmd.none ) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + NoOp -> + ( model, Cmd.none ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + +view : Model -> Document Msg +view model = + { title = "{{name}}" + , body = [ Html.text "{{name}}" ] + } +""" + |> String.replace "{{name}}" (Path.toModulePath path) + |> String.replace "{{flags}}" (Path.toFlags path) + |> String.trim diff --git a/cli/src/Add/Sandbox.elm b/cli/src/Add/Sandbox.elm new file mode 100644 index 0000000..1c86170 --- /dev/null +++ b/cli/src/Add/Sandbox.elm @@ -0,0 +1,58 @@ +module Add.Sandbox exposing (create) + +import Path exposing (Path) + + +create : Path -> String +create path = + """ +module Pages.{{name}} exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + {{flags}} + + +type alias Model = + {} + + +type Msg + = NoOp + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.sandbox + { init = init + , update = update + , view = view + } + + +init : Model +init = + {} + + +update : Msg -> Model -> Model +update msg model = + case msg of + NoOp -> + {} + + +view : Model -> Document Msg +view model = + { title = "{{name}}" + , body = [ Html.text "{{name}}" ] + } +""" + |> String.replace "{{name}}" (Path.toModulePath path) + |> String.replace "{{flags}}" (Path.toFlags path) + |> String.trim diff --git a/cli/src/Add/Static.elm b/cli/src/Add/Static.elm new file mode 100644 index 0000000..fe0bb3d --- /dev/null +++ b/cli/src/Add/Static.elm @@ -0,0 +1,44 @@ +module Add.Static exposing (create) + +import Path exposing (Path) + + +create : Path -> String +create path = + """ +module Pages.{{name}} exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Global +import Html +import Spa + + +type alias Flags = + {{flags}} + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.static + { view = view + } + + +view : Document Msg +view = + { title = "{{name}}" + , body = [ Html.text "{{name}}" ] + } +""" + |> String.replace "{{name}}" (Path.toModulePath path) + |> String.replace "{{flags}}" (Path.toFlags path) + |> String.trim diff --git a/cli/src/Generators/Pages.elm b/cli/src/Generators/Pages.elm new file mode 100644 index 0000000..59f340e --- /dev/null +++ b/cli/src/Generators/Pages.elm @@ -0,0 +1,299 @@ +module Generators.Pages exposing + ( generate + , pagesBundle + , pagesCustomType + , pagesImports + , pagesInit + , pagesUpdate + , pagesUpgradedTypes + , pagesUpgradedValues + ) + +import Path exposing (Path) +import Utils.Generate as Utils + + +generate : List Path -> String +generate paths = + String.trim """ +module Generated.Pages exposing + ( Model + , Msg + , init + , subscriptions + , update + , view + ) + +import Browser exposing (Document) +import Generated.Route as Route exposing (Route) +import Global +import Spa +{{pagesImports}} + + + +-- TYPES + + +{{pagesModels}} + + +{{pagesMsgs}} + + + +-- PAGES + + +type alias UpgradedPage flags model msg = + { init : flags -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + , update : msg -> model -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + , bundle : model -> Global.Model -> Spa.Bundle Msg + } + + +type alias UpgradedPages = +{{pagesUpgradedTypes}} + + +pages : UpgradedPages +pages = +{{pagesUpgradedValues}} + + + +-- INIT + + +{{pagesInit}} + + + +-- UPDATE + + +{{pagesUpdate}} + + + +-- BUNDLE - (view + subscriptions) + + +{{pagesBundle}} + + +view : Model -> Global.Model -> Document Msg +view model = + bundle model >> .view + + +subscriptions : Model -> Global.Model -> Sub Msg +subscriptions model = + bundle model >> .subscriptions + +""" + |> String.replace "{{pagesImports}}" (pagesImports paths) + |> String.replace "{{pagesModels}}" (pagesModels paths) + |> String.replace "{{pagesMsgs}}" (pagesMsgs paths) + |> String.replace "{{pagesUpgradedTypes}}" (pagesUpgradedTypes paths) + |> String.replace "{{pagesUpgradedValues}}" (pagesUpgradedValues paths) + |> String.replace "{{pagesInit}}" (pagesInit paths) + |> String.replace "{{pagesUpdate}}" (pagesUpdate paths) + |> String.replace "{{pagesBundle}}" (pagesBundle paths) + + +pagesImports : List Path -> String +pagesImports paths = + paths + |> List.map Path.toModulePath + |> List.map ((++) "import Pages.") + |> String.join "\n" + + +pagesModels : List Path -> String +pagesModels = + pagesCustomType "Model" + + +pagesMsgs : List Path -> String +pagesMsgs = + pagesCustomType "Msg" + + +pagesCustomType : String -> List Path -> String +pagesCustomType name paths = + Utils.customType + { name = name + , variants = + List.map + (\path -> + Path.toTypeName path + ++ "_" + ++ name + ++ " Pages." + ++ Path.toModulePath path + ++ "." + ++ name + ) + paths + } + + +pagesUpgradedTypes : List Path -> String +pagesUpgradedTypes paths = + paths + |> List.map + (\path -> + let + name = + "Pages." ++ Path.toModulePath path + in + ( Path.toVariableName path + , "UpgradedPage " + ++ name + ++ ".Flags " + ++ name + ++ ".Model " + ++ name + ++ ".Msg" + ) + ) + |> Utils.recordType + |> Utils.indent 1 + + +pagesUpgradedValues : List Path -> String +pagesUpgradedValues paths = + paths + |> List.map + (\path -> + ( Path.toVariableName path + , "Pages." + ++ Path.toModulePath path + ++ ".page |> Spa.upgrade " + ++ Path.toTypeName path + ++ "_Model " + ++ Path.toTypeName path + ++ "_Msg" + ) + ) + |> Utils.recordValue + |> Utils.indent 1 + + +pagesInit : List Path -> String +pagesInit paths = + Utils.function + { name = "init" + , annotation = [ "Route", "Global.Model", "( Model, Cmd Msg, Cmd Global.Msg )" ] + , inputs = [ "route" ] + , body = + Utils.caseExpression + { variable = "route" + , cases = + paths + |> List.map + (\path -> + ( "Route." + ++ Path.toTypeName path + ++ (if Path.hasParams path then + " params" + + else + "" + ) + , "pages." + ++ Path.toVariableName path + ++ ".init" + ++ (if Path.hasParams path then + " params" + + else + " ()" + ) + ) + ) + } + } + + +pagesUpdate : List Path -> String +pagesUpdate paths = + Utils.function + { name = "update" + , annotation = [ "Msg", "Model", "Global.Model", "( Model, Cmd Msg, Cmd Global.Msg )" ] + , inputs = [ "bigMsg bigModel" ] + , body = + Utils.caseExpression + { variable = "( bigMsg, bigModel )" + , cases = + paths + |> List.map + (\path -> + let + typeName = + Path.toTypeName path + in + ( "( " + ++ typeName + ++ "_Msg msg, " + ++ typeName + ++ "_Model model )" + , "pages." ++ Path.toVariableName path ++ ".update msg model" + ) + ) + |> (\cases -> + if List.length paths == 1 then + cases + + else + cases ++ [ ( "_", "always ( bigModel, Cmd.none, Cmd.none )" ) ] + ) + } + } + + +pagesBundle : List Path -> String +pagesBundle paths = + Utils.function + { name = "bundle" + , annotation = [ "Model", "Global.Model", "Spa.Bundle Msg" ] + , inputs = [ "bigModel" ] + , body = + Utils.caseExpression + { variable = "bigModel" + , cases = + paths + |> List.map + (\path -> + let + typeName = + Path.toTypeName path + in + ( typeName ++ "_Model model" + , "pages." ++ Path.toVariableName path ++ ".bundle model" + ) + ) + } + } + + + +-- bundle : Model -> Global.Model -> Spa.Bundle Msg +-- bundle appModel = +-- case appModel of +-- Top_Model model -> +-- pages.top.bundle model +-- Profile_Model model -> +-- pages.profile.bundle model +-- About_Model model -> +-- pages.about.bundle model +-- Authors_Dynamic_Posts_Dynamic_Model model -> +-- pages.authors_dynamic_posts_dynamic.bundle model +-- Posts_Top_Model model -> +-- pages.posts_top.bundle model +-- Posts_Dynamic_Model model -> +-- pages.posts_dynamic.bundle model +-- NotFound_Model model -> +-- pages.notFound.bundle model diff --git a/cli/src/Generators/Route.elm b/cli/src/Generators/Route.elm new file mode 100644 index 0000000..cf900b9 --- /dev/null +++ b/cli/src/Generators/Route.elm @@ -0,0 +1,111 @@ +module Generators.Route exposing + ( generate + , routeCustomType + , routeParsers + , routeSegments + ) + +import Path exposing (Path) +import Utils.Generate as Utils + + +generate : List Path -> String +generate paths = + String.trim """ +module Generated.Route exposing + ( Route(..) + , fromUrl + , toHref + ) + +import Url exposing (Url) +import Url.Parser as Parser exposing ((), Parser) + + +{{routeCustomType}} + + +fromUrl : Url -> Maybe Route +fromUrl = + Parser.parse routes + + +routes : Parser (Route -> a) a +routes = + Parser.oneOf +{{routeParsers}} + + +toHref : Route -> String +toHref route = + let + segments : List String + segments = +{{routeSegments}} + in + segments + |> String.join "/" + |> String.append "/" +""" + |> String.replace "{{routeCustomType}}" (routeCustomType paths) + |> String.replace "{{routeParsers}}" (routeParsers paths) + |> String.replace "{{routeSegments}}" (routeSegments paths) + + +routeCustomType : List Path -> String +routeCustomType paths = + Utils.customType + { name = "Route" + , variants = + List.map + (\path -> Path.toTypeName path ++ Path.optionalParams path) + paths + } + + +routeParsers : List Path -> String +routeParsers paths = + paths + |> List.map Path.toParser + |> Utils.list + |> Utils.indent 2 + + +routeSegments : List Path -> String +routeSegments paths = + case paths of + [] -> + "" + + _ -> + Utils.caseExpression + { variable = "route" + , cases = + paths + |> List.map + (\path -> + ( Path.toTypeName path ++ Path.toParamInputs path + , Path.toParamList path + ) + ) + } + |> Utils.indent 3 + + + +-- case route of +-- Top -> +-- [] +-- About -> +-- [ "about" ] +-- Authors_Dynamic_Posts_Dynamic { param1, param2 } -> +-- [ "authors", param1, "posts", param2 ] +-- Posts_Top -> +-- [ "posts" ] +-- Posts_Dynamic { param1 } -> +-- [ "posts", param1 ] +-- Profile -> +-- [ "profile" ] +-- NotFound -> +-- [ "not-found" ] +-- """ diff --git a/cli/src/Main.elm b/cli/src/Main.elm new file mode 100644 index 0000000..464faa4 --- /dev/null +++ b/cli/src/Main.elm @@ -0,0 +1,33 @@ +module Main exposing (main) + +import Platform +import Ports + + +main : + Program + { command : String + , name : String + , pageType : String + , filepaths : List String + } + () + Never +main = + Platform.worker + { init = + \{ command, filepaths, name, pageType } -> + ( () + , case command of + "build" -> + Ports.build filepaths + + "add" -> + Ports.add { pageType = pageType, name = name } + + _ -> + Ports.uhhh () + ) + , update = \_ model -> ( model, Cmd.none ) + , subscriptions = \_ -> Sub.none + } diff --git a/cli/src/Path.elm b/cli/src/Path.elm new file mode 100644 index 0000000..b7b0f39 --- /dev/null +++ b/cli/src/Path.elm @@ -0,0 +1,266 @@ +module Path exposing + ( Path + , fromFilepath + , fromModuleName + , hasParams + , optionalParams + , toFilepath + , toFlags + , toList + , toModulePath + , toParamInputs + , toParamList + , toParser + , toTypeName + , toVariableName + ) + +import Utils.Generate as Utils + + +type Path + = Internals (List String) + + +fromFilepath : String -> Path +fromFilepath filepath = + filepath + |> String.replace ".elm" "" + |> String.split "/" + |> List.filter ((/=) "") + |> Internals + + +fromModuleName : String -> Path +fromModuleName name = + name + |> String.split "." + |> Internals + + +toFilepath : Path -> String +toFilepath (Internals list) = + String.join "/" list ++ ".elm" + + +toList : Path -> List String +toList (Internals list) = + list + + +toModulePath : Path -> String +toModulePath = + join "." + + +toTypeName : Path -> String +toTypeName = + join "_" + + +toVariableName : Path -> String +toVariableName (Internals list) = + let + lowercaseFirstLetter : String -> String + lowercaseFirstLetter str = + String.left 1 (String.toLower str) ++ String.dropLeft 1 str + in + list |> List.map lowercaseFirstLetter |> String.join "_" + + +optionalParams : Path -> String +optionalParams = + toSingleLineParamRecord + Utils.singleLineRecordType + (\num -> ( "param" ++ String.fromInt num, "String" )) + + +toFlags : Path -> String +toFlags path = + let + params = + optionalParams path + in + if params == "" then + "()" + + else + String.dropLeft 1 params + + +dynamicCount : Path -> Int +dynamicCount (Internals list) = + list + |> List.filter ((==) "Dynamic") + |> List.length + + +toParser : Path -> String +toParser (Internals list) = + let + count : Int + count = + dynamicCount (Internals list) + + toUrlSegment : String -> String + toUrlSegment piece = + if piece == "Dynamic" then + "Parser.string" + + else + "Parser.s \"" ++ sluggify piece ++ "\"" + + toUrlParser : List String -> String + toUrlParser list_ = + "(" + ++ (list_ |> stripEndingTop |> List.map toUrlSegment |> String.join " ") + ++ ")" + + toStaticParser : List String -> String + toStaticParser list_ = + "Parser.map " + ++ toTypeName (Internals list_) + ++ " " + ++ toUrlParser list_ + + toParamMap : Path -> String + toParamMap = + toSingleLineParamRecord + Utils.singleLineRecordValue + (\num -> ( "param" ++ String.fromInt num, "param" ++ String.fromInt num )) + + dynamicParamsFn : List String -> String + dynamicParamsFn list_ = + "\\" ++ (List.range 1 count |> List.map (\num -> "param" ++ String.fromInt num) |> String.join " ") ++ " ->" ++ toParamMap (Internals list_) + + toDynamicParser : List String -> String + toDynamicParser list_ = + String.join "\n" + [ toUrlParser list_ + , " |> Parser.map (" ++ dynamicParamsFn list_ ++ ")" + , " |> Parser.map " ++ toTypeName (Internals list_) + ] + in + case list of + [ "Top" ] -> + "Parser.map Top Parser.top" + + _ -> + if count > 0 then + toDynamicParser list + + else + toStaticParser list + + +stripEndingTop : List String -> List String +stripEndingTop list_ = + List.reverse list_ + |> (\l -> + if List.head l == Just "Top" then + List.drop 1 l + + else + l + ) + |> List.reverse + + +sluggify : String -> String +sluggify = + String.toList + >> List.map + (\char -> + if Char.isUpper char then + String.fromList [ ' ', char ] + + else + String.fromList [ char ] + ) + >> String.concat + >> String.trim + >> String.replace " " "-" + >> String.toLower + + + +-- { param1, param2 } + + +toParamInputs : Path -> String +toParamInputs path = + let + count = + dynamicCount path + in + if count == 0 then + "" + + else + " { " ++ (List.range 1 count |> List.map (\num -> "param" ++ String.fromInt num) |> String.join ", ") ++ " }" + + + +-- [ "authors", param1, "posts", param2 ] + + +toParamList : Path -> String +toParamList (Internals list) = + let + helper : String -> ( List String, Int ) -> ( List String, Int ) + helper piece ( names, num ) = + if piece == "Dynamic" then + ( names ++ [ "param" ++ String.fromInt num ] + , num + 1 + ) + + else + ( names ++ [ "\"" ++ sluggify piece ++ "\"" ] + , num + ) + in + list + |> stripEndingTop + |> List.foldl helper ( [], 1 ) + |> Tuple.first + |> (\items -> + if List.length items == 0 then + "[]" + + else + "[ " ++ String.join ", " items ++ " ]" + ) + + +hasParams : Path -> Bool +hasParams path = + dynamicCount path > 0 + + + +-- HELPERS + + +join : String -> Path -> String +join separator (Internals list) = + String.join separator list + + +toSingleLineParamRecord : + (List ( String, String ) -> String) + -> (Int -> ( String, String )) + -> Path + -> String +toSingleLineParamRecord toRecord toItem path = + let + count = + dynamicCount path + in + if count == 0 then + "" + + else + List.range 1 count + |> List.map toItem + |> toRecord + |> String.append " " diff --git a/cli/src/Ports.elm b/cli/src/Ports.elm new file mode 100644 index 0000000..6951334 --- /dev/null +++ b/cli/src/Ports.elm @@ -0,0 +1,72 @@ +port module Ports exposing (add, build, uhhh) + +import Add.Component +import Add.Element +import Add.Sandbox +import Add.Static +import Generators.Pages as Pages +import Generators.Route as Route +import Path + + +port addPort : { filepath : String, content : String } -> Cmd msg + + +port buildPort : List { filepath : String, content : String } -> Cmd msg + + +port uhhh : () -> Cmd msg + + +add : { name : String, pageType : String } -> Cmd msg +add { name, pageType } = + let + path = + Path.fromModuleName name + + sendItBro : String -> Cmd msg + sendItBro content = + addPort + { filepath = Path.toFilepath path + , content = content + } + in + case pageType of + "static" -> + path + |> Add.Static.create + |> sendItBro + + "sandbox" -> + path + |> Add.Sandbox.create + |> sendItBro + + "element" -> + path + |> Add.Element.create + |> sendItBro + + "component" -> + path + |> Add.Component.create + |> sendItBro + + _ -> + uhhh () + + +build : List String -> Cmd msg +build filepaths = + filepaths + |> List.map Path.fromFilepath + |> (\paths -> + [ { filepath = "Generated/Route.elm" + , content = Route.generate paths + } + , { filepath = "Generated/Pages.elm" + , content = Pages.generate paths + } + ] + ) + |> buildPort diff --git a/cli/src/Utils/Generate.elm b/cli/src/Utils/Generate.elm new file mode 100644 index 0000000..985ef13 --- /dev/null +++ b/cli/src/Utils/Generate.elm @@ -0,0 +1,256 @@ +module Utils.Generate exposing + ( caseExpression + , customType + , function + , import_ + , indent + , list + , recordType + , recordValue + , singleLineRecordType + , singleLineRecordValue + , tuple + ) + + +indent : Int -> String -> String +indent count string = + String.lines string + |> List.map (String.append (" " |> List.repeat count |> String.concat)) + |> String.join "\n" + + +customType : + { name : String + , variants : List String + } + -> String +customType options = + case options.variants of + [] -> + "" + + first :: [] -> + "type " ++ options.name ++ " = " ++ first + + first :: rest -> + multilineIndentedThing + { header = "type " ++ options.name + , items = { first = first, rest = rest } + , prefixes = { first = "= ", rest = "| " } + , suffix = [] + } + + +module_ : + { name : String + , exposing_ : List String + } + -> String +module_ options = + case options.exposing_ of + [] -> + "module " ++ options.name + + first :: [] -> + "module " ++ options.name ++ " exposing " ++ "(" ++ first ++ ")" + + first :: rest -> + multilineIndentedThing + { header = "module " ++ options.name + , items = { first = first, rest = rest } + , prefixes = { first = "( ", rest = ", " } + , suffix = [ ")" ] + } + + +singleLineRecordType : List ( String, String ) -> String +singleLineRecordType = + singleLineRecord " : " + + +singleLineRecordValue : List ( String, String ) -> String +singleLineRecordValue = + singleLineRecord " = " + + +recordType : List ( String, String ) -> String +recordType = + record (\( key, value ) -> key ++ " : " ++ value) + + +recordValue : List ( String, String ) -> String +recordValue = + record (\( key, value ) -> key ++ " = " ++ value) + + +list : List String -> String +list values = + case values of + [] -> + "[]" + + first :: [] -> + "[ " ++ first ++ " ]" + + first :: rest -> + multilineThing + { items = { first = first, rest = rest } + , prefixes = { first = "[ ", rest = ", " } + , suffix = [ "]" ] + } + + +tuple : List String -> String +tuple values = + case values of + [] -> + "()" + + first :: [] -> + "( " ++ first ++ " )" + + first :: rest -> + multilineThing + { items = { first = first, rest = rest } + , prefixes = { first = "( ", rest = ", " } + , suffix = [ ")" ] + } + + +import_ : + { name : String + , alias : Maybe String + , exposing_ : List String + } + -> String +import_ options = + case ( options.alias, options.exposing_ ) of + ( Nothing, [] ) -> + "import " ++ options.name + + ( Just alias_, [] ) -> + "import " ++ options.name ++ " as " ++ alias_ + + ( Nothing, _ ) -> + "import " ++ options.name ++ " exposing (" ++ String.join ", " options.exposing_ ++ ")" + + ( Just alias_, _ ) -> + "import " ++ options.name ++ " as " ++ alias_ ++ " exposing (" ++ String.join ", " options.exposing_ ++ ")" + + +function : + { name : String + , inputs : List String + , annotation : List String + , body : String + } + -> String +function options = + case options.annotation of + [] -> + "" + + _ -> + String.join "\n" + [ options.name ++ " : " ++ String.join " -> " options.annotation + , options.name ++ " " ++ List.foldl (\arg str -> str ++ arg ++ " ") "" options.inputs ++ "=" + , options.body |> indent 1 + ] + + +caseExpression : + { variable : String + , cases : List ( String, String ) + } + -> String +caseExpression options = + let + toBranch : ( String, String ) -> String + toBranch ( value, result ) = + String.join "\n" + [ value ++ " ->" + , indent 1 result + ] + in + case options.cases of + [] -> + "" + + _ -> + [ "case " ++ options.variable ++ " of" + , options.cases + |> List.map toBranch + |> String.join "\n\n" + |> indent 1 + ] + |> String.join "\n" + + + +-- HELPERS + + +multilineIndentedThing : + { header : String + , items : { first : String, rest : List String } + , prefixes : { first : String, rest : String } + , suffix : List String + } + -> String +multilineIndentedThing options = + String.join "\n" + [ options.header + , multilineThing options |> indent 1 + ] + + +multilineThing : + { options + | items : { first : String, rest : List String } + , prefixes : { first : String, rest : String } + , suffix : List String + } + -> String +multilineThing { items, prefixes, suffix } = + [ [ prefixes.first ++ items.first ] + , List.map (String.append prefixes.rest) items.rest + , suffix + ] + |> List.concat + |> String.join "\n" + + +record : + (( String, String ) -> String) + -> List ( String, String ) + -> String +record fromTuple properties = + case properties of + [] -> + "{}" + + first :: [] -> + "{ " ++ fromTuple first ++ " }" + + first :: rest -> + multilineThing + { items = { first = fromTuple first, rest = List.map fromTuple rest } + , prefixes = { first = "{ ", rest = ", " } + , suffix = [ "}" ] + } + + +singleLineRecord : String -> List ( String, String ) -> String +singleLineRecord separator properties = + case properties of + [] -> + "{}" + + _ -> + "{ " + ++ (properties + |> List.map (\( k, v ) -> k ++ separator ++ v) + |> String.join ", " + ) + ++ " }" diff --git a/cli/src/elm/Add.elm b/cli/src/elm/Add.elm deleted file mode 100644 index 01aadf7..0000000 --- a/cli/src/elm/Add.elm +++ /dev/null @@ -1,85 +0,0 @@ -module Add exposing - ( PageType - , generate - , modulePathDecoder - , pageTypeDecoder - ) - -import Json.Decode as D exposing (Decoder) -import Templates.Component -import Templates.Element -import Templates.Sandbox -import Templates.Static - - -type PageType - = Static - | Sandbox - | Element - | Component - - -pageTypeDecoder : Decoder PageType -pageTypeDecoder = - D.string - |> D.andThen - (\pageType -> - case pageType of - "static" -> - D.succeed Static - - "sandbox" -> - D.succeed Sandbox - - "element" -> - D.succeed Element - - "component" -> - D.succeed Component - - _ -> - D.fail <| "Did not recognize page type: " ++ pageType - ) - - -modulePathDecoder : Decoder (List String) -modulePathDecoder = - let - isValidModuleName : String -> Bool - isValidModuleName name = - String.split "." name - |> List.all - (\str -> - case String.toList str of - [] -> - False - - first :: rest -> - Char.isUpper first && List.all Char.isAlpha rest - ) - in - D.string - |> D.andThen - (\name -> - if isValidModuleName name then - D.succeed (String.split "." name) - - else - D.fail "That module name isn't valid." - ) - - -generate : PageType -> { modulePath : List String, ui : String } -> String -generate pageType = - case pageType of - Static -> - Templates.Static.contents - - Sandbox -> - Templates.Sandbox.contents - - Element -> - Templates.Element.contents - - Component -> - Templates.Component.contents diff --git a/cli/src/elm/File.elm b/cli/src/elm/File.elm deleted file mode 100644 index 53de446..0000000 --- a/cli/src/elm/File.elm +++ /dev/null @@ -1,1133 +0,0 @@ -module File exposing - ( Config - , Details - , File - , encode - , pages - , params - , route - , routes - ) - -import Json.Encode as Json -import Set - - -type alias Config = - { ui : String - } - - -type alias Filepath = - List String - - -type alias Details = - { moduleName : String - , folders : List Filepath - , files : List Filepath - } - - -type alias File = - { filepath : Filepath - , contents : String - } - - -encode : File -> Json.Value -encode file = - Json.object - [ ( "filepath", Json.list Json.string file.filepath ) - , ( "contents", Json.string file.contents ) - ] - - - --- PARAMS - - -params : Details -> File -params details = - { filepath = filepathFor details.moduleName "Params" - , contents = paramsContents details - } - - -paramsContents : Details -> String -paramsContents details = - """ -module {{paramModuleName}} exposing (..) - - -{{paramsTypeAliases}} - """ - |> String.replace "{{paramModuleName}}" - (paramsModuleName details.moduleName) - |> String.replace "{{paramsTypeAliases}}" - (paramsTypeAliases details.files) - |> String.trim - - -paramsModuleName : String -> String -paramsModuleName = - moduleNameFor "Params" - - -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) - |> String.replace "{{paramsRecord}}" - (paramsRecord filepath |> indent 1) - |> String.trim - - -dynamicCount : List String -> Int -dynamicCount path = - path - |> List.filter ((==) "Dynamic") - |> List.length - - -paramsRecord : List String -> String -paramsRecord path = - if dynamicCount path > 0 then - List.range 1 (dynamicCount path) - |> List.map String.fromInt - |> List.map (\num -> [ "param", num, " : String" ]) - |> List.map String.concat - |> String.join "\n, " - |> (\str -> "{ " ++ str ++ "\n}") - - else - "{}" - - - --- ROUTE - - -type alias Context = - { shouldImportParams : String -> Bool - } - - -route : Context -> Details -> File -route context details = - { filepath = filepathFor details.moduleName "Route" - , contents = routeContents context details - } - - -routeContents : Context -> Details -> String -routeContents context details = - """ -module {{routeModuleName}} exposing - ( Route(..) - , toPath - ) - -{{routeImports}} - - -{{routeTypes}} - - -{{routeToPath}} - """ - |> String.replace "{{routeModuleName}}" - (routeModuleName details.moduleName) - |> String.replace "{{routeImports}}" - (routeImports context details) - |> String.replace "{{routeTypes}}" - (routeTypes details) - |> String.replace "{{routeToPath}}" - (routeToPath details) - |> String.trim - - -routeModuleName : String -> String -routeModuleName = - moduleNameFor "Route" - - -routeModuleNameFromFilepath : Filepath -> String -routeModuleNameFromFilepath = - String.join "." >> routeModuleName - - -routeImports : Context -> Details -> String -routeImports context details = - """ -{{paramImport}} -{{routeFolderImports}} - """ - |> String.replace "{{paramImport}}" - (paramImport context details) - |> String.replace "{{routeFolderImports}}" - (routeFolderImports details.folders) - |> String.trim - - -paramImport : Context -> Details -> String -paramImport context details = - if context.shouldImportParams details.moduleName then - paramsModuleName details.moduleName - |> (\str -> "import " ++ str ++ " as Params") - - else - "" - - -routeFolderImports : List Filepath -> String -routeFolderImports folderNames = - folderNames - |> List.map routeModuleNameFromFilepath - |> asImports - - -routeTypes : Details -> String -routeTypes details = - """ -type Route -{{routeVariants}} - """ - |> String.replace "{{routeVariants}}" (routeVariants details) - |> String.trim - - -routeVariants : Details -> String -routeVariants { folders, files } = - List.concat - [ List.map routeFileVariant files - , List.map routeFolderVariant folders - ] - |> (\list -> - case list of - [] -> - "" - - head :: rest -> - ("= " ++ head) - :: rest - |> String.join "\n| " - |> indent 1 - ) - - -routeFolderVariant : Filepath -> String -routeFolderVariant name = - (if last name == "Dynamic" then - "{{name}}_Folder String {{routeModuleName}}.Route" - - else - "{{name}}_Folder {{routeModuleName}}.Route" - ) - |> String.replace "{{name}}" (last name) - |> String.replace "{{routeModuleName}}" (routeModuleNameFromFilepath name) - - -routeFileVariant : Filepath -> String -routeFileVariant name = - if last name == "Dynamic" then - "Dynamic String Params.Dynamic" - - else - "{{name}} Params.{{name}}" - |> String.replace "{{name}}" (last name) - - -type Item - = StaticFile Filepath - | DynamicFile Filepath - | StaticFolder Filepath - | DynamicFolder Filepath - - -staticRouteCase : String -> String -staticRouteCase word = - case word of - "Top" -> - """ -Top _ -> - "/" - """ - - last_ -> - """ -{{name}} _ -> - "/{{slug}}" - """ - |> String.replace "{{name}}" last_ - |> String.replace "{{slug}}" (sluggify last_) - - -routeCaseTemplate : Item -> String -routeCaseTemplate item = - case item of - StaticFile filepath -> - staticRouteCase (last filepath) - - DynamicFile _ -> - """ -Dynamic value _ -> - "/" ++ value - """ - - StaticFolder filepath -> - """ -{{name}}_Folder subRoute -> - "/{{slug}}" ++ {{routeModuleName}}.toPath subRoute - """ - |> String.replace "{{name}}" (last filepath) - |> String.replace "{{slug}}" (sluggify (last filepath)) - |> String.replace "{{routeModuleName}}" (routeModuleNameFromFilepath filepath) - - DynamicFolder filepath -> - """ -Dynamic_Folder value subRoute -> - "/" ++ value ++ {{routeModuleName}}.toPath subRoute - """ - |> String.replace "{{routeModuleName}}" (routeModuleNameFromFilepath filepath) - - -routeToPath : Details -> String -routeToPath details = - """ -toPath : Route -> String -toPath route = - case route of -{{cases}} - """ - |> String.replace "{{cases}}" - (toItems details - |> List.map routeCaseTemplate - |> List.map String.trim - |> String.join "\n\n\n" - |> indent 2 - ) - |> String.trim - - -toItems : Details -> List Item -toItems { folders, files } = - let - ( dynamicFiles, staticFiles ) = - List.partition endsInDynamic files - - ( dynamicFolders, staticFolders ) = - List.partition endsInDynamic folders - in - [ List.map StaticFile staticFiles - , List.map StaticFolder staticFolders - , List.map DynamicFile dynamicFiles - , List.map DynamicFolder dynamicFolders - ] - |> List.concat - - - --- PAGES - - -pages : Context -> Details -> File -pages context details = - { filepath = filepathFor details.moduleName "Pages" - , contents = pagesContents context details - } - - -pagesContents : Context -> Details -> String -pagesContents context details = - """ -module {{pagesModuleName}} exposing - ( Model - , Msg - , page - , path - ) - -import Spa.Page -import Spa.Path exposing (Path, static, dynamic) -import {{layoutModuleName}} as Layout -import Utils.Spa as Spa -{{paramImport}} -import {{routeModuleName}} as Route exposing (Route) -{{pagesPageImports}} -{{pagesFolderRouteImports}} -{{pagesFolderPagesImports}} - - -{{pagesModelTypes}} - - -{{pagesMsgTypes}} - - -page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg -page = - Spa.layout - { path = path - , view = Layout.view - , recipe = - { init = init - , update = update - , bundle = bundle - } - } - - -path : Path -path = - {{pagesLayoutPath}} - - --- RECIPES - - -type alias Recipe flags model msg appMsg = - Spa.Recipe flags model msg Model Msg appMsg - - -type alias Recipes msg = -{{pagesRecipesTypeAliases}} - - -recipes : Recipes msg -recipes = -{{pagesRecipesFunctions}} - - - --- INIT - - -init : Route -> Spa.Init Model Msg -init route_ = -{{pagesInitFunction}} - - - --- UPDATE - - -update : Msg -> Model -> Spa.Update Model Msg -update bigMsg bigModel = -{{pagesUpdateFunction}} -{{defaultUpdateCase}} - - --- BUNDLE - - -bundle : Model -> Spa.Bundle Msg msg -bundle bigModel = -{{pagesBundleFunction}} - """ - |> String.replace "{{pagesModuleName}}" (pagesModuleName details.moduleName) - |> String.replace "{{layoutModuleName}}" (pagesLayoutModuleName details.moduleName) - |> String.replace "{{paramImport}}" (paramImport context details) - |> String.replace "{{routeModuleName}}" (routeModuleName details.moduleName) - |> String.replace "{{pagesPageImports}}" (pagesPageImports details.files) - |> String.replace "{{pagesFolderRouteImports}}" (pagesFolderImports "Route" details.folders) - |> String.replace "{{pagesFolderPagesImports}}" (pagesFolderImports "Pages" details.folders) - |> String.replace "{{pagesModelTypes}}" (pagesCustomTypes "Model" details) - |> String.replace "{{pagesMsgTypes}}" (pagesCustomTypes "Msg" details) - |> String.replace "{{pagesLayoutPath}}" (pagesLayoutPath details) - |> String.replace "{{pagesRecipesTypeAliases}}" (pagesRecipesTypeAliases details) - |> String.replace "{{pagesRecipesFunctions}}" (pagesRecipesFunctions details) - |> String.replace "{{pagesInitFunction}}" (pagesInitFunction details) - |> String.replace "{{pagesUpdateFunction}}" (pagesUpdateFunction details) - |> String.replace "{{pagesBundleFunction}}" (pagesBundleFunction details) - |> String.replace "{{defaultUpdateCase}}" - (if List.length (details.files ++ details.folders) < 2 then - "" - - else - "_ ->\n Spa.Page.keep bigModel" |> indent 2 - ) - |> String.trim - - -pagesLayoutModuleName : String -> String -pagesLayoutModuleName str = - case str of - "" -> - "Layout" - - _ -> - "Layouts." ++ str - - -pagesModuleName : String -> String -pagesModuleName = - moduleNameFor "Pages" - - -pagesPageImports : List Filepath -> String -pagesPageImports files = - files - |> List.map pagesPageModule - |> asImports - - -pagesPageModule : Filepath -> String -pagesPageModule path = - path - |> String.join "." - |> String.append "Pages." - - -pagesFolderImports : String -> List Filepath -> String -pagesFolderImports suffix folders = - folders - |> List.map (String.join "." >> moduleNameFor suffix) - |> asImports - - -pagesLayoutPath : Details -> String -pagesLayoutPath { moduleName } = - String.split "." moduleName - |> List.map - (\piece -> - case piece of - "Dynamic" -> - "dynamic" - - _ -> - "static \"" ++ sluggify piece ++ "\"" - ) - |> (\pieces -> - if List.isEmpty pieces || pieces == [ "static \"\"" ] then - "[]" - - else - "[ " ++ String.join ", " pieces ++ " ]" - ) - - -pagesCustomTypes : String -> Details -> String -pagesCustomTypes type_ { files, folders } = - let - toFileTuple : Filepath -> ( String, String ) - toFileTuple path = - let - name = - last path - in - ( name ++ type_ - , pagesPageModule path ++ "." ++ type_ - ) - - toFolderTuple : Filepath -> ( String, String ) - toFolderTuple path = - let - name = - last path - in - ( name ++ "_Folder_" ++ type_ - , pagesModuleName (String.join "." path) ++ "." ++ type_ - ) - in - List.concat - [ List.map toFileTuple files - , List.map toFolderTuple folders - ] - |> asCustomType type_ - - -asCustomType : String -> List ( String, String ) -> String -asCustomType name items = - case items of - [] -> - "" - - _ -> - items - |> List.map (\( a, b ) -> a ++ " " ++ b) - |> String.join "\n| " - |> (\str -> - [ "type " ++ name ++ "\n" - , indent 1 ("= " ++ str) - ] - ) - |> String.concat - - -pagesRecipesTypeAliases : Details -> String -pagesRecipesTypeAliases { files, folders } = - let - toFileAlias : Filepath -> String - toFileAlias path = - "{{uncapitalizedName}} : Recipe Params.{{name}} {{pagesPageModule}}.Model {{pagesPageModule}}.Msg msg" - |> String.replace "{{uncapitalizedName}}" (uncapitalize (last path)) - |> String.replace "{{name}}" (last path) - |> String.replace "{{pagesPageModule}}" (pagesPageModule path) - - toFolderAlias : Filepath -> String - toFolderAlias path = - "{{uncapitalizedName}}_folder : Recipe {{routeModuleName}}.Route {{pagesModuleName}}.Model {{pagesModuleName}}.Msg msg" - |> String.replace "{{uncapitalizedName}}" (uncapitalize (last path)) - |> String.replace "{{routeModuleName}}" (String.join "." path |> routeModuleName) - |> String.replace "{{pagesModuleName}}" (String.join "." path |> pagesModuleName) - in - [ List.map toFileAlias files - , List.map toFolderAlias folders - ] - |> List.concat - |> asRecord - |> indent 1 - - -pagesRecipesFunctions : Details -> String -pagesRecipesFunctions { files, folders } = - let - fileRecipe : Filepath -> String - fileRecipe path = - [ "page = " ++ pagesPageModule path ++ ".page" - , "toModel = " ++ last path ++ "Model" - , "toMsg = " ++ last path ++ "Msg" - ] - |> asRecord - |> String.trim - |> indent 2 - - toFileFunction : Filepath -> String - toFileFunction path = - """ -{{uncapitalizedName}} = - Spa.recipe -{{function}} - """ - |> String.replace "{{uncapitalizedName}}" (uncapitalize (last path)) - |> String.replace "{{function}}" (fileRecipe path) - |> String.trim - - folderRecipe : Filepath -> String - folderRecipe path = - [ "page = " ++ (String.join "." path |> pagesModuleName) ++ ".page" - , "toModel = " ++ last path ++ "_Folder_Model" - , "toMsg = " ++ last path ++ "_Folder_Msg" - ] - |> asRecord - |> String.trim - |> indent 2 - - toFolderFunction : Filepath -> String - toFolderFunction path = - """ -{{uncapitalizedName}}_folder = - Spa.recipe -{{function}} - """ - |> String.replace "{{uncapitalizedName}}" (uncapitalize (last path)) - |> String.replace "{{function}}" (folderRecipe path) - |> String.trim - in - [ List.map toFileFunction files - , List.map toFolderFunction folders - ] - |> List.concat - |> asRecord - |> indent 1 - - -pagesInitFunction : Details -> String -pagesInitFunction details = - toItems details - |> List.map pagesToInit - |> asCaseExpression "route_" - |> indent 1 - - -pagesToInit : Item -> String -pagesToInit item = - case item of - StaticFile path -> - "Route.{{name}} params ->\n recipes.{{uncapitalized}}.init params" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFile _ -> - "Route.Dynamic _ params ->\n recipes.dynamic.init params" - - StaticFolder path -> - "Route.{{name}}_Folder route ->\n recipes.{{uncapitalized}}_folder.init route" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFolder _ -> - "Route.Dynamic_Folder _ route ->\n recipes.dynamic_folder.init route" - - -pagesUpdateFunction : Details -> String -pagesUpdateFunction details = - toItems details - |> List.map pagesToUpdate - |> asCaseExpression "( bigMsg, bigModel )" - |> indent 1 - - -pagesToUpdate : Item -> String -pagesToUpdate item = - case item of - StaticFile path -> - "( {{name}}Msg msg, {{name}}Model model ) ->\n recipes.{{uncapitalized}}.update msg model" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFile _ -> - "( DynamicMsg msg, DynamicModel model ) ->\n recipes.dynamic.update msg model" - - StaticFolder path -> - "( {{name}}_Folder_Msg msg, {{name}}_Folder_Model model ) ->\n recipes.{{uncapitalized}}_folder.update msg model" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFolder _ -> - "( Dynamic_Folder_Msg msg, Dynamic_Folder_Model model ) ->\n recipes.dynamic_folder.update msg model" - - -pagesBundleFunction : Details -> String -pagesBundleFunction details = - toItems details - |> List.map pagesToBundle - |> asCaseExpression "bigModel" - |> indent 1 - - -pagesToBundle : Item -> String -pagesToBundle item = - case item of - StaticFile path -> - "{{name}}Model model ->\n recipes.{{uncapitalized}}.bundle model" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFile _ -> - "DynamicModel model ->\n recipes.dynamic.bundle model" - - StaticFolder path -> - "{{name}}_Folder_Model model ->\n recipes.{{uncapitalized}}_folder.bundle model" - |> String.replace "{{name}}" (last path) - |> String.replace "{{uncapitalized}}" (uncapitalize (last path)) - - DynamicFolder _ -> - "Dynamic_Folder_Model model ->\n recipes.dynamic_folder.bundle model" - - - --- ROUTES - - -routes : - { paths : List Filepath - , pathsWithFiles : List Filepath - } - -> String -routes { paths, pathsWithFiles } = - """ -module Generated.Routes exposing - ( Route - , parsers - , routes - , toPath - ) - -{{routesFolderImports}} -import Url.Parser as Parser exposing ((), Parser, map, s, string, top) - - - --- ALIASES - - -type alias Route = - Generated.Route.Route - - -toPath : Route -> String -toPath = - Generated.Route.toPath - - - --- ROUTES - - -type alias Routes = -{{routesTypeAliases}} - - -routes : Routes -routes = -{{routesRecords}} - - -parsers : List (Parser (Route -> a) a) -parsers = -{{routesParserLines}} - """ - |> String.replace "{{routesFolderImports}}" (routesFolderImports paths) - |> String.replace "{{routesTypeAliases}}" (routesTypeAliases pathsWithFiles) - |> String.replace "{{routesRecords}}" (routesRecords pathsWithFiles) - |> String.replace "{{routesParserLines}}" (routesParserLines pathsWithFiles) - |> String.trim - - -routesFolderImports : List Filepath -> String -routesFolderImports paths = - paths - |> List.map dropLast - |> Set.fromList - |> Set.toList - |> List.map - (\path -> - [ [ "Generated" ], path, [ "Route" ] ] - |> List.concat - |> String.join "." - ) - |> asImports - - -routesTypeAliases : List Filepath -> String -routesTypeAliases paths = - paths - |> List.sortWith shortestPathThenStatic - |> List.map routesTypeAlias - |> List.map String.trim - |> asRecord - |> indent 1 - - -routesTypeAlias : Filepath -> String -routesTypeAlias path = - "{{routesName}} : {{routesTypeAnnotation}}" - |> String.replace "{{routesName}}" (routesName path) - |> String.replace "{{routesTypeAnnotation}}" (routesTypeAnnotation path) - - -routesTypeAnnotation : Filepath -> String -routesTypeAnnotation path = - case dynamicCount path of - 0 -> - "Route" - - count -> - List.repeat count "String" - |> String.join " -> " - |> (\x -> x ++ " -> Route") - - -routesRecords : List Filepath -> String -routesRecords paths = - paths - |> List.sortWith shortestPathThenStatic - |> List.map routesRecord - |> List.map String.trim - |> asRecord - |> indent 1 - - -routesRecord : Filepath -> String -routesRecord path = - """{{routesName}} = -{{routesRecordFunction}} - """ - |> String.replace "{{routesName}}" (routesName path) - |> String.replace "{{routesRecordFunction}}" (routesRecordFunction path) - |> String.trim - - -routesRecordFunction : Filepath -> String -routesRecordFunction path = - let - inputs : Int -> String - inputs count = - "\\{{params}} ->" - |> String.replace "{{params}}" - (List.range 1 count - |> List.map String.fromInt - |> List.map (\num -> "param" ++ num) - |> String.join " " - ) - - record : Int -> String - record count = - "{ " - ++ (List.range 1 count - |> List.map String.fromInt - |> List.map (\num -> "param" ++ num ++ " = param" ++ num) - |> String.join ", " - ) - ++ " }" - - everySubset : List a -> List (List a) - everySubset path_ = - List.length path_ - |> List.range 1 - |> List.map (\x -> List.reverse path_ |> List.drop x |> List.reverse) - |> List.reverse - |> List.drop 1 - - swapLastTwo : List a -> List a - swapLastTwo = - List.reverse - >> (\list -> - case list of - [] -> - list - - _ :: [] -> - list - - a :: b :: rest -> - b :: a :: rest - ) - >> List.reverse - - suffix : List String -> String - suffix path_ = - if endsInDynamic path_ then - " param" ++ String.fromInt (dynamicCount path_) - - else - "" - - toRouteModule : List String -> String - toRouteModule = - routeModuleNameFromFilepath - >> String.split "." - >> swapLastTwo - >> String.join "." - - body : String - body = - everySubset path - |> List.map toRouteModule - |> List.indexedMap - (\i value -> - let - path_ = - String.split "." value - in - indent i <| - if i < List.length path then - value ++ ("_Folder" ++ suffix path_ ++ " <|") - - else - value ++ suffix path_ - ) - |> (\list -> list ++ [ indent (List.length path - 1) finalPiece ]) - |> String.join "\n" - - finalPiece = - toRouteModule path ++ suffix path - in - case dynamicCount path of - 0 -> - body - ++ " {}" - |> indent 1 - - count -> - [ inputs count - , indent 1 body ++ " " ++ record count - ] - |> String.join "\n" - |> indent 1 - - -routesParserLines : List Filepath -> String -routesParserLines paths = - paths - |> List.sortWith shortestPathThenStatic - |> List.map routesParserLine - |> List.map String.trim - |> asList - |> indent 1 - - -shortestPathThenStatic : Filepath -> Filepath -> Order -shortestPathThenStatic a b = - if List.length a < List.length b then - Basics.LT - - else if List.length a > List.length b then - Basics.GT - - else if last a == "Dynamic" then - Basics.GT - - else if last b == "Dynamic" then - Basics.LT - - else - Basics.EQ - - -routesParserLine : Filepath -> String -routesParserLine path = - """ -map routes.{{routesName}} - ({{routesParser}}) - """ - |> String.replace "{{routesName}}" (routesName path) - |> String.replace "{{routesParser}}" (routesParser path) - |> String.trim - - -routesName : Filepath -> String -routesName filepath = - filepath - |> List.map uncapitalize - |> String.join "_" - - -routesParser : Filepath -> String -routesParser filepath = - let - parserFor piece = - case piece of - "Top" -> - "top" - - "Dynamic" -> - "string" - - _ -> - "s \"" ++ sluggify piece ++ "\"" - in - case filepath of - [] -> - "UNKNOWN" - - first :: [] -> - parserFor first - - first :: rest -> - parserFor first ++ " " ++ routesParser rest - - - --- UTILS - - -asImports : List String -> String -asImports lines = - lines - |> List.map (String.append "import ") - |> String.join "\n" - |> String.trim - - -asRecord : List String -> String -asRecord = - asDataStructure "{" "}" - - -asList : List String -> String -asList = - asDataStructure "[" "]" - - -asCaseExpression : String -> List String -> String -asCaseExpression var cases = - if List.isEmpty cases then - "" - - else - cases - |> String.join "\n\n" - |> indent 1 - |> (++) ("case " ++ var ++ " of\n") - - -asDataStructure : String -> String -> List String -> String -asDataStructure left right items = - case items of - [] -> - left ++ right - - _ -> - left ++ " " ++ String.join "\n, " items ++ "\n" ++ right - - -fromModuleName : String -> List String -fromModuleName = - String.split "." - >> List.filter (not << String.isEmpty) - - -last : List String -> String -last list = - List.drop (List.length list - 1) list - |> List.head - |> Maybe.withDefault "" - - -indent : Int -> String -> String -indent tabs str = - str - |> String.split "\n" - |> List.map (\s -> String.concat (List.repeat tabs " " ++ [ s ])) - |> String.join "\n" - - -filepathFor : String -> String -> List String -filepathFor moduleName name = - fromModuleName moduleName ++ [ name ] - - -moduleNameFor : String -> String -> String -moduleNameFor ending name = - [ "Generated", name, ending ] - |> List.filter (String.isEmpty >> not) - |> String.join "." - - -sluggify : String -> String -sluggify word = - String.toList word - |> List.map - (\c -> - if Char.isUpper c then - [ '-', c ] - - else - [ c ] - ) - |> List.concat - |> String.fromList - |> String.toLower - |> String.dropLeft 1 - - -uncapitalize : String -> String -uncapitalize word = - case String.toList word of - [] -> - "" - - c :: rest -> - String.fromList (Char.toLower c :: rest) - - -dropLast : List a -> List a -dropLast = - List.reverse >> List.drop 1 >> List.reverse - - -endsInDynamic : List String -> Bool -endsInDynamic path = - last path == "Dynamic" diff --git a/cli/src/elm/Main.elm b/cli/src/elm/Main.elm deleted file mode 100644 index af68517..0000000 --- a/cli/src/elm/Main.elm +++ /dev/null @@ -1,243 +0,0 @@ -module Main exposing (main) - -import Add -import Dict exposing (Dict) -import File exposing (File) -import Json.Decode as D exposing (Decoder) -import Json.Encode as Json -import Ports -import Set exposing (Set) -import Templates.Layout -import Utils - - -type alias Flags = - { command : String - , data : Json.Value - } - - -type Args - = BuildArgs BuildConfig - | AddArgs AddConfig - - -type alias BuildConfig = - { paths : List Filepath - } - - -type alias AddConfig = - { ui : String - , pageType : Add.PageType - , modulePath : List String - , layoutPaths : List Filepath - } - - -argsDecoder : String -> Decoder Args -argsDecoder command = - case command of - "add" -> - D.map AddArgs <| - D.map4 AddConfig - (D.field "ui" D.string) - (D.field "pageType" Add.pageTypeDecoder) - (D.field "moduleName" Add.modulePathDecoder) - (D.field "layoutPaths" (D.list (D.list D.string))) - - "build" -> - D.map BuildArgs <| - D.map BuildConfig - (D.list (D.list D.string)) - - _ -> - D.fail <| "Couldn't recognize command: " ++ command - - -type alias Filepath = - List String - - -main : Program Flags () Never -main = - Platform.worker - { init = \flags -> ( (), handle flags ) - , update = \_ model -> ( model, Cmd.none ) - , subscriptions = always Sub.none - } - - -handle : Flags -> Cmd msg -handle flags = - D.decodeValue - (argsDecoder flags.command) - flags.data - |> (\result -> - case result of - Ok args -> - case args of - BuildArgs config -> - build config - - AddArgs config -> - add config - - Err error -> - case error of - D.Failure reason _ -> - Ports.error reason - - _ -> - Cmd.none - ) - - -build : BuildConfig -> Cmd msg -build { paths } = - List.concat - [ [ File [ "Routes" ] - (File.routes - { paths = Utils.allSubsets paths - , pathsWithFiles = paths - } - ) - ] - , paths - |> List.foldl groupByFolder Dict.empty - |> Utils.addInMissingFolders - |> toDetails - |> (\items -> - let - itemsWithFiles : List File.Details - itemsWithFiles = - List.filter - (.files >> List.isEmpty >> not) - items - - shouldImportParams : String -> Bool - shouldImportParams filepath = - List.member filepath - (List.map .moduleName itemsWithFiles) - in - List.concat - [ List.map File.params itemsWithFiles - , List.map (File.route { shouldImportParams = shouldImportParams }) items - , List.map (File.pages { shouldImportParams = shouldImportParams }) items - ] - ) - ] - |> List.map (\file -> { file | filepath = List.append [ "elm-stuff", ".elm-spa", "Generated" ] file.filepath }) - |> Ports.createFiles - - -add : AddConfig -> Cmd msg -add config = - Ports.createFiles <| - List.concat - [ layoutsToCreate - { path = config.modulePath - , existingLayouts = config.layoutPaths - } - |> List.map - (\path -> - { filepath = List.append [ "src", "Layouts" ] path - , contents = Templates.Layout.contents { ui = config.ui, modulePath = path } - } - ) - , [ { filepath = List.append [ "src", "Pages" ] config.modulePath - , contents = - Add.generate - config.pageType - { modulePath = config.modulePath - , ui = config.ui - } - } - ] - ] - - - --- UTILS - - -layoutsToCreate : { path : Filepath, existingLayouts : List Filepath } -> List Filepath -layoutsToCreate { path, existingLayouts } = - let - subLists : List a -> List (List a) - subLists list = - list - |> List.repeat (List.length list - 1) - |> List.indexedMap (\i -> List.take (1 + i)) - in - subLists path - |> List.filter (\list -> not (List.member list existingLayouts)) - - -dropLast : List a -> List a -dropLast = - List.reverse - >> List.drop 1 - >> List.reverse - - -fileWithin : Filepath -> String -fileWithin = - dropLast - >> String.join "." - - -folderWithin : Filepath -> String -folderWithin = - List.reverse - >> List.drop 2 - >> List.reverse - >> String.join "." - - -type alias Items = - { files : Set Filepath - , folders : Set Filepath - } - - -groupByFolder : - Filepath - -> Dict String Items - -> Dict String Items -groupByFolder filepath = - Dict.update - (fileWithin filepath) - (Maybe.map - (\entry -> { entry | files = Set.insert filepath entry.files }) - >> Maybe.withDefault - { files = Set.singleton filepath - , folders = Set.empty - } - >> Just - ) - >> Dict.update - (folderWithin filepath) - (Maybe.map - (\entry -> { entry | folders = Set.insert (dropLast filepath) entry.folders }) - >> Maybe.withDefault - { folders = Set.singleton (dropLast filepath) - , files = Set.empty - } - >> (\entry -> { entry | folders = Set.filter ((/=) []) entry.folders }) - >> Just - ) - - -toDetails : - Dict String Items - -> List File.Details -toDetails dict = - Dict.toList dict - |> List.map - (\( moduleName, { files, folders } ) -> - { moduleName = moduleName - , folders = Set.toList folders - , files = Set.toList files - } - ) diff --git a/cli/src/elm/Ports.elm b/cli/src/elm/Ports.elm deleted file mode 100644 index b4f863f..0000000 --- a/cli/src/elm/Ports.elm +++ /dev/null @@ -1,30 +0,0 @@ -port module Ports exposing - ( createFiles - , error - ) - -import File exposing (File) -import Json.Encode as Json - - -port outgoing : - { message : String - , data : Json.Value - } - -> Cmd msg - - -createFiles : List File -> Cmd msg -createFiles files = - outgoing - { message = "createFiles" - , data = Json.list File.encode files - } - - -error : String -> Cmd msg -error reason = - outgoing - { message = "error" - , data = Json.string reason - } diff --git a/cli/src/elm/Templates/Component.elm b/cli/src/elm/Templates/Component.elm deleted file mode 100644 index aaba075..0000000 --- a/cli/src/elm/Templates/Component.elm +++ /dev/null @@ -1,81 +0,0 @@ -module Templates.Component exposing (contents) - - -contents : { modulePath : List String, ui : String } -> String -contents options = - """ -module Pages.{{moduleName}} exposing (Model, Msg, page) - -import Spa.Page -import {{ui}} exposing (..) -import Generated{{moduleFolder}}.Params as Params -import Global -import Utils.Spa exposing (Page) - - -page : Page Params.{{fileName}} Model Msg model msg appMsg -page = - Spa.Page.component - { title = always "{{moduleName}}" - , init = always init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -type alias Model = - {} - - -init : Params.{{fileName}} -> ( Model, Cmd Msg, Cmd Global.Msg ) -init _ = - ( {} - , Cmd.none - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = Msg - - -update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) -update msg model = - ( model - , Cmd.none - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> {{ui}} Msg -view model = - text "{{moduleName}}" - - """ - |> String.replace "{{moduleName}}" (String.join "." options.modulePath) - |> String.replace "{{fileName}}" (options.modulePath |> List.reverse |> List.head |> Maybe.withDefault "YellAtRyanOnTheInternet") - |> String.replace "{{moduleFolder}}" (options.modulePath |> List.reverse |> List.drop 1 |> List.reverse |> List.map (String.append ".") |> String.concat) - |> String.replace "{{ui}}" options.ui - |> String.trim diff --git a/cli/src/elm/Templates/Element.elm b/cli/src/elm/Templates/Element.elm deleted file mode 100644 index 5c058f0..0000000 --- a/cli/src/elm/Templates/Element.elm +++ /dev/null @@ -1,79 +0,0 @@ -module Templates.Element exposing (contents) - - -contents : { modulePath : List String, ui : String } -> String -contents options = - """ -module Pages.{{moduleName}} exposing (Model, Msg, page) - -import Spa.Page -import {{ui}} exposing (..) -import Generated{{moduleFolder}}.Params as Params -import Utils.Spa exposing (Page) - - -page : Page Params.{{fileName}} Model Msg model msg appMsg -page = - Spa.Page.element - { title = always "{{moduleName}}" - , init = always init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -type alias Model = - {} - - -init : Params.{{fileName}} -> ( Model, Cmd Msg ) -init _ = - ( {} - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = Msg - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - ( model - , Cmd.none - ) - - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> {{ui}} Msg -view model = - text "{{moduleName}}" - - """ - |> String.replace "{{moduleName}}" (String.join "." options.modulePath) - |> String.replace "{{fileName}}" (options.modulePath |> List.reverse |> List.head |> Maybe.withDefault "YellAtRyanOnTheInternet") - |> String.replace "{{moduleFolder}}" (options.modulePath |> List.reverse |> List.drop 1 |> List.reverse |> List.map (String.append ".") |> String.concat) - |> String.replace "{{ui}}" options.ui - |> String.trim diff --git a/cli/src/elm/Templates/Layout.elm b/cli/src/elm/Templates/Layout.elm deleted file mode 100644 index edf794e..0000000 --- a/cli/src/elm/Templates/Layout.elm +++ /dev/null @@ -1,21 +0,0 @@ -module Templates.Layout exposing (contents) - - -contents : { modulePath : List String, ui : String } -> String -contents options = - """ -module Layouts.{{moduleName}} exposing (view) - -import {{ui}} exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> {{ui}} msg -view { page } = - page - - - """ - |> String.replace "{{moduleName}}" (String.join "." options.modulePath) - |> String.replace "{{ui}}" options.ui - |> String.trim diff --git a/cli/src/elm/Templates/Sandbox.elm b/cli/src/elm/Templates/Sandbox.elm deleted file mode 100644 index 6cd4522..0000000 --- a/cli/src/elm/Templates/Sandbox.elm +++ /dev/null @@ -1,64 +0,0 @@ -module Templates.Sandbox exposing (contents) - - -contents : { modulePath : List String, ui : String } -> String -contents options = - """ -module Pages.{{moduleName}} exposing (Model, Msg, page) - -import Spa.Page -import {{ui}} exposing (..) -import Generated{{moduleFolder}}.Params as Params -import Utils.Spa exposing (Page) - - -page : Page Params.{{fileName}} Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "{{moduleName}}" - , init = always init - , update = always update - , view = always view - } - - - --- INIT - - -type alias Model = - {} - - -init : Params.{{fileName}} -> Model -init _ = - {} - - - --- UPDATE - - -type Msg - = Msg - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> {{ui}} Msg -view model = - text "{{moduleName}}" - - """ - |> String.replace "{{moduleName}}" (String.join "." options.modulePath) - |> String.replace "{{fileName}}" (options.modulePath |> List.reverse |> List.head |> Maybe.withDefault "YellAtRyanOnTheInternet") - |> String.replace "{{moduleFolder}}" (options.modulePath |> List.reverse |> List.drop 1 |> List.reverse |> List.map (String.append ".") |> String.concat) - |> String.replace "{{ui}}" options.ui - |> String.trim diff --git a/cli/src/elm/Templates/Static.elm b/cli/src/elm/Templates/Static.elm deleted file mode 100644 index 1fcf445..0000000 --- a/cli/src/elm/Templates/Static.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Templates.Static exposing (contents) - - -contents : { modulePath : List String, ui : String } -> String -contents options = - """ -module Pages.{{moduleName}} exposing (Model, Msg, page) - -import Spa.Page -import {{ui}} exposing (..) -import Generated{{moduleFolder}}.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.{{fileName}} Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "{{moduleName}}" - , view = always view - } - - - --- VIEW - - -view : {{ui}} Msg -view = - text "{{moduleName}}" - - """ - |> String.replace "{{moduleName}}" (String.join "." options.modulePath) - |> String.replace "{{fileName}}" (options.modulePath |> List.reverse |> List.head |> Maybe.withDefault "YellAtRyanOnTheInternet") - |> String.replace "{{moduleFolder}}" (options.modulePath |> List.reverse |> List.drop 1 |> List.reverse |> List.map (String.append ".") |> String.concat) - |> String.replace "{{ui}}" options.ui - |> String.trim diff --git a/cli/src/elm/Utils.elm b/cli/src/elm/Utils.elm deleted file mode 100644 index b8b77a9..0000000 --- a/cli/src/elm/Utils.elm +++ /dev/null @@ -1,62 +0,0 @@ -module Utils exposing - ( Items - , addInMissingFolders - , allSubsets - ) - -import Dict exposing (Dict) -import List.Extra -import Set exposing (Set) - - -type alias Filepath = - List String - - -type alias Items = - { files : Set Filepath - , folders : Set Filepath - } - - -allSubsets : List (List comparable) -> List (List comparable) -allSubsets = - List.concatMap - (\list -> List.indexedMap (\i _ -> List.take (i + 1) list) list) - >> List.Extra.unique - - -addInMissingFolders : Dict String Items -> Dict String Items -addInMissingFolders dict = - let - keys : List String - keys = - Dict.keys dict - |> List.map (String.split ".") - |> allSubsets - |> List.map (String.join ".") - - splitOnDot : String -> Filepath - splitOnDot str = - if String.isEmpty str then - [] - - else - String.split "." str - - oneLongerThan : Filepath -> Set Filepath - oneLongerThan filepath = - keys - |> List.map splitOnDot - |> List.filter (\dictFilepath -> List.length dictFilepath == List.length filepath + 1) - |> Set.fromList - in - keys - |> List.map - (\key -> - Dict.get key dict - |> Maybe.withDefault (Items Set.empty Set.empty) - |> (\items -> { items | folders = Set.union items.folders (oneLongerThan (splitOnDot key)) }) - |> Tuple.pair key - ) - |> Dict.fromList diff --git a/cli/src/index.js b/cli/src/index.js deleted file mode 100755 index bb5be6a..0000000 --- a/cli/src/index.js +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env node -const package = require('../package.json') -const path = require('path') -const cwd = process.cwd() -const { File, Elm, bold } = require('./utils.js') - -const main = ([ command, ...args ] = []) => - commands[command] - ? commands[command](args) - : commands.help(args) - -// elm-spa init -const init = (args) => { - const parseInitArgs = (args) => { - const flagPrefix = '--' - const isFlag = arg => arg.indexOf(flagPrefix) == 0 - - const flags = - args.filter(isFlag) - .reduce((obj, arg) => { - const [ key, value ] = arg.split('=') - obj[key.substring(flagPrefix.length)] = value - return obj - }, {}) - - const [ relative = '.' ] = args.filter(arg => !isFlag(arg)) - - return { ui: flags.ui || 'Element', relative } - } - - const { ui, relative } = parseInitArgs(args) - - return Promise.resolve( - (ui === 'Element') - ? File.cp(path.join(__dirname, '..', 'initial-projects', 'elm-ui'), path.join(cwd, relative)) - : File.cp(path.join(__dirname, '..', 'initial-projects', 'html'), path.join(cwd, relative)) - ) - .then(_ => console.info(` -${bold('elm-spa')} created a new project in: - - ${path.join(cwd, relative)} - - run these commands to get started: - ${bold('cd ' + path.join(cwd, relative))} - ${bold('npm start')} -`)) - .catch(console.error) -} - -// elm-spa add -const add = args => - Elm.friendlyAddMessages(args) - .then(_ => { - const [ pageType, moduleName, relative = '.' ] = args - const dir = path.join(cwd, relative) - - return Elm.checkForElmSpaJson(dir) - .then(config => - File.paths(path.join(dir, 'src', 'Layouts')) - .then(layoutPaths => Elm.run('add', { relative })({ - pageType, - moduleName, - layoutPaths, - ui: config.ui - })) - .then(Elm.formatOutput) - ) - .then(str => `\n${str}\n`) - }) - .then(console.info) - .catch(console.error) - -// elm-spa build -const build = ([ relative = '.' ]) => { - const dir = path.join(cwd, relative) - - return Elm.checkForElmSpaJson(dir) - .then(json => - File.paths(path.join(dir, 'src', 'Pages')) - .then(Elm.run('build', { relative }, json['elm-spa'])) - .then(Elm.formatOutput) - ) - .then(str => `\n${str}\n`) - .then(console.info) - .catch(console.error) - } - -const version = - `${bold('elm-spa')} ${package.version}` - -// elm-spa help -const help = () => console.info(` -${version} - -usage: ${bold('elm-spa')} [...] - -commands: - - ${bold('init')} [options] create a new project at - - options: - ${bold('--ui=')} the ui module your \`view\` uses - (default: Element) - - examples: - ${bold('elm-spa init your-project')} - ${bold('elm-spa init --ui=Html your-project')} - - ${bold('build')} generate pages and routes - - examples: - ${bold('elm-spa build .')} - - ${bold('add')} static create a new static page - sandbox create a new sandbox page - element create a new element page - component create a new component page - - examples: - ${bold('elm-spa add static AboutUs')} - ${bold('elm-spa add element Settings.Index')} - - ${bold('help')} print this help screen - - examples: - ${bold('elm-spa help')} - ${bold('elm-spa wat')} -`) - -const commands = { - init, - add, - build, - help, - '-v': _ => console.info(version) -} - -main(process.argv.slice(2)) \ No newline at end of file diff --git a/cli/src/utils.js b/cli/src/utils.js deleted file mode 100644 index b04d5c5..0000000 --- a/cli/src/utils.js +++ /dev/null @@ -1,233 +0,0 @@ -const path = require('path') -const fs = require('fs') -const cwd = process.cwd() - -const File = (_ => { - const mkdir = (filepath) => - new Promise((resolve, reject) => - fs.mkdir(filepath, { recursive: true }, - (err) => err ? reject(err) : resolve(filepath) - ) - ) - - const read = (filepath) => - new Promise((resolve, reject) => - fs.readFile(filepath, (err, data) => - err ? reject(err) : resolve(data.toString('utf8')) - ) - ) - - const cp = (src, dest) => { - const exists = fs.existsSync(src) - const stats = exists && fs.statSync(src) - if (stats && stats.isDirectory()) { - fs.mkdirSync(dest) - fs.readdirSync(src).forEach(child => - cp(path.join(src, child), path.join(dest, child)) - ) - } else { - fs.copyFileSync(src, dest) - } - } - - const deleteFolderRecursive = (filepath) => { - if (fs.existsSync(filepath)) { - fs.readdirSync(filepath).forEach(file => { - const current = path.join(filepath, file) - if (fs.lstatSync(current).isDirectory()) { - deleteFolderRecursive(current) - } else { - fs.unlinkSync(current) - } - }) - fs.rmdirSync(filepath) - } - } - - const rmdir = (folder) => new Promise((resolve, reject) => { - try { - deleteFolderRecursive(folder) - resolve() - } catch (err) { - reject(err) - } - }) - - const create = (filepath, contents) => { - const folderOf = (path_) => - path_.split(path.sep).slice(0, -1).join(path.sep) - - 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('.')[1] == 'elm' - ? [[ 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 { - read, - paths, - mkdir, - rmdir, - cp, - create - } -})() - -const Elm = (_ => { - const { Elm } = require('../dist/elm.compiled.js') - - const handlers = { - error: (_, message) => - Promise.reject(message), - createFiles: ({ relative }, files) => - File.rmdir(path.join(cwd, relative, 'elm-stuff', '.elm-spa', 'Generated')) - .then(_ => - Promise.all( - files - .map(item => ({ - ...item, - filepath: path.join(cwd, relative, ...item.filepath) + '.elm' - })) - .map(item => File.create(item.filepath, item.contents)) - ) - ) - } - - const run = (command, args) => (data) => - new Promise((resolve, reject) => { - const app = Elm.Main.init({ flags: { command, data } }) - - app.ports.outgoing.subscribe(({ message, data }) => - handlers[message] - ? Promise.resolve(handlers[message](args, data)) - .then(resolve) - .catch(reject) - : reject(`Didn't recognize message "${message}"– yell at @ryannhg on the internet!\n`) - ) - }) - - const checkForElmSpaJson = (paths) => - new Promise((resolve, reject) => - fs.readFile(path.join(paths, 'elm-spa.json'), (_, contents) => - contents - ? Promise.resolve(contents.toString()) - .then(JSON.parse) - .then(resolve) - .catch(_ => `Couldn't understand the ${bold('elm-spa.json')} file at:\n${paths}`) - : reject(`Couldn't find an ${bold('elm-spa.json')} file at:\n${paths}`) - ) - ) - - const alphabetically = (a, b) => - (a < b) ? -1 : (a > b) ? 1 : 0 - - const formatOutput = files => [ - bold('elm-spa') + ` created ${bold(files.length)} file${files.length === 1 ? '' : 's'}:`, - files.sort(alphabetically).map(file => ' ' + file).join('\n'), - ].join('\n\n') - - const friendlyAddMessages = (args = []) => { - const [ page, moduleName, relative = '.' ] = args - - const expectedFiles = [ - path.join(cwd, relative, 'elm-spa.json'), - path.join(cwd, relative, 'src', 'Layouts') - ] - - if (expectedFiles.some(file => !fs.existsSync(file))) { - return Promise.reject(`\n I don't see an elm-spa project here...\n\n Please run this command in the directory with your ${bold('elm-spa.json')}\n`) - } - - const isValidPage = { - 'static': true, - 'sandbox': true, - 'element': true, - 'component': true - } - - const isValidModuleName = (name = '') => { - const isAlphaOnly = word => word.match(/[A-Z|a-z]+/)[0] === word - const isCapitalized = word => word[0].toUpperCase() === word[0] - return name && - name.length && - name.split('.').every(word => isAlphaOnly(word) && isCapitalized(word)) - } - - const messages = { - invalidPage: ({ page, name }) => ` - ${bold(page)} is not a valid page. - - Try one of these? - ${bold(Object.keys(isValidPage).map(page => `elm-spa add ${page} ${name}`).join('\n '))} - `, - invalidModuleName: ({ page, name }) => ` - ${bold(name)} doesn't look like an Elm module. - - Here are some examples of what I'm expecting: - ${bold(`elm-spa add ${page} Example`)} - ${bold(`elm-spa add ${page} Settings.User`)} - ` - } - - if (isValidPage[page] !== true) { - return Promise.reject(messages.invalidPage({ - page, - name: isValidModuleName(moduleName) ? moduleName : 'Example' - })) - } else if (isValidModuleName(moduleName) === false) { - return Promise.reject(messages.invalidModuleName({ page, name: moduleName })) - } else { - return Promise.resolve(args) - } - } - - return { run, checkForElmSpaJson, formatOutput, friendlyAddMessages } -})() - -const bold = str => '\033[1m' + str + '\033[0m' - -module.exports = { - Elm, - File, - bold -} diff --git a/cli/tests/Tests/Generators/Pages.elm b/cli/tests/Tests/Generators/Pages.elm new file mode 100644 index 0000000..7805e71 --- /dev/null +++ b/cli/tests/Tests/Generators/Pages.elm @@ -0,0 +1,233 @@ +module Tests.Generators.Pages exposing (suite) + +import Expect exposing (Expectation) +import Generators.Pages as Pages +import Path exposing (Path) +import Test exposing (..) + + +paths : + { empty : List Path + , single : List Path + , multiple : List Path + } +paths = + { empty = [] + , single = + [ Path.fromFilepath "Top.elm" + ] + , multiple = + [ Path.fromFilepath "Top.elm" + , Path.fromFilepath "About.elm" + , Path.fromFilepath "NotFound.elm" + , Path.fromFilepath "Posts/Top.elm" + , Path.fromFilepath "Posts/Dynamic.elm" + , Path.fromFilepath "Authors/Dynamic/Posts/Dynamic.elm" + ] + } + + +suite : Test +suite = + describe "Generators.Pages" + [ describe "pagesImports" + [ test "returns empty string when no paths" <| + \_ -> + paths.empty + |> Pages.pagesImports + |> Expect.equal "" + , test "returns single import for single path" <| + \_ -> + paths.single + |> Pages.pagesImports + |> Expect.equal "import Pages.Top" + , test "returns multiple import for multiple path" <| + \_ -> + paths.multiple + |> Pages.pagesImports + |> Expect.equal (String.trim """ +import Pages.Top +import Pages.About +import Pages.NotFound +import Pages.Posts.Top +import Pages.Posts.Dynamic +import Pages.Authors.Dynamic.Posts.Dynamic +""") + , describe "pagesCustomType" + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesCustomType "Model" + |> Expect.equal "type Model = Top_Model Pages.Top.Model" + , test "works with multiple paths" <| + \_ -> + paths.multiple + |> Pages.pagesCustomType "Model" + |> Expect.equal (String.trim """ +type Model + = Top_Model Pages.Top.Model + | About_Model Pages.About.Model + | NotFound_Model Pages.NotFound.Model + | Posts_Top_Model Pages.Posts.Top.Model + | Posts_Dynamic_Model Pages.Posts.Dynamic.Model + | Authors_Dynamic_Posts_Dynamic_Model Pages.Authors.Dynamic.Posts.Dynamic.Model +""") + ] + , describe "pagesUpgradedTypes" + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesUpgradedTypes + |> Expect.equal " { top : UpgradedPage Pages.Top.Flags Pages.Top.Model Pages.Top.Msg }" + , test "works with multiple paths" <| + \_ -> + paths.multiple + |> Pages.pagesUpgradedTypes + |> Expect.equal """ { top : UpgradedPage Pages.Top.Flags Pages.Top.Model Pages.Top.Msg + , about : UpgradedPage Pages.About.Flags Pages.About.Model Pages.About.Msg + , notFound : UpgradedPage Pages.NotFound.Flags Pages.NotFound.Model Pages.NotFound.Msg + , posts_top : UpgradedPage Pages.Posts.Top.Flags Pages.Posts.Top.Model Pages.Posts.Top.Msg + , posts_dynamic : UpgradedPage Pages.Posts.Dynamic.Flags Pages.Posts.Dynamic.Model Pages.Posts.Dynamic.Msg + , authors_dynamic_posts_dynamic : UpgradedPage Pages.Authors.Dynamic.Posts.Dynamic.Flags Pages.Authors.Dynamic.Posts.Dynamic.Model Pages.Authors.Dynamic.Posts.Dynamic.Msg + }""" + ] + , describe "pagesUpgradedValues" + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesUpgradedValues + |> Expect.equal " { top = Pages.Top.page |> Spa.upgrade Top_Model Top_Msg }" + , test "works with multiple paths" <| + \_ -> + paths.multiple + |> Pages.pagesUpgradedValues + |> Expect.equal """ { top = Pages.Top.page |> Spa.upgrade Top_Model Top_Msg + , about = Pages.About.page |> Spa.upgrade About_Model About_Msg + , notFound = Pages.NotFound.page |> Spa.upgrade NotFound_Model NotFound_Msg + , posts_top = Pages.Posts.Top.page |> Spa.upgrade Posts_Top_Model Posts_Top_Msg + , posts_dynamic = Pages.Posts.Dynamic.page |> Spa.upgrade Posts_Dynamic_Model Posts_Dynamic_Msg + , authors_dynamic_posts_dynamic = Pages.Authors.Dynamic.Posts.Dynamic.page |> Spa.upgrade Authors_Dynamic_Posts_Dynamic_Model Authors_Dynamic_Posts_Dynamic_Msg + }""" + ] + , describe "pagesInit" + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesInit + |> Expect.equal (String.trim """ +init : Route -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +init route = + case route of + Route.Top -> + pages.top.init () +""") + , test "works with multiple path" <| + \_ -> + paths.multiple + |> Pages.pagesInit + |> Expect.equal (String.trim """ +init : Route -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +init route = + case route of + Route.Top -> + pages.top.init () + + Route.About -> + pages.about.init () + + Route.NotFound -> + pages.notFound.init () + + Route.Posts_Top -> + pages.posts_top.init () + + Route.Posts_Dynamic params -> + pages.posts_dynamic.init params + + Route.Authors_Dynamic_Posts_Dynamic params -> + pages.authors_dynamic_posts_dynamic.init params +""") + ] + , describe "pagesUpdate" <| + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesUpdate + |> Expect.equal (String.trim """ +update : Msg -> Model -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +update bigMsg bigModel = + case ( bigMsg, bigModel ) of + ( Top_Msg msg, Top_Model model ) -> + pages.top.update msg model +""") + , test "works with multiple paths" <| + \_ -> + paths.multiple + |> Pages.pagesUpdate + |> Expect.equal (String.trim """ +update : Msg -> Model -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +update bigMsg bigModel = + case ( bigMsg, bigModel ) of + ( Top_Msg msg, Top_Model model ) -> + pages.top.update msg model + + ( About_Msg msg, About_Model model ) -> + pages.about.update msg model + + ( NotFound_Msg msg, NotFound_Model model ) -> + pages.notFound.update msg model + + ( Posts_Top_Msg msg, Posts_Top_Model model ) -> + pages.posts_top.update msg model + + ( Posts_Dynamic_Msg msg, Posts_Dynamic_Model model ) -> + pages.posts_dynamic.update msg model + + ( Authors_Dynamic_Posts_Dynamic_Msg msg, Authors_Dynamic_Posts_Dynamic_Model model ) -> + pages.authors_dynamic_posts_dynamic.update msg model + + _ -> + always ( bigModel, Cmd.none, Cmd.none ) +""") + ] + , describe "pagesBundle" <| + [ test "works with single path" <| + \_ -> + paths.single + |> Pages.pagesBundle + |> Expect.equal (String.trim """ +bundle : Model -> Global.Model -> Spa.Bundle Msg +bundle bigModel = + case bigModel of + Top_Model model -> + pages.top.bundle model +""") + , test "works with multiple paths" <| + \_ -> + paths.multiple + |> Pages.pagesBundle + |> Expect.equal (String.trim """ +bundle : Model -> Global.Model -> Spa.Bundle Msg +bundle bigModel = + case bigModel of + Top_Model model -> + pages.top.bundle model + + About_Model model -> + pages.about.bundle model + + NotFound_Model model -> + pages.notFound.bundle model + + Posts_Top_Model model -> + pages.posts_top.bundle model + + Posts_Dynamic_Model model -> + pages.posts_dynamic.bundle model + + Authors_Dynamic_Posts_Dynamic_Model model -> + pages.authors_dynamic_posts_dynamic.bundle model +""") + ] + ] + ] diff --git a/cli/tests/Tests/Generators/Route.elm b/cli/tests/Tests/Generators/Route.elm new file mode 100644 index 0000000..2873328 --- /dev/null +++ b/cli/tests/Tests/Generators/Route.elm @@ -0,0 +1,121 @@ +module Tests.Generators.Route exposing (suite) + +import Expect exposing (Expectation) +import Generators.Route as Route +import Path exposing (Path) +import Test exposing (..) + + +paths : + { empty : List Path + , single : List Path + , multiple : List Path + } +paths = + { empty = [] + , single = + [ Path.fromFilepath "Top.elm" + ] + , multiple = + [ Path.fromFilepath "Top.elm" + , Path.fromFilepath "About.elm" + , Path.fromFilepath "NotFound.elm" + , Path.fromFilepath "Posts/Top.elm" + , Path.fromFilepath "Posts/Dynamic.elm" + , Path.fromFilepath "Authors/Dynamic/Posts/Dynamic.elm" + ] + } + + +suite : Test +suite = + describe "Generators.Route" + [ describe "routeCustomType" + [ test "returns empty for missing variants" <| + \_ -> + paths.empty + |> Route.routeCustomType + |> Expect.equal "" + , test "handles single path" <| + \_ -> + paths.single + |> Route.routeCustomType + |> Expect.equal "type Route = Top" + , test "handles multiple paths" <| + \_ -> + paths.multiple + |> Route.routeCustomType + |> Expect.equal (String.trim """ +type Route + = Top + | About + | NotFound + | Posts_Top + | Posts_Dynamic { param1 : String } + | Authors_Dynamic_Posts_Dynamic { param1 : String, param2 : String } +""") + ] + , describe "routeParsers" + [ test "handles empty path" <| + \_ -> + paths.empty + |> Route.routeParsers + |> Expect.equal " []" + , test "handles single path" <| + \_ -> + paths.single + |> Route.routeParsers + |> Expect.equal " [ Parser.map Top Parser.top ]" + , test "handles multiple paths" <| + \_ -> + paths.multiple + |> Route.routeParsers + |> Expect.equal """ [ Parser.map Top Parser.top + , Parser.map About (Parser.s "about") + , Parser.map NotFound (Parser.s "not-found") + , Parser.map Posts_Top (Parser.s "posts") + , (Parser.s "posts" Parser.string) + |> Parser.map (\\param1 -> { param1 = param1 }) + |> Parser.map Posts_Dynamic + , (Parser.s "authors" Parser.string Parser.s "posts" Parser.string) + |> Parser.map (\\param1 param2 -> { param1 = param1, param2 = param2 }) + |> Parser.map Authors_Dynamic_Posts_Dynamic + ]""" + ] + , describe "routeSegments" + [ test "handles empty path" <| + \_ -> + paths.empty + |> Route.routeSegments + |> Expect.equal "" + , test "handles single path" <| + \_ -> + paths.single + |> Route.routeSegments + |> Expect.equal """ case route of + Top -> + []""" + , test "handles multiple paths" <| + \_ -> + paths.multiple + |> Route.routeSegments + |> Expect.equal """ case route of + Top -> + [] + + About -> + [ "about" ] + + NotFound -> + [ "not-found" ] + + Posts_Top -> + [ "posts" ] + + Posts_Dynamic { param1 } -> + [ "posts", param1 ] + + Authors_Dynamic_Posts_Dynamic { param1, param2 } -> + [ "authors", param1, "posts", param2 ]""" + ] + ] diff --git a/cli/tests/Tests/Path.elm b/cli/tests/Tests/Path.elm new file mode 100644 index 0000000..d498b7e --- /dev/null +++ b/cli/tests/Tests/Path.elm @@ -0,0 +1,183 @@ +module Tests.Path exposing (suite) + +import Expect exposing (Expectation) +import Path exposing (Path) +import Test exposing (..) + + +suite : Test +suite = + describe "Path" + [ describe "fromFilepath" + [ test "ignores first slash" <| + \_ -> + "/Top.elm" + |> Path.fromFilepath + |> Path.toList + |> Expect.equalLists [ "Top" ] + , test "works without folders" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toList + |> Expect.equalLists [ "Top" ] + , test "works with a single folder" <| + \_ -> + "Posts/Top.elm" + |> Path.fromFilepath + |> Path.toList + |> Expect.equalLists [ "Posts", "Top" ] + , test "works with nested folders" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toList + |> Expect.equalLists [ "Authors", "Dynamic", "Posts", "Dynamic" ] + ] + , describe "toModulePath" + [ test "works without folders" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toModulePath + |> Expect.equal "Top" + , test "works with a single folder" <| + \_ -> + "Posts/Top.elm" + |> Path.fromFilepath + |> Path.toModulePath + |> Expect.equal "Posts.Top" + , test "works with nested folders" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toModulePath + |> Expect.equal "Authors.Dynamic.Posts.Dynamic" + ] + , describe "toVariableName" + [ test "works without folders" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toVariableName + |> Expect.equal "top" + , test "works correctly with capital letters" <| + \_ -> + "NotFound.elm" + |> Path.fromFilepath + |> Path.toVariableName + |> Expect.equal "notFound" + , test "works with a single folder" <| + \_ -> + "Posts/Top.elm" + |> Path.fromFilepath + |> Path.toVariableName + |> Expect.equal "posts_top" + , test "works with nested folders" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toVariableName + |> Expect.equal "authors_dynamic_posts_dynamic" + ] + , describe "toTypeName" + [ test "works without folders" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toTypeName + |> Expect.equal "Top" + , test "works with a single folder" <| + \_ -> + "Posts/Top.elm" + |> Path.fromFilepath + |> Path.toTypeName + |> Expect.equal "Posts_Top" + , test "works with nested folders" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toTypeName + |> Expect.equal "Authors_Dynamic_Posts_Dynamic" + ] + , describe "optionalParams" + [ test "works without folders" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.optionalParams + |> Expect.equal "" + , test "works with a single folder" <| + \_ -> + "Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.optionalParams + |> Expect.equal " { param1 : String }" + , test "works with nested folders" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.optionalParams + |> Expect.equal " { param1 : String, param2 : String }" + ] + , describe "toParser" + [ test "works with top" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toParser + |> Expect.equal "Parser.map Top Parser.top" + , test "works with single static path" <| + \_ -> + "About.elm" + |> Path.fromFilepath + |> Path.toParser + |> Expect.equal "Parser.map About (Parser.s \"about\")" + , test "works with multiple static paths" <| + \_ -> + "About/Team.elm" + |> Path.fromFilepath + |> Path.toParser + |> Expect.equal "Parser.map About_Team (Parser.s \"about\" Parser.s \"team\")" + , test "works with single dynamic path" <| + \_ -> + "Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toParser + |> Expect.equal (String.trim """ +(Parser.s "posts" Parser.string) + |> Parser.map (\\param1 -> { param1 = param1 }) + |> Parser.map Posts_Dynamic +""") + , test "works with multiple dynamic paths" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toParser + |> Expect.equal (String.trim """ +(Parser.s "authors" Parser.string Parser.s "posts" Parser.string) + |> Parser.map (\\param1 param2 -> { param1 = param1, param2 = param2 }) + |> Parser.map Authors_Dynamic_Posts_Dynamic +""") + , describe "toFlags" + [ test "works with no dynamic params" <| + \_ -> + "Top.elm" + |> Path.fromFilepath + |> Path.toFlags + |> Expect.equal "()" + , test "works with one dynamic param" <| + \_ -> + "Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toFlags + |> Expect.equal "{ param1 : String }" + , test "works with multiple dynamic params" <| + \_ -> + "Authors/Dynamic/Posts/Dynamic.elm" + |> Path.fromFilepath + |> Path.toFlags + |> Expect.equal "{ param1 : String, param2 : String }" + ] + ] + ] diff --git a/cli/tests/Tests/Utils/Generate.elm b/cli/tests/Tests/Utils/Generate.elm new file mode 100644 index 0000000..38ed9e5 --- /dev/null +++ b/cli/tests/Tests/Utils/Generate.elm @@ -0,0 +1,274 @@ +module Tests.Utils.Generate exposing (suite) + +import Expect exposing (Expectation) +import Test exposing (..) +import Utils.Generate as Generate + + +suite : Test +suite = + describe "Utils.Generate" + [ describe "indent" + [ test "indents with four spaces" <| + \_ -> + "abc" + |> Generate.indent 1 + |> Expect.equal " abc" + , test "supports indenting twice" <| + \_ -> + "abc" + |> Generate.indent 2 + |> Expect.equal " abc" + , test "works with multiple lines" <| + \_ -> + "abc\ndef" + |> Generate.indent 1 + |> Expect.equal " abc\n def" + ] + , describe "customType" + [ test "returns empty string with no variants" <| + \_ -> + { name = "Fruit" + , variants = [] + } + |> Generate.customType + |> Expect.equal "" + , test "returns single line for single variant" <| + \_ -> + { name = "Fruit" + , variants = [ "Apple" ] + } + |> Generate.customType + |> Expect.equal "type Fruit = Apple" + , test "returns multiple lines for multiple variants" <| + \_ -> + { name = "Fruit" + , variants = + [ "Apple" + , "Banana" + , "Cherry" + ] + } + |> Generate.customType + |> Expect.equal (String.trim """ +type Fruit + = Apple + | Banana + | Cherry +""") + , describe "import_" + [ test "works with only a name" <| + \_ -> + { name = "Url" + , alias = Nothing + , exposing_ = [] + } + |> Generate.import_ + |> Expect.equal "import Url" + , test "works with an alias" <| + \_ -> + { name = "Data.User" + , alias = Just "User" + , exposing_ = [] + } + |> Generate.import_ + |> Expect.equal "import Data.User as User" + , test "works with a name and exposing" <| + \_ -> + { name = "Url" + , alias = Nothing + , exposing_ = [ "Url" ] + } + |> Generate.import_ + |> Expect.equal "import Url exposing (Url)" + , test "works with an alias and exposing" <| + \_ -> + { name = "Data.User" + , alias = Just "User" + , exposing_ = [ "User" ] + } + |> Generate.import_ + |> Expect.equal "import Data.User as User exposing (User)" + , test "works with an alias and mutiple exposing items" <| + \_ -> + { name = "Css.Html" + , alias = Just "Html" + , exposing_ = [ "Html", "div" ] + } + |> Generate.import_ + |> Expect.equal "import Css.Html as Html exposing (Html, div)" + ] + , describe "recordType" + [ test "has empty record when given no properties" <| + \_ -> + [] + |> Generate.recordType + |> Expect.equal "{}" + , test "has single-line record when given one property" <| + \_ -> + [ ( "name", "String" ) ] + |> Generate.recordType + |> Expect.equal "{ name : String }" + , test "has multi-line record when given multiple property" <| + \_ -> + [ ( "name", "String" ) + , ( "age", "Int" ) + ] + |> Generate.recordType + |> Expect.equal (String.trim """ +{ name : String +, age : Int +} +""") + ] + , describe "recordValue" + [ test "has empty record when given no properties" <| + \_ -> + [] + |> Generate.recordValue + |> Expect.equal "{}" + , test "has single-line record when given one property" <| + \_ -> + [ ( "name", "\"Ryan\"" ) ] + |> Generate.recordValue + |> Expect.equal "{ name = \"Ryan\" }" + , test "has multi-line record when given multiple property" <| + \_ -> + [ ( "name", "\"Ryan\"" ) + , ( "age", "123" ) + ] + |> Generate.recordValue + |> Expect.equal (String.trim """ +{ name = "Ryan" +, age = 123 +} +""") + ] + , describe "tuple" + [ test "has empty tuple when given no properties" <| + \_ -> + [] + |> Generate.tuple + |> Expect.equal "()" + , test "has single-line tuple when given one property" <| + \_ -> + [ "123" ] + |> Generate.tuple + |> Expect.equal "( 123 )" + , test "has multi-line tuple when given multiple property" <| + \_ -> + [ "123" + , "456" + ] + |> Generate.tuple + |> Expect.equal (String.trim """ +( 123 +, 456 +) +""") + ] + , describe "list" + [ test "has empty list when given no properties" <| + \_ -> + [] + |> Generate.list + |> Expect.equal "[]" + , test "has single-line list when given one property" <| + \_ -> + [ "123" ] + |> Generate.list + |> Expect.equal "[ 123 ]" + , test "has multi-line list when given multiple property" <| + \_ -> + [ "123" + , "456" + ] + |> Generate.list + |> Expect.equal (String.trim """ +[ 123 +, 456 +] +""") + ] + , describe "function" + [ test "returns blank string without annotation" <| + \_ -> + { name = "name" + , annotation = [] + , inputs = [] + , body = "\"Ryan\"" + } + |> Generate.function + |> Expect.equal "" + , test "works with no inputs" <| + \_ -> + { name = "name" + , annotation = [ "String" ] + , inputs = [] + , body = "\"Ryan\"" + } + |> Generate.function + |> Expect.equal (String.trim """ +name : String +name = + "Ryan" +""") + , test "works with one input" <| + \_ -> + { name = "length" + , annotation = [ "String", "Int" ] + , inputs = [ "name" ] + , body = "String.length name" + } + |> Generate.function + |> Expect.equal (String.trim """ +length : String -> Int +length name = + String.length name +""") + , test "works with multiple input" <| + \_ -> + { name = "fullname" + , annotation = [ "String", "String", "String" ] + , inputs = [ "first", "last" ] + , body = "first ++ \" \" ++ last" + } + |> Generate.function + |> Expect.equal (String.trim """ +fullname : String -> String -> String +fullname first last = + first ++ " " ++ last +""") + ] + , describe "caseExpression" + [ test "returns empty string with missing conditionals" <| + \_ -> + { variable = "route" + , cases = [] + } + |> Generate.caseExpression + |> Expect.equal "" + , test "works with multiple conditions" <| + \_ -> + { variable = "route" + , cases = + [ ( "Top", "\"/\"" ) + , ( "About", "\"/about\"" ) + , ( "NotFound", "\"/not-found\"" ) + ] + } + |> Generate.caseExpression + |> Expect.equal (String.trim """ +case route of + Top -> + "/" + + About -> + "/about" + + NotFound -> + "/not-found" +""") + ] + ] + ] diff --git a/cli/tests/UtilsTest.elm b/cli/tests/UtilsTest.elm deleted file mode 100644 index a52b3e7..0000000 --- a/cli/tests/UtilsTest.elm +++ /dev/null @@ -1,84 +0,0 @@ -module UtilsTest exposing (expected, input, suite) - -import Dict exposing (Dict) -import Expect exposing (Expectation) -import Fuzz exposing (Fuzzer, int, list, string) -import Set exposing (Set) -import Test exposing (..) -import Utils exposing (Items) - - -suite : Test -suite = - describe "Utils" - [ describe "allSubsets" - [ test "works with authors posts example" <| - \_ -> - Utils.allSubsets - [ [ "Top" ] - , [ "NotFound" ] - , [ "Authors", "Dynamic", "Posts", "Dynamic" ] - ] - |> Expect.equalLists - [ [ "Top" ] - , [ "NotFound" ] - , [ "Authors" ] - , [ "Authors", "Dynamic" ] - , [ "Authors", "Dynamic", "Posts" ] - , [ "Authors", "Dynamic", "Posts", "Dynamic" ] - ] - ] - , describe "addInMissingFolders" - [ test "works with authors post example" <| - \_ -> - Utils.addInMissingFolders input - |> Expect.equalDicts expected - ] - ] - - -input : Dict String Items -input = - Dict.fromList - [ ( "" - , { files = Set.fromList [ [ "NotFound" ], [ "Top" ] ] - , folders = Set.fromList [] - } - ) - , ( "Authors.Dynamic" - , { files = Set.fromList [] - , folders = Set.fromList [ [ "Authors", "Dynamic", "Posts" ] ] - } - ) - , ( "Authors.Dynamic.Posts" - , { files = Set.fromList [ [ "Authors", "Dynamic", "Posts", "Dynamic" ] ] - , folders = Set.fromList [] - } - ) - ] - - -expected : Dict String Items -expected = - Dict.fromList - [ ( "" - , { files = Set.fromList [ [ "NotFound" ], [ "Top" ] ] - , folders = Set.fromList [ [ "Authors" ] ] - } - ) - , ( "Authors" - , { files = Set.fromList [] - , folders = Set.fromList [ [ "Authors", "Dynamic" ] ] - } - ) - , ( "Authors.Dynamic" - , { files = Set.fromList [] - , folders = Set.fromList [ [ "Authors", "Dynamic", "Posts" ] ] - } - ) - , ( "Authors.Dynamic.Posts" - , { files = Set.fromList [ [ "Authors", "Dynamic", "Posts", "Dynamic" ] ] - , folders = Set.fromList [] - } - ) - ] diff --git a/docs.json b/docs.json new file mode 100644 index 0000000..446b739 --- /dev/null +++ b/docs.json @@ -0,0 +1 @@ +[{"name":"Spa","comment":" When you create an app with the [elm/browser](/packages/elm/browser/latest) package,\nyou can build anything from a static `Html msg` page to a fully-fledged web `Browser.application`.\n\n`elm-spa` uses the existing design at the page-level, so you can quickly add new pages to your Elm application!\n\n\n## the four kinds of pages:\n\n1. [static](#static-pages) – a page that only renders HTML.\n2. [sandbox](#sandbox-pages) – a page with state.\n3. [element](#element-pages) – a page with side-effects.\n4. [component](#component-pages) – a page with global state.\n\n\n# static pages\n\nJust like [Elm's intro \"Hello!\" example](https://elm-lang.org/examples/hello),\nsometimes you just want a page that renders some HTML, without having any state.\n\n@docs static\n\n**Note:** Static pages don't store data, so Model is always an empty tuple: ()\n\n\n# sandbox pages\n\nWhen you're ready to keep track of state, like in\n[Elm's \"Counter\" example](https://elm-lang.org/examples/buttons),\nyou can use a sandbox page.\n\nSimilar to `Browser.sandbox`, this allows you to init your model, and update it with messages!\n\n@docs sandbox\n\n\n# element pages\n\nIf you're ready to [send HTTP requests](https://elm-lang.org/examples/cat-gifs) or [listen to events](https://elm-lang.org/examples/time) for updates, it's time to upgrade to an element page!\n\nAdditionally, an element will give you access to Flags, so you can access things like URL parameters.\n\n@docs element\n\n**New to Cmd or Sub?** I recommend checking out Elm's official guide , that's a great place to wrap your head around these two concepts.\n\n\n# component pages\n\nIf you need to access shared state across all pages, or need to send a global commands like signing in/signing out a user, a component page is what you'll need.\n\nComponent pages gain access to the `Global.Model`, and return an additional `Cmd Global.Msg` so you can read/update the global state of your application.\n\n@docs component\n\n**Cool trick:** Don't need access to Global.Model in all of your functions? Use always to skip the first argument.\n\nIn the following example, only the view function receives Global.Model:\n\n import Global\n import Spa exposing (Page)\n\n page : Page Flags Model Msg Global.Model Global.Msg\n page =\n Spa.component\n { init = always init\n , update = always update\n , view = view -- not using \"always\"!\n , subscriptions = always subscriptions\n }\n\n\n# putting pages together\n\n**Note:** The [elm-spa cli tool](https://www.npmjs.com/package/elm-spa) will generate all the upcoming code\nfor you. You can continue reading to understand what it's doing under the hood!\n\n@docs Page\n\n\n## learning with an example\n\nLet's imagine we are building a website for a restaurant! This website has the following routes:\n\n1. `/home` - the homepage\n2. `/menu` - the menu\n3. `/faqs` - frequently asked questions\n\nWith `elm-spa` we'd create a module for each page, and import them together in one file.\n\n module Pages exposing (Model, Msg, init, update, view, subscriptions)\n\n import Pages.Home\n import Pages.Menu\n import Pages.Faqs\n\nIf we want to implement the `Pages.init` function, we'll need to return the same type of value. This means we need to make a shared model and a shared msg type to handle all the different pages in our application:\n\n type Model\n = Home_Model Pages.Home.Model\n | Menu_Model Pages.Menu.Model\n | Faqs_Model Pages.Faqs.Model\n\n type Msg\n = Home_Msg Pages.Home.Msg\n | Menu_Msg Pages.Menu.Msg\n | Faqs_Msg Pages.Faqs.Msg\n\nThese two types allow `init` to return `( Model, Cmd Msg, Cmd Global.Msg )` for any page that the user is looking at!\n\n\n## upgrading pages\n\nWith the custom types above:\n\n1. `Home_Model` can upgrade a `Pages.Home.Model` to a `Model`\n2. `Home_Msg` can upgrade a `Pages.Home.Msg` to a `Msg`\n\nWe use the upgrade function to return a record that makes writing the combined `init`/`update`/`view`/`subscriptions` functions clean and easy!\n\n@docs upgrade\n\n import Pages.Faqs\n import Pages.Home\n import Pages.Menu\n import Spa\n\n type alias UpgradedPage pageFlags pageModel pageMsg =\n { init : pageFlags -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg )\n , update : pageMsg -> pageModel -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg )\n , bundle : pageModel -> Global.Model -> Spa.Bundle Msg\n }\n\n type alias UpgradedPages =\n { home : UpgradedPage Pages.Home.Flags Pages.Home.Model Pages.Home.Msg\n , menu : UpgradedPage Pages.Menu.Flags Pages.Menu.Model Pages.Menu.Msg\n , faqs : UpgradedPage Pages.Faqs.Flags Pages.Faqs.Model Pages.Faqs.Msg\n }\n\n pages : UpgradedPages\n pages =\n { home = Pages.Home.page |> Spa.upgrade Home_Model Home_Msg\n , menu = Pages.Menu.page |> Spa.upgrade Menu_Model Menu_Msg\n , faqs = Pages.Faqs.page |> Spa.upgrade Faqs_Model Faqs_Msg\n }\n\nNow when we write `init`, we can use the upgraded `pages` variable to keep things easy to read.\n\n\n## implementing `init`\n\n import Route exposing (Route)\n\n init : Route -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg )\n init route =\n case route of\n Route.Home ->\n pages.home.init ()\n\n Route.Menu ->\n pages.menu.init ()\n\n Route.Faqs ->\n pages.faqs.init ()\n\n\n## implementing `update`\n\n update : Msg -> Model -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg )\n update bigMsg bigModel =\n case ( bigMsg, bigModel ) of\n ( Home_Msg msg, Home_Model model ) ->\n pages.home.update msg model\n\n ( Menu_Msg msg, Menu_Model model ) ->\n pages.menu.update msg model\n\n ( Faqs_Msg msg, Faqs_Model model ) ->\n pages.faqs.update msg model\n\n _ ->\n always ( bigModel, Cmd.none, Cmd.none )\n\n\n## implementing `bundle`\n\nWith `elm-spa`, we don't need to write out a case expression for both `view` and `subscriptions`.\nWe can just write one that bundles them together.\n\nWe can create `view` and `subscriptions` from this single function:\n\n@docs Bundle\n\n\n# that's it!\n\nYou can check out or join #elm-spa-users on the official Elm slack channel for any questions ❤️\n\n","unions":[],"aliases":[{"name":"Bundle","comment":"\n\n import Spa\n\n bundle : Model -> Global.Model -> Spa.Bundle Msg\n bundle bigModel =\n case bigModel of\n Home_Model model ->\n pages.home.bundle model\n\n Menu_Model model ->\n pages.menu.bundle model\n\n Faqs_Model model ->\n pages.faqs.bundle model\n\n view : Model -> Global.Model -> Document Msg\n view model =\n bundle model >> .view\n\n subscriptions : Model -> Global.Model -> Sub Msg\n subscriptions model =\n bundle model >> .subscriptions\n\n","args":["msg"],"type":"{ view : Browser.Document msg, subscriptions : Platform.Sub.Sub msg }"},{"name":"Page","comment":" What was the point of using the functions above? They all return the `Page` type,\nand we can use the `Spa.upgrade` function on any of them!\n\n(The following example will illustrate why that's a good thing)\n\n","args":["flags","model","msg","globalModel","globalMsg"],"type":"{ init : globalModel -> flags -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), update : globalModel -> msg -> model -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), view : globalModel -> model -> Browser.Document msg, subscriptions : globalModel -> model -> Platform.Sub.Sub msg }"}],"values":[{"name":"component","comment":"\n\n import Global\n import Spa exposing (Page)\n\n page : Page Flags Model Msg Global.Model Global.Msg\n page =\n Spa.component\n { init = init\n , update = update\n , view = view\n , subscriptions = subscriptions\n }\n\n","type":"{ init : globalModel -> flags -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), update : globalModel -> msg -> model -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), view : globalModel -> model -> Browser.Document msg, subscriptions : globalModel -> model -> Platform.Sub.Sub msg } -> Spa.Page flags model msg globalModel globalMsg"},{"name":"element","comment":"\n\n page : Page Flags Model Msg globalModel globalMsg\n page =\n Spa.element\n { init = init\n , update = update\n , view = view\n , subscriptions = subscriptions\n }\n\n","type":"{ init : flags -> ( model, Platform.Cmd.Cmd msg ), update : msg -> model -> ( model, Platform.Cmd.Cmd msg ), view : model -> Browser.Document msg, subscriptions : model -> Platform.Sub.Sub msg } -> Spa.Page flags model msg globalModel globalMsg"},{"name":"sandbox","comment":"\n\n import Spa exposing (Page)\n\n page : Page Flags Model Msg globalModel globalMsg\n page =\n Spa.sandbox\n { init = init\n , update = update\n , view = view\n }\n\n","type":"{ init : model, update : msg -> model -> model, view : model -> Browser.Document msg } -> Spa.Page flags model msg globalModel globalMsg"},{"name":"static","comment":"\n\n page : Page Flags Model Msg globalModel globalMsg\n page =\n Spa.static\n { view = view\n }\n\n","type":"{ view : Browser.Document msg } -> Spa.Page flags () msg globalModel globalMsg"},{"name":"upgrade","comment":" For each page we export from our `Pages.*` modules, we should call the `upgrade` function with the corresponding `Model` and `Msg` variants, like this:\n\n pages : UpgradedPages\n pages =\n { home = Pages.Home.page |> Spa.upgrade Home_Model Home_Msg\n , menu = Pages.Menu.page |> Spa.upgrade Menu_Model Menu_Msg\n , faqs = Pages.Faqs.page |> Spa.upgrade Faqs_Model Faqs_Msg\n }\n\n","type":"(pageModel -> model) -> (pageMsg -> msg) -> Spa.Page pageFlags pageModel pageMsg globalModel globalMsg -> { init : pageFlags -> globalModel -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), update : pageMsg -> pageModel -> globalModel -> ( model, Platform.Cmd.Cmd msg, Platform.Cmd.Cmd globalMsg ), bundle : pageModel -> globalModel -> Spa.Bundle msg }"}],"binops":[]}] \ No newline at end of file diff --git a/elm-analyse.json b/elm-analyse.json deleted file mode 100644 index 9794de7..0000000 --- a/elm-analyse.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "checks" : { - "SingleFieldRecord": false, - "ImportAll": false - } -} \ No newline at end of file diff --git a/elm.json b/elm.json index 1b1b973..4775c7b 100644 --- a/elm.json +++ b/elm.json @@ -1,23 +1,18 @@ { "type": "package", "name": "ryannhg/elm-spa", - "summary": "single page apps made easy", + "summary": "a way to build single page apps with Elm", "license": "BSD-3-Clause", - "version": "3.0.2", + "version": "4.0.0", "exposed-modules": [ - "Spa", - "Spa.Page", - "Spa.Types", - "Spa.Transition", - "Spa.Path" + "Spa" ], "elm-version": "0.19.0 <= v < 0.20.0", "dependencies": { "elm/browser": "1.0.0 <= v < 2.0.0", "elm/core": "1.0.0 <= v < 2.0.0", "elm/html": "1.0.0 <= v < 2.0.0", - "elm/url": "1.0.0 <= v < 2.0.0", - "mdgriffith/elm-ui": "1.1.5 <= v < 2.0.0" + "elm/url": "1.0.0 <= v < 2.0.0" }, "test-dependencies": {} -} +} \ No newline at end of file diff --git a/examples/docs/elm.json b/example/elm.json similarity index 55% rename from examples/docs/elm.json rename to example/elm.json index 09e64ed..3380e39 100644 --- a/examples/docs/elm.json +++ b/example/elm.json @@ -2,25 +2,18 @@ "type": "application", "source-directories": [ "src", - "elm-stuff/.elm-spa" + "../src" ], "elm-version": "0.19.1", "dependencies": { "direct": { "elm/browser": "1.0.2", - "elm/core": "1.0.4", + "elm/core": "1.0.5", "elm/html": "1.0.0", - "elm/http": "2.0.0", - "elm/json": "1.1.3", - "elm/parser": "1.1.0", - "elm/url": "1.0.0", - "elm-explorations/markdown": "1.0.0", - "mdgriffith/elm-ui": "1.1.5", - "ryannhg/elm-spa": "3.0.0" + "elm/url": "1.0.0" }, "indirect": { - "elm/bytes": "1.0.8", - "elm/file": "1.0.5", + "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/virtual-dom": "1.0.2" } diff --git a/cli/initial-projects/elm-ui/package-lock.json b/example/package-lock.json similarity index 92% rename from cli/initial-projects/elm-ui/package-lock.json rename to example/package-lock.json index 7d862df..744de1c 100644 --- a/cli/initial-projects/elm-ui/package-lock.json +++ b/example/package-lock.json @@ -1,16 +1,16 @@ { - "name": "my-elm-spa-project", - "version": "1.0.0", + "name": "elm-spa-example", + "version": "4.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==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -72,9 +72,9 @@ "dev": true }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, "bcrypt-pbkdf": { @@ -161,19 +161,28 @@ }, "dependencies": { "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.1", + "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.3.0" + } + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" } } } @@ -343,15 +352,15 @@ } }, "elm-hot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.1.tgz", - "integrity": "sha512-ZHjoHd2Ev6riNXNQirj3J+GKKXXwedAUikfFBYzlVL/+3CdGs96cpZ7nhAk4c5l//Qa9ymltrqX36mOlr0pPFA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.4.tgz", + "integrity": "sha512-qPDP/o/Fkifriaxaf3E7hHFB5L6Ijihyg8is4A6xna6/h/zebUiNssbQrxywI2oxNUkr6W/leEu/WlIC1tmVnw==", "dev": true }, "elm-live": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-4.0.1.tgz", - "integrity": "sha512-IlonaC1pO/QoXlOrwwrJaxyvpJAT8QDSfzenkChbhU1PC1fJetkj2TwZfki+y1ZxpSMTnMSomMraOdWA6DO3VQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-4.0.2.tgz", + "integrity": "sha512-4I3UvJxF6MubC14VsgtV11B0zBxaaKtdKKsWquoaa5a3UHBIGW83qgTnt/NxOj4omOLfupaftmDaE4yRMTgTcw==", "dev": true, "requires": { "chalk": "^1.1.1", @@ -359,7 +368,7 @@ "commander": "2.17.1", "crocks": "0.12.1", "cross-spawn": "5.0.1", - "elm-hot": "1.1.1", + "elm-hot": "1.1.4", "finalhandler": "1.1.2", "http-proxy": "1.17.0", "internal-ip": "4.3.0", @@ -392,9 +401,9 @@ } }, "es6-promisify": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz", - "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.1.0.tgz", + "integrity": "sha512-jCsk2fpfEFusVv1MDkF4Uf0hAzIKNDMgR6LyOIw6a3jwkN1sCgWzuwgnsHY9YSQ8n8P31HoncvE0LC44cpWTrw==", "dev": true }, "escape-html": { @@ -464,15 +473,15 @@ "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=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "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=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fill-range": { @@ -509,9 +518,9 @@ } }, "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.10.0.tgz", + "integrity": "sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ==", "dev": true, "requires": { "debug": "^3.0.0" @@ -843,18 +852,18 @@ "dev": true }, "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", "dev": true }, "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "dev": true, "requires": { - "mime-db": "1.42.0" + "mime-db": "1.43.0" } }, "ms": { @@ -930,9 +939,9 @@ "dev": true }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -990,9 +999,9 @@ "dev": true }, "picomatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", - "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, "pseudomap": { @@ -1002,9 +1011,9 @@ "dev": true }, "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", "dev": true }, "pump": { @@ -1036,18 +1045,18 @@ "dev": true }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", "dev": true, "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.2.1" } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -1057,7 +1066,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -1067,7 +1076,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" } @@ -1276,21 +1285,13 @@ "dev": true }, "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==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "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 - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, "tunnel-agent": { @@ -1324,9 +1325,9 @@ } }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, "verror": { @@ -1420,9 +1421,9 @@ "dev": true }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -1434,13 +1435,13 @@ "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..6e7d92b --- /dev/null +++ b/example/package.json @@ -0,0 +1,23 @@ +{ + "name": "elm-spa-example", + "version": "4.0.0", + "description": "single page apps made easy", + "main": "index.js", + "scripts": { + "start": "npm install && npm run build && npm run dev", + "build": "npm run build:elm-spa && npm run build:elm", + "build:elm-spa": "elm-spa build .", + "build:elm": "elm make src/Main.elm --optimize --output public/dist/elm.js", + "dev": "npm run dev:elm-spa & npm run dev:elm", + "dev:elm-spa": "chokidar src/Pages -c \"npm run build:elm-spa\"", + "dev:elm": "elm-live src/Main.elm -u -d public -- --debug --output public/dist/elm.js" + }, + "keywords": [], + "author": "Ryan Haskell-Glatz", + "license": "ISC", + "devDependencies": { + "chokidar-cli": "2.1.0", + "elm": "0.19.1-3", + "elm-live": "4.0.2" + } +} diff --git a/example/public/index.html b/example/public/index.html new file mode 100644 index 0000000..ca4b1b3 --- /dev/null +++ b/example/public/index.html @@ -0,0 +1,16 @@ + + + + + + Elm Application + + + + + + + + \ No newline at end of file diff --git a/example/public/main.css b/example/public/main.css new file mode 100644 index 0000000..2c53dac --- /dev/null +++ b/example/public/main.css @@ -0,0 +1,32 @@ +.fixed--full { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.absolute--full { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.absolute--center { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.bg--overlay { + background: rgba(0,0,0,0.25); +} +.min-width--480 { + min-width: 320px; +} +.bg--white { + background: white; +} \ No newline at end of file diff --git a/example/src/Components.elm b/example/src/Components.elm new file mode 100644 index 0000000..f510814 --- /dev/null +++ b/example/src/Components.elm @@ -0,0 +1,172 @@ +module Components exposing + ( footer + , layout + , navbar + ) + +import Browser exposing (Document) +import Data.Modal as Modal exposing (Modal) +import Data.SignInForm exposing (SignInForm) +import Data.User as User exposing (User) +import Html exposing (..) +import Html.Attributes as Attr exposing (class, href) +import Html.Events as Events +import Generated.Route as Route + + + +-- LAYOUT + + +layout : + { page : Document msg + , global : + { global + | modal : Maybe Modal + , user : Maybe User + } + , actions : + { onSignOut : msg + , openSignInModal : msg + , closeModal : msg + , attemptSignIn : msg + , onSignInEmailInput : String -> msg + , onSignInPasswordInput : String -> msg + } + } + -> Document msg +layout { page, actions, global } = + { title = page.title + , body = + [ div [ class "container pad--medium column spacing--large h--fill" ] + [ navbar { user = global.user, actions = actions } + , div [ class "column spacing--large", Attr.style "flex" "1 0 auto" ] page.body + , footer + , global.modal + |> Maybe.map (viewModal actions) + |> Maybe.withDefault (text "") + ] + ] + } + + + +-- NAVBAR + + +navbar : + { user : Maybe User + , actions : { actions | openSignInModal : msg, onSignOut : msg } + } + -> Html msg +navbar ({ actions } as options) = + header [ class "container" ] + [ div [ class "row spacing--between center-y" ] + [ a [ class "link font--h5 font--bold", href "/" ] [ text "home" ] + , div [ class "row spacing--medium center-y" ] + [ a [ class "link", href "/about" ] [ text "about" ] + , a [ class "link", href "/posts" ] [ text "posts" ] + , case options.user of + Just user -> + a [ class "link", href (Route.toHref Route.Profile) ] [ text "profile" ] + + Nothing -> + button [ class "button", Events.onClick actions.openSignInModal ] [ text "sign in" ] + ] + ] + ] + + + +-- FOOTER + + +footer : Html msg +footer = + Html.footer [ class "container py--medium" ] + [ text "built with elm, 2020" + ] + + + +-- MODAL + + +viewModal : + { actions + | closeModal : msg + , attemptSignIn : msg + , onSignInEmailInput : String -> msg + , onSignInPasswordInput : String -> msg + } + -> Modal + -> Html msg +viewModal actions modal_ = + case modal_ of + Modal.SignInModal { email, password } -> + modal + { title = "Sign in" + , body = + form [ class "column spacing--medium", Events.onSubmit actions.attemptSignIn ] + [ emailField + { label = "Email" + , value = email + , onInput = actions.onSignInEmailInput + } + , passwordField + { label = "Password" + , value = password + , onInput = actions.onSignInPasswordInput + } + , button [ class "button" ] [ text "Sign in" ] + ] + , actions = actions + } + + +modal : + { title : String + , body : Html msg + , actions : { actions | closeModal : msg } + } + -> Html msg +modal ({ actions } as options) = + div [ class "fixed--full" ] + [ div [ class "absolute--full bg--overlay", Events.onClick actions.closeModal ] [] + , div [ class "column spacing--large pad--large absolute--center min-width--480 bg--white" ] + [ div [ class "row spacing--between center-y" ] + [ h3 [ class "font--h3" ] [ text options.title ] + , button [ class "modal__close", Events.onClick actions.closeModal ] [ text "✖️" ] + ] + , options.body + ] + ] + + + +-- FORMS + + +inputField : + String + -> { label : String, value : String, onInput : String -> msg } + -> Html msg +inputField type_ options = + label [ class "column spacing--small" ] + [ span [] [ text options.label ] + , input [ Attr.type_ type_, Attr.value options.value, Events.onInput options.onInput ] [] + ] + + +emailField : + { label : String, value : String, onInput : String -> msg } + -> Html msg +emailField = + inputField "email" + + +passwordField : + { label : String, value : String, onInput : String -> msg } + -> Html msg +passwordField = + inputField "password" diff --git a/example/src/Data/Modal.elm b/example/src/Data/Modal.elm new file mode 100644 index 0000000..f7a9ace --- /dev/null +++ b/example/src/Data/Modal.elm @@ -0,0 +1,28 @@ +module Data.Modal exposing + ( Modal(..) + , signInForm + , updateSignInForm + ) + +import Data.SignInForm exposing (SignInForm) + + +type Modal + = SignInModal SignInForm + + +signInForm : Modal -> Maybe SignInForm +signInForm modal = + case modal of + SignInModal form -> + Just form + + +updateSignInForm : + (SignInForm -> SignInForm) + -> Modal + -> Modal +updateSignInForm fn modal = + case modal of + SignInModal form -> + SignInModal (fn form) diff --git a/example/src/Data/SignInForm.elm b/example/src/Data/SignInForm.elm new file mode 100644 index 0000000..d2c61ef --- /dev/null +++ b/example/src/Data/SignInForm.elm @@ -0,0 +1,35 @@ +module Data.SignInForm exposing + ( Field(..) + , SignInForm + , empty + , updateEmail + , updatePassword + ) + + +type alias SignInForm = + { email : String + , password : String + } + + +type Field + = Email + | Password + + +empty : SignInForm +empty = + { email = "" + , password = "" + } + + +updateEmail : String -> SignInForm -> SignInForm +updateEmail email form = + { form | email = email } + + +updatePassword : String -> SignInForm -> SignInForm +updatePassword password form = + { form | password = password } diff --git a/example/src/Data/Tab.elm b/example/src/Data/Tab.elm new file mode 100644 index 0000000..149b78c --- /dev/null +++ b/example/src/Data/Tab.elm @@ -0,0 +1,41 @@ +module Data.Tab exposing + ( Tab + , ourMission + , ourTeam + , ourValues + , toString + ) + + +type Tab + = OurTeam + | OurValues + | OurMission + + +ourTeam : Tab +ourTeam = + OurTeam + + +ourValues : Tab +ourValues = + OurValues + + +ourMission : Tab +ourMission = + OurMission + + +toString : Tab -> String +toString tab = + case tab of + OurTeam -> + "Our Team" + + OurValues -> + "Our Values" + + OurMission -> + "Our Mission" diff --git a/example/src/Data/User.elm b/example/src/Data/User.elm new file mode 100644 index 0000000..1adbce8 --- /dev/null +++ b/example/src/Data/User.elm @@ -0,0 +1,16 @@ +module Data.User exposing (User, fullname) + + +type alias User = + { avatar : String + , name : + { first : String + , last : String + } + , email : String + } + + +fullname : User -> String +fullname { name } = + name.first ++ " " ++ name.last diff --git a/example/src/Global.elm b/example/src/Global.elm new file mode 100644 index 0000000..2d4cc2c --- /dev/null +++ b/example/src/Global.elm @@ -0,0 +1,181 @@ +module Global exposing + ( Flags + , Model + , Msg + , init + , openSignInModal + , signOut + , subscriptions + , update + , view + ) + +import Browser exposing (Document) +import Browser.Navigation as Nav +import Components +import Data.Modal as Modal exposing (Modal) +import Data.SignInForm as SignInForm exposing (SignInForm) +import Data.User exposing (User) +import Task +import Url exposing (Url) + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + { flags : Flags + , url : Url + , key : Nav.Key + , user : Maybe User + , modal : Maybe Modal + } + + +init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) +init flags url key = + ( Model + flags + url + key + Nothing + Nothing + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = AttemptSignIn + | SignOut + | UpdateSignInForm SignInForm.Field String + | OpenModal Modal + | CloseModal + + +openSignInModal : Cmd Msg +openSignInModal = + send (OpenModal (Modal.SignInModal SignInForm.empty)) + + +signOut : Cmd Msg +signOut = + send SignOut + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + -- SIGN IN + AttemptSignIn -> + model.modal + |> Maybe.andThen Modal.signInForm + |> Maybe.map (attemptSignIn model) + |> Maybe.withDefault ( model, Cmd.none ) + + SignOut -> + ( { model | user = Nothing } + , Cmd.none + ) + + UpdateSignInForm field value -> + let + updateFieldWith : String -> SignInForm -> SignInForm + updateFieldWith = + case field of + SignInForm.Email -> + SignInForm.updateEmail + + SignInForm.Password -> + SignInForm.updatePassword + in + ( model.modal + |> Maybe.map (Modal.updateSignInForm (updateFieldWith value)) + |> (\modal -> { model | modal = modal }) + , Cmd.none + ) + + -- MODAL + OpenModal modal -> + ( { model | modal = Just modal } + , Cmd.none + ) + + CloseModal -> + ( { model | modal = Nothing } + , Cmd.none + ) + + +attemptSignIn : Model -> SignInForm -> ( Model, Cmd Msg ) +attemptSignIn model form = + if form.email == "ryan.nhg@gmail.com" && form.password == "password" then + ( { model + | user = + Just + (User + "https://avatars2.githubusercontent.com/u/6187256?s=128&v=4" + { first = "Ryan", last = "Haskell-Glatz" } + form.email + ) + , modal = Nothing + } + , Cmd.none + ) + + else + ( model, Cmd.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : + { page : Document msg + , global : Model + , toMsg : Msg -> msg + } + -> Document msg +view { page, global, toMsg } = + let + actions = + { onSignOut = toMsg <| SignOut + , openSignInModal = toMsg <| OpenModal (Modal.SignInModal SignInForm.empty) + , closeModal = toMsg <| CloseModal + , attemptSignIn = toMsg <| AttemptSignIn + , onSignInEmailInput = toMsg << UpdateSignInForm SignInForm.Email + , onSignInPasswordInput = toMsg << UpdateSignInForm SignInForm.Password + } + in + Components.layout + { page = page + , global = global + , actions = actions + } + + + +-- UTILS + + +send : msg -> Cmd msg +send msg = + Task.succeed msg |> Task.perform identity diff --git a/example/src/Main.elm b/example/src/Main.elm new file mode 100644 index 0000000..1f30ef2 --- /dev/null +++ b/example/src/Main.elm @@ -0,0 +1,141 @@ +module Main exposing (main) + +import Browser exposing (Document) +import Browser.Navigation as Nav exposing (Key) +import Global +import Html +import Generated.Pages as Pages +import Generated.Route as Route exposing (Route) +import Url exposing (Url) + + +main : Program Flags Model Msg +main = + Browser.application + { init = init + , view = view + , update = update + , subscriptions = subscriptions + , onUrlRequest = LinkClicked + , onUrlChange = UrlChanged + } + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + { key : Key + , url : Url + , global : Global.Model + , page : Pages.Model + } + + +init : Flags -> Url -> Key -> ( Model, Cmd Msg ) +init flags url key = + let + ( global, globalCmd ) = + Global.init flags url key + + ( page, pageCmd, pageGlobalCmd ) = + Pages.init (fromUrl url) global + in + ( Model key url global page + , Cmd.batch + [ Cmd.map Global globalCmd + , Cmd.map Global pageGlobalCmd + , Cmd.map Page pageCmd + ] + ) + + +type Msg + = LinkClicked Browser.UrlRequest + | UrlChanged Url + | Global Global.Msg + | Page Pages.Msg + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + LinkClicked (Browser.Internal url) -> + ( model, Nav.pushUrl model.key (Url.toString url) ) + + LinkClicked (Browser.External href) -> + ( model, Nav.load href ) + + UrlChanged url -> + let + ( page, pageCmd, globalCmd ) = + Pages.init (fromUrl url) model.global + in + ( { model | url = url, page = page } + , Cmd.batch + [ Cmd.map Page pageCmd + , Cmd.map Global globalCmd + ] + ) + + Global globalMsg -> + let + ( global, globalCmd ) = + Global.update globalMsg model.global + in + ( { model | global = global } + , Cmd.map Global globalCmd + ) + + Page pageMsg -> + let + ( page, pageCmd, globalCmd ) = + Pages.update pageMsg model.page model.global + in + ( { model | page = page } + , Cmd.batch + [ Cmd.map Page pageCmd + , Cmd.map Global globalCmd + ] + ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.batch + [ model.global + |> Global.subscriptions + |> Sub.map Global + , model.page + |> (\page -> Pages.subscriptions page model.global) + |> Sub.map Page + ] + + +view : Model -> Browser.Document Msg +view model = + let + documentMap : + (msg1 -> msg2) + -> Document msg1 + -> Document msg2 + documentMap fn doc = + { title = doc.title + , body = List.map (Html.map fn) doc.body + } + in + Global.view + { page = Pages.view model.page model.global |> documentMap Page + , global = model.global + , toMsg = Global + } + + +fromUrl : Url -> Route +fromUrl = + Route.fromUrl >> Maybe.withDefault Route.NotFound diff --git a/example/src/Pages/About.elm b/example/src/Pages/About.elm new file mode 100644 index 0000000..4abf349 --- /dev/null +++ b/example/src/Pages/About.elm @@ -0,0 +1,143 @@ +module Pages.About exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Data.Tab as Tab exposing (Tab) +import Html exposing (..) +import Html.Attributes as Attr exposing (class) +import Html.Events as Events +import Spa exposing (Page) + + + +-- PAGE + + +page : Spa.Page Flags Model Msg globalModel globalMsg +page = + Spa.sandbox + { init = init + , update = update + , view = view + } + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + { tab : Tab + } + + +init : Model +init = + { tab = Tab.ourTeam + } + + + +-- UPDATE + + +type Msg + = SelectedTab Tab + + +update : Msg -> Model -> Model +update msg model = + case msg of + SelectedTab tab -> + { model | tab = tab } + + + +-- VIEW + + +view : + Model + -> Document Msg +view model = + { title = "About" + , body = + [ h1 [ class "font--h1" ] [ text "About" ] + , tabs + { selected = model.tab + , choices = + [ ( Tab.ourTeam, viewOurTeam ) + , ( Tab.ourMission, viewOurMission ) + , ( Tab.ourValues, viewOurValues ) + ] + , toString = Tab.toString + , onSelect = SelectedTab + } + ] + } + + +viewOurTeam : Html msg +viewOurTeam = + div [ class "column spacing--small" ] + [ h3 [ class "font--h3" ] [ text "Our Team" ] + , p [] [ text "Here's a short paragraph about our team." ] + ] + + +viewOurValues : Html msg +viewOurValues = + div [ class "column spacing--small" ] + [ h3 [ class "font--h3" ] [ text "Our Values" ] + , p [] [ text "Here's a short paragraph about our values." ] + ] + + +viewOurMission : Html msg +viewOurMission = + div [ class "column spacing--small" ] + [ h3 [ class "font--h3" ] [ text "Our Mission" ] + , p [] [ text "Here's a short paragraph about our mission." ] + ] + + +tabs : + { selected : a + , choices : List ( a, Html msg ) + , toString : a -> String + , onSelect : a -> msg + } + -> Html msg +tabs options = + Html.div [ class "column spacing--medium" ] + [ Html.div [ class "row spacing--small" ] + (List.map + (\( choice, _ ) -> + choice + |> options.toString + |> text + |> List.singleton + |> button [ class "button", Events.onClick (options.onSelect choice) ] + ) + options.choices + ) + , options.choices + |> List.filterMap + (\( value, html ) -> + if value == options.selected then + Just html + + else + Nothing + ) + |> List.head + |> Maybe.withDefault (Html.text "") + ] diff --git a/example/src/Pages/Authors/Dynamic/Posts/Dynamic.elm b/example/src/Pages/Authors/Dynamic/Posts/Dynamic.elm new file mode 100644 index 0000000..639bef9 --- /dev/null +++ b/example/src/Pages/Authors/Dynamic/Posts/Dynamic.elm @@ -0,0 +1,52 @@ +module Pages.Authors.Dynamic.Posts.Dynamic exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Html exposing (..) +import Html.Attributes as Attr exposing (class) +import Spa exposing (Page) + + +type alias Flags = + { param1 : String + , param2 : String + } + + +type alias Model = + { authorId : String + , postId : String + } + + +type alias Msg = + Never + + +page : Page Flags Model Msg globalModel globalMsg +page = + Spa.element + { init = + \flags -> + ( Model flags.param1 flags.param2 + , Cmd.none + ) + , update = \_ model -> ( model, Cmd.none ) + , subscriptions = \_ -> Sub.none + , view = view + } + + +view : Model -> Document msg +view model = + { title = String.concat [ "Post ", model.postId, " by Author ", model.authorId ] + , body = + [ h1 [ class "font--h1" ] [ text ("Post " ++ model.postId) ] + , h3 [ class "font--h5" ] [ text ("Author: " ++ model.authorId) ] + , p [] [ text "(but imagine an actual blog post here)" ] + ] + } diff --git a/example/src/Pages/NotFound.elm b/example/src/Pages/NotFound.elm new file mode 100644 index 0000000..48fed6f --- /dev/null +++ b/example/src/Pages/NotFound.elm @@ -0,0 +1,39 @@ +module Pages.NotFound exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Html exposing (..) +import Html.Attributes as Attr exposing (class) +import Spa exposing (Page) + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Page Flags Model Msg globalModel globalMsg +page = + Spa.static + { view = view + } + + +view : Document msg +view = + { title = "NotFound" + , body = + [ h1 [ class "font--h1" ] [ text "Page not found..." ] + ] + } diff --git a/example/src/Pages/Posts/Dynamic.elm b/example/src/Pages/Posts/Dynamic.elm new file mode 100644 index 0000000..5a22a74 --- /dev/null +++ b/example/src/Pages/Posts/Dynamic.elm @@ -0,0 +1,45 @@ +module Pages.Posts.Dynamic exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Html exposing (..) +import Html.Attributes as Attr exposing (class) +import Spa exposing (Page) + + +type alias Flags = + { param1 : String + } + + +type alias Model = + { postId : String + } + + +type alias Msg = + Never + + +page : Page Flags Model Msg globalModel globalMsg +page = + Spa.element + { init = \flags -> ( Model flags.param1, Cmd.none ) + , update = \_ model -> ( model, Cmd.none ) + , subscriptions = \_ -> Sub.none + , view = view + } + + +view : Model -> Document msg +view model = + { title = model.postId ++ " | Post" + , body = + [ h1 [ class "font--h1" ] [ text ("Post " ++ model.postId) ] + , p [] [ text "(but imagine an actual blog post here)" ] + ] + } diff --git a/example/src/Pages/Posts/Top.elm b/example/src/Pages/Posts/Top.elm new file mode 100644 index 0000000..38e8cfa --- /dev/null +++ b/example/src/Pages/Posts/Top.elm @@ -0,0 +1,44 @@ +module Pages.Posts.Top exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Html exposing (..) +import Html.Attributes as Attr exposing (class, href) +import Spa exposing (Page) + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Page Flags Model Msg globalModel globalMsg +page = + Spa.static + { view = view + } + + +view : Document msg +view = + { title = "Posts" + , body = + [ h1 [ class "font--h1" ] [ text "Posts" ] + , ul [] + [ li [] [ a [ class "link", href "/posts/1" ] [ text "The first post" ] ] + , li [] [ a [ class "link", href "/posts/2" ] [ text "The second post" ] ] + , li [] [ a [ class "link", href "/posts/3" ] [ text "The last post" ] ] + ] + ] + } diff --git a/example/src/Pages/Profile.elm b/example/src/Pages/Profile.elm new file mode 100644 index 0000000..ed5e08c --- /dev/null +++ b/example/src/Pages/Profile.elm @@ -0,0 +1,109 @@ +module Pages.Profile exposing + ( Flags + , Model + , Msg + , page + ) + +import Browser exposing (Document) +import Browser.Navigation as Nav +import Data.User as User exposing (User) +import Global +import Html exposing (..) +import Html.Attributes exposing (alt, class, src) +import Html.Events as Events +import Spa exposing (Page) + + + +-- PAGE + + +page : Spa.Page Flags Model Msg Global.Model Global.Msg +page = + Spa.component + { init = always init + , update = always update + , subscriptions = always subscriptions + , view = view + } + + + +-- INIT + + +type alias Flags = + () + + +type alias Model = + {} + + +init : + Flags + -> ( Model, Cmd Msg, Cmd Global.Msg ) +init _ = + ( Model + , Cmd.none + , Cmd.none + ) + + + +-- UPDATE + + +type Msg + = ClickedSignIn + | ClickedSignOut + + +update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +update msg model = + case msg of + ClickedSignIn -> + ( model, Cmd.none, Global.openSignInModal ) + + ClickedSignOut -> + ( model, Cmd.none, Global.signOut ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : + Global.Model + -> Model + -> Document Msg +view global model = + { title = "Profile" + , body = + [ h1 [ class "font--h1" ] [ text "Profile" ] + , case global.user of + Just user -> + div [ class "row spacing--large" ] + [ img [ src user.avatar, alt (User.fullname user) ] [] + , div [ class "column spacing--large align--left" ] + [ div [ class "column" ] + [ h3 [ class "font--h5 font--bold" ] [ text (User.fullname user) ] + , p [] [ text user.email ] + ] + , button [ Events.onClick ClickedSignOut, class "button" ] [ text "Sign out" ] + ] + ] + + Nothing -> + div [ class "column spacing--medium align--left" ] + [ p [] [ text "You need to sign in to view this page!" ] + , button [ Events.onClick ClickedSignIn, class "button" ] [ text "Sign in" ] + ] + ] + } diff --git a/example/src/Pages/Top.elm b/example/src/Pages/Top.elm new file mode 100644 index 0000000..0269709 --- /dev/null +++ b/example/src/Pages/Top.elm @@ -0,0 +1,34 @@ +module Pages.Top exposing (Flags, Model, Msg, page) + +import Browser exposing (Document) +import Html exposing (..) +import Html.Attributes as Attr exposing (class) +import Spa exposing (Page) + + +type alias Flags = + () + + +type alias Model = + () + + +type alias Msg = + Never + + +page : Page Flags Model Msg globalModel globalMsg +page = + Spa.static + { view = view + } + + +view : Document msg +view = + { title = "Homepage" + , body = + [ h1 [ class "font--h1" ] [ text "Homepage" ] + ] + } diff --git a/example/src/Route.elm b/example/src/Route.elm new file mode 100644 index 0000000..ceecc81 --- /dev/null +++ b/example/src/Route.elm @@ -0,0 +1,71 @@ +module Route exposing + ( Route(..) + , fromUrl + , toHref + ) + +import Url exposing (Url) +import Url.Parser as Parser exposing ((), Parser) + + +type Route + = About + | Authors_Dynamic_Posts_Dynamic { param1 : String, param2 : String } + | Top + | Posts_Top + | Posts_Dynamic { param1 : String } + | Profile + | NotFound + + +fromUrl : Url -> Maybe Route +fromUrl = + Parser.parse routes + + +routes : Parser (Route -> a) a +routes = + Parser.oneOf + [ Parser.map Top Parser.top + , Parser.map About (Parser.s "about") + , Parser.map Posts_Top (Parser.s "posts") + , Parser.map Profile (Parser.s "profile") + , (Parser.s "posts" Parser.string) + |> Parser.map (\param1 -> { param1 = param1 }) + |> Parser.map Posts_Dynamic + , (Parser.s "authors" Parser.string Parser.s "posts" Parser.string) + |> Parser.map (\param1 param2 -> { param1 = param1, param2 = param2 }) + |> Parser.map Authors_Dynamic_Posts_Dynamic + ] + + +toHref : Route -> String +toHref route = + let + segments : List String + segments = + case route of + Top -> + [] + + About -> + [ "about" ] + + Authors_Dynamic_Posts_Dynamic { param1, param2 } -> + [ "authors", param1, "posts", param2 ] + + Posts_Top -> + [ "posts" ] + + Posts_Dynamic { param1 } -> + [ "posts", param1 ] + + Profile -> + [ "profile" ] + + NotFound -> + [ "not-found" ] + in + segments + |> String.join "/" + |> String.append "/" diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index f8964f6..0000000 --- a/examples/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# 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. diff --git a/examples/complex/.gitignore b/examples/complex/.gitignore deleted file mode 100644 index d8f6743..0000000 --- a/examples/complex/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -dist -elm-stuff -node_modules \ No newline at end of file diff --git a/examples/complex/README.md b/examples/complex/README.md deleted file mode 100644 index 95d8599..0000000 --- a/examples/complex/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# elm-spa/examples/complex -> an example of using more complex features - - -## running things - -``` -npm install -npm run dev -``` \ No newline at end of file diff --git a/examples/complex/elm.json b/examples/complex/elm.json deleted file mode 100644 index c22c17a..0000000 --- a/examples/complex/elm.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "type": "application", - "source-directories": [ - "src", - "../../src", - "elm-stuff/.elm-spa" - ], - "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", - "elm/url": "1.0.0", - "elm-explorations/markdown": "1.0.0", - "mdgriffith/elm-ui": "1.1.5" - }, - "indirect": { - "elm/time": "1.0.0", - "elm/virtual-dom": "1.0.2" - } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - }, - "elm-spa": { - "ui": "Element" - } -} diff --git a/examples/complex/index.html b/examples/complex/index.html deleted file mode 100644 index b198a4e..0000000 --- a/examples/complex/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - elm-spa - - - - - - - - - - - diff --git a/examples/complex/package-lock.json b/examples/complex/package-lock.json deleted file mode 100644 index 6b621ce..0000000 --- a/examples/complex/package-lock.json +++ /dev/null @@ -1,1251 +0,0 @@ -{ - "name": "complex-elm-spa-example", - "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==", - "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" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "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=" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "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=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "chokidar-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-2.1.0.tgz", - "integrity": "sha512-6n21AVpW6ywuEPoxJcLXMA2p4T+SLjWsXKny/9yTWFz0kKxESI3eUylpeV97LylING/27T/RVTY0f2/0QaWq9Q==", - "requires": { - "chokidar": "^3.2.3", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "yargs": "^13.3.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" - }, - "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=" - }, - "crocks": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/crocks/-/crocks-0.12.1.tgz", - "integrity": "sha512-2qCRJwBmPlRQXzd50k9gt9PaItultOP8lj/cKSH2Eai9aeBuNqAnDuyolAm9TGn6Pw/4BgbxtPJLU1S+tQ4WMQ==" - }, - "cross-spawn": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.0.1.tgz", - "integrity": "sha1-o7uzAtsil8vqPATt82lB9GE6o5k=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "elm": { - "version": "0.19.1-3", - "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-3.tgz", - "integrity": "sha512-6y36ewCcVmTOx8lj7cKJs3bhI5qMfoVEigePZ9PhEUNKpwjjML/pU2u2YSpHVAznuCcojoF6KIsrS1Ci7GtVaQ==", - "requires": { - "request": "^2.88.0" - } - }, - "elm-hot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.1.tgz", - "integrity": "sha512-ZHjoHd2Ev6riNXNQirj3J+GKKXXwedAUikfFBYzlVL/+3CdGs96cpZ7nhAk4c5l//Qa9ymltrqX36mOlr0pPFA==" - }, - "elm-live": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-4.0.1.tgz", - "integrity": "sha512-IlonaC1pO/QoXlOrwwrJaxyvpJAT8QDSfzenkChbhU1PC1fJetkj2TwZfki+y1ZxpSMTnMSomMraOdWA6DO3VQ==", - "requires": { - "chalk": "^1.1.1", - "chokidar": "3.0.2", - "commander": "2.17.1", - "crocks": "0.12.1", - "cross-spawn": "5.0.1", - "elm-hot": "1.1.1", - "finalhandler": "1.1.2", - "http-proxy": "1.17.0", - "internal-ip": "4.3.0", - "mime": "2.4.3", - "open": "6.4.0", - "pem": "1.14.2", - "serve-static": "1.14.1", - "ws": "7.1.1" - }, - "dependencies": { - "chokidar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", - "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", - "requires": { - "anymatch": "^3.0.1", - "braces": "^3.0.2", - "fsevents": "^2.0.6", - "glob-parent": "^5.0.0", - "is-binary-path": "^2.1.0", - "is-glob": "^4.0.1", - "normalize-path": "^3.0.0", - "readdirp": "^3.1.1" - } - } - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "es6-promisify": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz", - "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "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=" - }, - "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=" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "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==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "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==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } - } - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "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==" - }, - "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=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==" - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "requires": { - "is-wsl": "^1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "pem": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.2.tgz", - "integrity": "sha512-TOnPtq3ZFnCniOZ+rka4pk8UIze9xG1qI+wNE7EmkiR/cg+53uVvk5QbkWZ7M6RsuOxzz62FW1hlAobJr/lTOA==", - "requires": { - "es6-promisify": "^6.0.0", - "md5": "^2.2.1", - "os-tmpdir": "^1.0.1", - "which": "^1.3.1" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "picomatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", - "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "requires": { - "picomatch": "^2.0.4" - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "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" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "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" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "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==", - "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=" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "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=" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "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==", - "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==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.1.tgz", - "integrity": "sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==", - "requires": { - "async-limiter": "^1.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/examples/complex/package.json b/examples/complex/package.json deleted file mode 100644 index 31aa710..0000000 --- a/examples/complex/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "complex-elm-spa-example", - "version": "1.0.0", - "description": "an example for elm-spa!", - "main": "src/index.js", - "scripts": { - "start": "npm run dev", - "dev": "npm run elm:spa:build && npm run elm:spa:watch & npm run elm:live", - "build": "npm run elm:spa:build && npm run elm:compile", - "elm:compile": "elm make src/Main.elm --output=dist/elm.compiled.js --optimize", - "elm:live": "elm-live src/Main.elm --start-page=index.html --open --pushstate --port=1234 -- --output=dist/elm.compiled.js --debug", - "elm:spa:build": "elm-spa build .", - "elm:spa:watch": "chokidar 'src/Pages' -c 'npm run elm:spa:build'" - }, - "dependencies": { - "chokidar-cli": "2.1.0", - "elm": "0.19.1-3", - "elm-live": "4.0.1" - }, - "devDependencies": {}, - "keywords": [] -} diff --git a/examples/complex/public/ports.js b/examples/complex/public/ports.js deleted file mode 100644 index c1eed03..0000000 --- a/examples/complex/public/ports.js +++ /dev/null @@ -1,24 +0,0 @@ -// On load, listen to Elm! -window.addEventListener('load', _ => { - window.ports = { - init: app => - app.ports.outgoing.subscribe(({ action, data }) => - actions[action] - ? actions[action](data) - : console.warn(`I didn't recognize action "${action}".`) - ) - } -}) - -// maps actions to functions! -const actions = { - 'ALERT': message => - window.alert(message), - 'SCROLL_TO': id => - document.getElementById(id) && - window.scrollTo({ - top: document.getElementById(id).offsetTop, - left: 0, - behavior: 'smooth' - }) -} diff --git a/examples/complex/public/styles.css b/examples/complex/public/styles.css deleted file mode 100644 index 99f62ca..0000000 --- a/examples/complex/public/styles.css +++ /dev/null @@ -1,37 +0,0 @@ -body { - overflow-y: scroll; -} -.markdown > * { - margin: 0; - margin-top: 32px; -} -.markdown > *:not(:first-child) { - margin-top: 1rem; -} -.markdown pre { - font-size: 16px; - line-height: 1.3; - border: solid 1px #d0d0d0; - border-radius: 2px; - padding: 16px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); -} -.markdown p { - font-size: 18px; - line-height: 1.5; -} -.markdown h3, -.markdown h3 * { - font-size: 36px; - line-height: 1.2; -} -.markdown h4, -.markdown h4 * { - font-size: 22px; - line-height: 1.2; -} -.markdown a { - text-decoration: underline; - font-weight: 600; - color: rgb(204, 75, 75); -} \ No newline at end of file diff --git a/examples/complex/src/Components/Button.elm b/examples/complex/src/Components/Button.elm deleted file mode 100644 index 7e8dbe3..0000000 --- a/examples/complex/src/Components/Button.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Components.Button exposing (view) - -import Utils.Styles as Styles -import Element exposing (..) -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input -import Html.Attributes as Attr - - -view : - { onPress : Maybe msg - , label : Element msg - } - -> Element msg -view config = - Input.button - ((if config.onPress == Nothing then - alpha 0.6 - - else - alpha 1 - ) - :: Styles.button - ) - config diff --git a/examples/complex/src/Components/Hero.elm b/examples/complex/src/Components/Hero.elm deleted file mode 100644 index 59c6a47..0000000 --- a/examples/complex/src/Components/Hero.elm +++ /dev/null @@ -1,58 +0,0 @@ -module Components.Hero exposing (Action(..), view) - -import Utils.Styles as Styles -import Element exposing (..) -import Element.Input as Input - - -type Action msg - = Link String - | Button msg - - -view : - { title : String - , subtitle : Element msg - , buttons : List { action : Action msg, label : Element msg } - } - -> Element msg -view config = - column - [ paddingEach - { top = 128 - , bottom = 148 - , left = 0 - , right = 0 - } - , spacing 20 - , centerX - ] - <| - List.concat - [ [ Styles.h1 [ centerX ] (text config.title) - , el [ centerX, alpha 0.8 ] config.subtitle - ] - , config.buttons - |> List.map (viewAction (centerX :: Styles.button)) - |> viewActions - ] - - -viewAction : List (Attribute msg) -> { action : Action msg, label : Element msg } -> Element msg -viewAction attrs { action, label } = - case action of - Link url -> - link attrs { url = url, label = label } - - Button msg -> - Input.button attrs { onPress = Just msg, label = label } - - -viewActions : List (Element msg) -> List (Element msg) -viewActions links = - if List.isEmpty links then - [] - - else - [ wrappedRow [ spacing 24, centerX ] links - ] diff --git a/examples/complex/src/Components/Section.elm b/examples/complex/src/Components/Section.elm deleted file mode 100644 index 20b33fb..0000000 --- a/examples/complex/src/Components/Section.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Components.Section exposing (view) - -import Utils.Styles as Styles -import Element exposing (..) -import Html.Attributes as Attr -import Markdown - - -view : - { title : String - , content : String - } - -> Element msg -view config = - paragraph [] - [ Styles.h3 [] (text config.title) - , Element.html (Markdown.toHtml [ Attr.class "markdown" ] config.content) - ] diff --git a/examples/complex/src/Global.elm b/examples/complex/src/Global.elm deleted file mode 100644 index 3b943b1..0000000 --- a/examples/complex/src/Global.elm +++ /dev/null @@ -1,73 +0,0 @@ -module Global exposing - ( Flags - , Model - , Msg(..) - , init - , subscriptions - , update - ) - -import Generated.Routes as Routes exposing (Route, routes) -import Ports - - -type alias Flags = - () - - -type alias Model = - { user : Maybe String - } - - -type Msg - = SignIn String - | SignOut - | AfterNavigate - { old : Route - , new : Route - } - - -type alias Commands msg = - { navigate : Route -> Cmd msg - } - - -init : Commands msg -> Flags -> ( Model, Cmd Msg, Cmd msg ) -init _ _ = - ( { user = Nothing } - , Cmd.none - , Cmd.none - ) - - -update : Commands msg -> Msg -> Model -> ( Model, Cmd Msg, Cmd msg ) -update commands msg model = - case msg of - AfterNavigate routes -> - ( model - , Cmd.none - , if routes.new == Routes.routes.guide then - Ports.alert "Global: You're on the guide!" - - else - Cmd.none - ) - - SignIn user -> - ( { model | user = Just user } - , Cmd.none - , commands.navigate routes.top - ) - - SignOut -> - ( { model | user = Nothing } - , Cmd.none - , Cmd.none - ) - - -subscriptions : Model -> Sub Msg -subscriptions _ = - Sub.none diff --git a/examples/complex/src/Layout.elm b/examples/complex/src/Layout.elm deleted file mode 100644 index 5e4816b..0000000 --- a/examples/complex/src/Layout.elm +++ /dev/null @@ -1,89 +0,0 @@ -module Layout exposing (view) - -import Components.Button -import Utils.Styles as Styles -import Element exposing (..) -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input -import Global -import Html.Attributes as Attr -import Spa.Page -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, global, fromGlobalMsg } = - column - [ Font.size 16 - , Font.color Styles.colors.jet - , Font.family Styles.fonts.sans - , paddingEach - { top = 32 - , left = 16 - , right = 16 - , bottom = 128 - } - , spacing 32 - , width (fill |> maximum 640) - , height fill - , centerX - ] - [ Element.map fromGlobalMsg (viewNavbar global.user) - , page - ] - - -viewNavbar : Maybe String -> Element Global.Msg -viewNavbar user_ = - row - [ width fill - , spacing 24 - ] - [ row [ Font.size 18, spacing 24 ] <| - (link - [ Font.size 20 - , Font.semiBold - , Font.color Styles.colors.coral - , Styles.transition - { property = "opacity" - , duration = 150 - } - , mouseOver [ alpha 0.6 ] - ] - { label = text "elm-spa" - , url = "/" - } - :: List.map viewLink - [ ( "docs", "/docs" ) - , ( "guide", "/guide" ) - ] - ) - , el [ alignRight ] <| - case user_ of - Just name -> - Components.Button.view - { onPress = Just Global.SignOut - , label = text ("sign out " ++ name) - } - - Nothing -> - viewButtonLink ( "sign in", "/sign-in" ) - ] - - -viewLink : ( String, String ) -> Element msg -viewLink ( label, url ) = - link Styles.link - { url = url - , label = text label - } - - -viewButtonLink : ( String, String ) -> Element msg -viewButtonLink ( label, url ) = - link Styles.button - { url = url - , label = text label - } diff --git a/examples/complex/src/Layouts/Docs.elm b/examples/complex/src/Layouts/Docs.elm deleted file mode 100644 index 6f4944d..0000000 --- a/examples/complex/src/Layouts/Docs.elm +++ /dev/null @@ -1,41 +0,0 @@ -module Layouts.Docs exposing (view) - -import Utils.Styles as Styles -import Element exposing (..) -import Element.Font as Font -import Generated.Routes as Routes exposing (Route, routes) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, route } = - column [ width fill ] - [ row [ spacing 16 ] <| - List.map (viewLink route) - [ { label = "elm" - , route = routes.docs_dynamic "elm" - } - , { label = "elm-spa" - , route = routes.docs_dynamic "elm-spa" - } - ] - , page - ] - - -viewLink : Route -> { label : String, route : Route } -> Element msg -viewLink activeRoute { label, route } = - if route == activeRoute then - link - [ Font.underline - ] - { url = Routes.toPath route - , label = text label - } - - else - link - Styles.link - { url = Routes.toPath route - , label = text label - } diff --git a/examples/complex/src/Layouts/Guide.elm b/examples/complex/src/Layouts/Guide.elm deleted file mode 100644 index 00adbaf..0000000 --- a/examples/complex/src/Layouts/Guide.elm +++ /dev/null @@ -1,19 +0,0 @@ -module Layouts.Guide exposing (view) - -import Utils.Styles as Styles -import Element exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page } = - column - [ width fill - , spacing -128 - ] - [ page - , row [ centerX, spacing 16 ] - [ link Styles.link { label = text "programming", url = "/guide/programming" } - , link Styles.link { label = text "elm", url = "/guide/elm" } - ] - ] diff --git a/examples/complex/src/Layouts/Guide/Dynamic.elm b/examples/complex/src/Layouts/Guide/Dynamic.elm deleted file mode 100644 index a1f8050..0000000 --- a/examples/complex/src/Layouts/Guide/Dynamic.elm +++ /dev/null @@ -1,9 +0,0 @@ -module Layouts.Guide.Dynamic exposing (view) - -import Element exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page } = - page diff --git a/examples/complex/src/Layouts/Guide/Dynamic/Dynamic.elm b/examples/complex/src/Layouts/Guide/Dynamic/Dynamic.elm deleted file mode 100644 index f77e986..0000000 --- a/examples/complex/src/Layouts/Guide/Dynamic/Dynamic.elm +++ /dev/null @@ -1,9 +0,0 @@ -module Layouts.Guide.Dynamic.Dynamic exposing (view) - -import Element exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page } = - page diff --git a/examples/complex/src/Layouts/Guide/Dynamic/Faq.elm b/examples/complex/src/Layouts/Guide/Dynamic/Faq.elm deleted file mode 100644 index 4386637..0000000 --- a/examples/complex/src/Layouts/Guide/Dynamic/Faq.elm +++ /dev/null @@ -1,9 +0,0 @@ -module Layouts.Guide.Dynamic.Faq exposing (view) - -import Element exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page } = - page diff --git a/examples/complex/src/Main.elm b/examples/complex/src/Main.elm deleted file mode 100644 index 01e3902..0000000 --- a/examples/complex/src/Main.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Main exposing (main) - -import Generated.Pages as Pages -import Generated.Routes as Routes -import Global -import Spa -import Transitions - - -main : Spa.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg -main = - Spa.create - { ui = Spa.usingElmUi - , transitions = Transitions.transitions - , routing = - { routes = Routes.parsers - , toPath = Routes.toPath - , notFound = Routes.routes.notFound - , afterNavigate = Just Global.AfterNavigate - } - , global = - { init = Global.init - , update = Global.update - , subscriptions = Global.subscriptions - } - , page = Pages.page - } diff --git a/examples/complex/src/Pages/Docs/Dynamic.elm b/examples/complex/src/Pages/Docs/Dynamic.elm deleted file mode 100644 index d049ab5..0000000 --- a/examples/complex/src/Pages/Docs/Dynamic.elm +++ /dev/null @@ -1,67 +0,0 @@ -module Pages.Docs.Dynamic exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Dict exposing (Dict) -import Element exposing (..) -import Element.Font as Font -import Generated.Docs.Params as Params -import Global -import Utils.Spa as Spa exposing (Page) - - -type alias Model = - { slug : String - } - - -type alias Msg = - Never - - -page : Page Params.Dynamic Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Dynamic" - , init = always init - , update = always update - , view = always view - } - - - --- INIT - - -init : Params.Dynamic -> Model -init { param1 } = - { slug = param1 - } - - - --- UPDATE - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - ] - [ Components.Hero.view - { title = "docs: " ++ model.slug - , subtitle = text "\"it's not done until the docs are great.\"" - , buttons = - [ { label = text "back to docs", action = Components.Hero.Link "/docs" } - ] - } - ] diff --git a/examples/complex/src/Pages/Docs/Static.elm b/examples/complex/src/Pages/Docs/Static.elm deleted file mode 100644 index 6134fab..0000000 --- a/examples/complex/src/Pages/Docs/Static.elm +++ /dev/null @@ -1,40 +0,0 @@ -module Pages.Docs.Static exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Docs.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Static Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Static" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column - [ width fill - ] - [ Components.Hero.view - { title = "static tho" - , subtitle = text "\"it's not done until the docs are great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Docs/Top.elm b/examples/complex/src/Pages/Docs/Top.elm deleted file mode 100644 index 15abdf3..0000000 --- a/examples/complex/src/Pages/Docs/Top.elm +++ /dev/null @@ -1,38 +0,0 @@ -module Pages.Docs.Top exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Docs.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Docs" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ width fill ] - [ Components.Hero.view - { title = "docs" - , subtitle = text "\"it's not done until the docs are great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide.elm b/examples/complex/src/Pages/Guide.elm deleted file mode 100644 index 117a485..0000000 --- a/examples/complex/src/Pages/Guide.elm +++ /dev/null @@ -1,43 +0,0 @@ -module Pages.Guide exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Guide Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Guide" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column - [ width fill ] - [ Components.Hero.view - { title = "guide" - , subtitle = text "alright, where should we begin?" - , buttons = - [ { label = text "new to web dev", action = Components.Hero.Link "/guide/programming" } - , { label = text "new to elm", action = Components.Hero.Link "/guide/elm" } - , { label = text "new to elm-spa", action = Components.Hero.Link "/guide/elm-spa" } - ] - } - ] diff --git a/examples/complex/src/Pages/Guide/Dynamic/Dynamic/Top.elm b/examples/complex/src/Pages/Guide/Dynamic/Dynamic/Top.elm deleted file mode 100644 index 72f42b6..0000000 --- a/examples/complex/src/Pages/Guide/Dynamic/Dynamic/Top.elm +++ /dev/null @@ -1,56 +0,0 @@ -module Pages.Guide.Dynamic.Dynamic.Top exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Dynamic.Dynamic.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - { folder : String - , me : String - } - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Guide.Dynamic.Top" - , init = always init - , update = always update - , view = always view - } - - -init : Params.Top -> Model -init { param1, param2 } = - { folder = param1 - , me = param2 - } - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - ] - [ Components.Hero.view - { title = model.me ++ " in " ++ model.folder - , subtitle = text "oh boi" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/Dynamic/Faq/Top.elm b/examples/complex/src/Pages/Guide/Dynamic/Faq/Top.elm deleted file mode 100644 index cf58461..0000000 --- a/examples/complex/src/Pages/Guide/Dynamic/Faq/Top.elm +++ /dev/null @@ -1,54 +0,0 @@ -module Pages.Guide.Dynamic.Faq.Top exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Dynamic.Faq.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - { slug : String - } - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Guide.Dynamic.Top" - , init = always init - , update = always update - , view = always view - } - - -init : Params.Top -> Model -init { param1 } = - { slug = param1 - } - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - ] - [ Components.Hero.view - { title = "Faq for " ++ model.slug - , subtitle = text "\"you're gonna be great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/Dynamic/Intro.elm b/examples/complex/src/Pages/Guide/Dynamic/Intro.elm deleted file mode 100644 index c226cb4..0000000 --- a/examples/complex/src/Pages/Guide/Dynamic/Intro.elm +++ /dev/null @@ -1,54 +0,0 @@ -module Pages.Guide.Dynamic.Intro exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Dynamic.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - { slug : String - } - - -type alias Msg = - Never - - -page : Page Params.Intro Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Guide.Dynamic.Intro" - , init = always init - , update = always update - , view = always view - } - - -init : Params.Intro -> Model -init { param1 } = - { slug = param1 - } - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - ] - [ Components.Hero.view - { title = "intro to " ++ model.slug - , subtitle = text "\"you're gonna be great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/Dynamic/Other.elm b/examples/complex/src/Pages/Guide/Dynamic/Other.elm deleted file mode 100644 index 402b468..0000000 --- a/examples/complex/src/Pages/Guide/Dynamic/Other.elm +++ /dev/null @@ -1,54 +0,0 @@ -module Pages.Guide.Dynamic.Other exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Dynamic.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - { slug : String - } - - -type alias Msg = - Never - - -page : Page Params.Other Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Guide.Dynamic.Other" - , init = always init - , update = always update - , view = always view - } - - -init : Params.Other -> Model -init { param1 } = - { slug = param1 - } - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - ] - [ Components.Hero.view - { title = "other " ++ model.slug - , subtitle = text "\"you're gonna be great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/Elm.elm b/examples/complex/src/Pages/Guide/Elm.elm deleted file mode 100644 index 65d0d50..0000000 --- a/examples/complex/src/Pages/Guide/Elm.elm +++ /dev/null @@ -1,38 +0,0 @@ -module Pages.Guide.Elm exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Elm Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Guide.Elm" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ width fill ] - [ Components.Hero.view - { title = "intro to elm" - , subtitle = text "\"you're gonna be great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/ElmSpa.elm b/examples/complex/src/Pages/Guide/ElmSpa.elm deleted file mode 100644 index 9bc5f00..0000000 --- a/examples/complex/src/Pages/Guide/ElmSpa.elm +++ /dev/null @@ -1,38 +0,0 @@ -module Pages.Guide.ElmSpa exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.ElmSpa Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Guide.ElmSpa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ width fill ] - [ Components.Hero.view - { title = "intro to elm-spa" - , subtitle = text "\"you're gonna be great.\"" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/Guide/Programming.elm b/examples/complex/src/Pages/Guide/Programming.elm deleted file mode 100644 index 5999189..0000000 --- a/examples/complex/src/Pages/Guide/Programming.elm +++ /dev/null @@ -1,38 +0,0 @@ -module Pages.Guide.Programming exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Guide.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Programming Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Guide.Programming" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ width fill ] - [ Components.Hero.view - { title = "programming" - , subtitle = text "become nerdy, in an awful way" - , buttons = [] - } - ] diff --git a/examples/complex/src/Pages/NotFound.elm b/examples/complex/src/Pages/NotFound.elm deleted file mode 100644 index 4b4f0b1..0000000 --- a/examples/complex/src/Pages/NotFound.elm +++ /dev/null @@ -1,40 +0,0 @@ -module Pages.NotFound exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Element exposing (..) -import Generated.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.NotFound Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "NotFound" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - column [ width fill ] - [ Components.Hero.view - { title = "page not found" - , subtitle = text "but i'm not even mad about it." - , buttons = - [ { label = text "back home", action = Components.Hero.Link "/" } - ] - } - ] diff --git a/examples/complex/src/Pages/SignIn.elm b/examples/complex/src/Pages/SignIn.elm deleted file mode 100644 index bd89248..0000000 --- a/examples/complex/src/Pages/SignIn.elm +++ /dev/null @@ -1,204 +0,0 @@ -module Pages.SignIn exposing (Model, Msg, page) - -import Spa.Page -import Components.Button -import Utils.Styles as Styles -import Element exposing (..) -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input -import Generated.Params as Params -import Global -import Html exposing (Html) -import Html.Attributes as Attr -import Html.Events as Events -import Utils.Spa exposing (Page) - - -type alias Model = - { username : String - , password : String - } - - -type Msg - = UpdatedField Field String - | ClickedSignIn - - -type Field - = Username - | Password - - -page : Page Params.SignIn Model Msg model msg appMsg -page = - Spa.Page.component - { title = always "sign in | elm-spa" - , init = always init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg ) -init flags = - ( { username = "" - , password = "" - } - , Cmd.none - , Cmd.none - ) - - - --- UPDATE - - -update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) -update msg model = - case msg of - UpdatedField Username value -> - ( { model | username = value } - , Cmd.none - , Cmd.none - ) - - UpdatedField Password value -> - ( { model | password = value } - , Cmd.none - , Cmd.none - ) - - ClickedSignIn -> - ( model - , Cmd.none - , Spa.Page.send <| - Global.SignIn model.username - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Element Msg -view model = - el [ centerX, centerY ] <| - form - { onSubmit = ClickedSignIn - } - [ spacing 32 ] - [ el [ Font.size 24, Font.semiBold ] - (text "Sign in") - , column [ spacing 16 ] - [ viewField - { label = "Username" - , onChange = UpdatedField Username - , inputType = TextInput - , value = model.username - } - , viewField - { label = "Password" - , onChange = UpdatedField Password - , inputType = PasswordInput - , value = model.password - } - ] - , el [ alignRight ] <| - if String.isEmpty model.username then - Input.button (Styles.button ++ [ alpha 0.6 ]) - { onPress = Nothing - , label = text "Sign In" - } - - else - Input.button (Styles.button ++ [ htmlAttribute (Attr.type_ "submit") ]) - { onPress = Just ClickedSignIn - , label = text "Sign In" - } - ] - - -form : { onSubmit : msg } -> List (Attribute msg) -> List (Element msg) -> Element msg -form config attrs children = - Element.html - (Html.form - [ Events.onSubmit config.onSubmit ] - [ toHtml (column attrs children) - ] - ) - - -toHtml : Element msg -> Html msg -toHtml = - Element.layoutWith { options = [ Element.noStaticStyleSheet ] } [] - - -type InputType - = TextInput - | PasswordInput - - -viewField : - { inputType : InputType - , label : String - , onChange : String -> msg - , value : String - } - -> Element msg -viewField config = - let - styles = - { field = - [ paddingXY 4 4 - , Border.rounded 0 - , Border.widthEach - { top = 0 - , left = 0 - , right = 0 - , bottom = 1 - } - ] - , label = - [ Font.size 16 - , Font.semiBold - ] - } - - label = - Input.labelAbove - styles.label - (text config.label) - in - case config.inputType of - TextInput -> - Input.text styles.field - { onChange = config.onChange - , text = config.value - , placeholder = Nothing - , label = label - } - - PasswordInput -> - Input.currentPassword styles.field - { onChange = config.onChange - , text = config.value - , placeholder = Nothing - , label = label - , show = False - } diff --git a/examples/complex/src/Pages/Top.elm b/examples/complex/src/Pages/Top.elm deleted file mode 100644 index fa31961..0000000 --- a/examples/complex/src/Pages/Top.elm +++ /dev/null @@ -1,131 +0,0 @@ -module Pages.Top exposing (Model, Msg, page) - -import Spa.Page -import Components.Hero -import Components.Section -import Utils.Styles as Styles -import Element exposing (..) -import Generated.Params as Params -import Html.Attributes as Attr -import Ports -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.element - { title = always "elm-spa" - , init = always init - , update = always update - , view = always view - , subscriptions = always subscriptions - } - - - --- INIT - - -init : Params.Top -> ( Model, Cmd Msg ) -init params = - ( () - , Cmd.none - ) - - - --- UPDATE - - -type Msg - = ScrollTo String - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - ScrollTo id -> - ( model - , Ports.scrollTo id - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Element Msg -view model = - column [ width fill ] - [ Components.Hero.view - { title = "elm-spa" - , subtitle = text "a framework for Elm." - , buttons = - [ { label = text "learn more" - , action = Components.Hero.Button (ScrollTo "page-content") - } - ] - } - , column - [ width fill - , spacing 48 - , htmlAttribute (Attr.id "page-content") - ] - [ Components.Section.view - { title = "does elm need a framework?" - , content = """ -__nope, not really__– it's kinda got one built in! so building something like _React_, _VueJS_, or _Angular_ wouldn't really make sense. - -#### ...but even _frameworks_ need frameworks! - -that's why projects like _VueJS_ also have awesome tools like [NuxtJS](#nuxt) that bring together the best tools in the ecosystem (and a set of shared best practices!) - -welcome to __elm-spa__, a framework for Elm! -""" - } - , Components.Section.view - { title = "what does it do?" - , content = """ -__elm-spa__ brings together the best of the Elm ecosystem in one place. - -- [elm-ui](#elm-ui) – a package for creating layout and styles (without CSS!) - -- [elm-live](#elm-live) – a dev server (without a webpack config!) - -- [elm-spa](#elm-spa) – a package for composing pages (without all the typing!) -""" - } - , Components.Section.view - { title = "new to programming?" - , content = """ -perfect! if you're able to read through this paragraph, you're already _overqualified_. - -#### new to elm? - -welcome aboard! we've got a series of short tutorials to help you get started. - -#### new to elm-spa? - -let's dive in and check out all the neat stuff that's ready for your next Elm app! -""" - } - , wrappedRow [ spacing 24 ] - [ link Styles.button { label = text "new to programming", url = "/guide/programming" } - , link Styles.button { label = text "new to elm", url = "/guide/elm" } - , link Styles.button { label = text "new to elm-spa", url = "/guide/elm-spa" } - ] - ] - ] diff --git a/examples/complex/src/Ports.elm b/examples/complex/src/Ports.elm deleted file mode 100644 index 4626c36..0000000 --- a/examples/complex/src/Ports.elm +++ /dev/null @@ -1,22 +0,0 @@ -port module Ports exposing (alert, scrollTo) - -import Json.Encode as Json - - -port outgoing : { action : String, data : Json.Value } -> Cmd msg - - -scrollTo : String -> Cmd msg -scrollTo id = - outgoing - { action = "SCROLL_TO" - , data = Json.string id - } - - -alert : String -> Cmd msg -alert message = - outgoing - { action = "ALERT" - , data = Json.string message - } diff --git a/examples/complex/src/Transitions.elm b/examples/complex/src/Transitions.elm deleted file mode 100644 index e4b6c80..0000000 --- a/examples/complex/src/Transitions.elm +++ /dev/null @@ -1,54 +0,0 @@ -module Transitions exposing (transitions) - -import Element exposing (..) -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition exposing (Transition) -import Spa.Types -import Utils.Styles as Styles - - -transitions : Spa.Types.Transitions (Element msg) -transitions = - { layout = Transition.fadeElmUi 300 - , page = Transition.fadeElmUi 300 - , pages = - [ { path = Docs.path - , transition = batmanNewspaper 600 - } - ] - } - - - --- MAKE YOUR OWN! - - -batmanNewspaper : Int -> Transition (Element msg) -batmanNewspaper duration = - Transition.custom - { duration = duration - , invisible = - \page -> - el - [ alpha 0 - , width fill - , rotate (4 * pi) - , scale 0 - , Styles.transition - { property = "all" - , duration = duration - } - ] - page - , visible = - \page -> - el - [ alpha 1 - , width fill - , Styles.transition - { property = "all" - , duration = duration - } - ] - page - } diff --git a/examples/complex/src/Utils/Spa.elm b/examples/complex/src/Utils/Spa.elm deleted file mode 100644 index aae2879..0000000 --- a/examples/complex/src/Utils/Spa.elm +++ /dev/null @@ -1,67 +0,0 @@ -module Utils.Spa exposing - ( Bundle - , Init - , LayoutContext - , Page - , PageContext - , Recipe - , Update - , layout - , recipe - ) - -import Element exposing (Element) -import Generated.Routes as Routes exposing (Route) -import Global -import Spa.Page -import Spa.Types - - -type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Init model msg = - Spa.Types.Init Route model msg Global.Model Global.Msg - - -type alias Update model msg = - Spa.Types.Update Route model msg Global.Model Global.Msg - - -type alias Bundle msg appMsg = - Spa.Types.Bundle Route msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias LayoutContext msg = - Spa.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg - - -type alias PageContext = - Spa.Types.PageContext Route Global.Model - - -type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -layout : - Layout params model msg appMsg - -> Page params model msg layoutModel layoutMsg appMsg -layout = - Spa.Page.layout Element.map - - -type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -recipe : - Upgrade params model msg layoutModel layoutMsg appMsg - -> Recipe params model msg layoutModel layoutMsg appMsg -recipe = - Spa.Page.recipe Element.map diff --git a/examples/complex/src/Utils/Styles.elm b/examples/complex/src/Utils/Styles.elm deleted file mode 100644 index d652878..0000000 --- a/examples/complex/src/Utils/Styles.elm +++ /dev/null @@ -1,104 +0,0 @@ -module Utils.Styles exposing - ( button - , colors - , fonts - , h1 - , h3 - , link - , transition - ) - -import Element exposing (..) -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Html.Attributes as Attr - - -colors = - { white = rgb 1 1 1 - , jet = rgb255 40 40 40 - , coral = rgb255 204 75 75 - } - - -fonts = - { sans = - [ Font.external - { name = "IBM Plex Sans" - , url = "https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,400i,600,600i&display=swap" - } - , Font.serif - ] - } - - -link : List (Attribute msg) -link = - [ Font.underline - , Font.color colors.coral - , transition - { property = "opacity" - , duration = 150 - } - , mouseOver - [ alpha 0.6 - ] - ] - - -button : List (Attribute msg) -button = - [ paddingXY 16 8 - , Font.size 14 - , Border.color colors.coral - , Font.color colors.coral - , Background.color colors.white - , Border.width 2 - , Border.rounded 4 - , pointer - , transition - { property = "all" - , duration = 150 - } - , mouseOver - [ Font.color colors.white - , Background.color colors.coral - ] - ] - - -h1 : List (Attribute msg) -> Element msg -> Element msg -h1 = - elWith - [ Font.family fonts.sans - , Font.semiBold - , Font.size 64 - ] - - -h3 : List (Attribute msg) -> Element msg -> Element msg -h3 = - elWith - [ Font.family fonts.sans - , Font.semiBold - , Font.size 36 - ] - - -transition : - { property : String - , duration : Int - } - -> Attribute msg -transition { property, duration } = - Element.htmlAttribute - (Attr.style - "transition" - (property ++ " " ++ String.fromInt duration ++ "ms ease-in-out") - ) - - -elWith : List (Attribute msg) -> List (Attribute msg) -> Element msg -> Element msg -elWith styles otherStyles = - el ([ Element.htmlAttribute (Attr.class "markdown") ] ++ styles ++ otherStyles) diff --git a/examples/docs/.gitignore b/examples/docs/.gitignore deleted file mode 100644 index c23bda3..0000000 --- a/examples/docs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -node_modules -elm-stuff -dist \ No newline at end of file diff --git a/examples/docs/README.md b/examples/docs/README.md deleted file mode 100644 index fb3b6fe..0000000 --- a/examples/docs/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# your elm-spa -> single page apps made easy. learn more at [https://elm-spa.dev](https://elm-spa.dev) - -### local development - -``` -npm run dev -``` - -## folder structure - -```elm -README.md -- this file you're reading 👀 -elm.json -- has project dependencies -src/ - Main.elm -- the entrypoint to the app - Global.elm -- share state across pages - Transitions.elm -- smoothly animate between pages - Ports.elm -- communicate with JS - Pages/ -- where all your pages go - Layouts/ -- reusable views around pages - Components/ -- views shared across the site - Utils/ -- a place for helper functions -``` \ No newline at end of file diff --git a/examples/docs/elm-spa.json b/examples/docs/elm-spa.json deleted file mode 100644 index 2d70f02..0000000 --- a/examples/docs/elm-spa.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ui": "Element" -} \ No newline at end of file diff --git a/examples/docs/netlify.toml b/examples/docs/netlify.toml deleted file mode 100644 index cab849f..0000000 --- a/examples/docs/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -# sends all routes to /index.html -# (so you can handle 404s there!) -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file diff --git a/examples/docs/package-lock.json b/examples/docs/package-lock.json deleted file mode 100644 index 79b1825..0000000 --- a/examples/docs/package-lock.json +++ /dev/null @@ -1,1457 +0,0 @@ -{ - "name": "my-elm-spa-project", - "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" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "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 - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "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" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true - }, - "chokidar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", - "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", - "dev": true, - "requires": { - "anymatch": "^3.0.1", - "braces": "^3.0.2", - "fsevents": "^2.0.6", - "glob-parent": "^5.0.0", - "is-binary-path": "^2.1.0", - "is-glob": "^4.0.1", - "normalize-path": "^3.0.0", - "readdirp": "^3.1.1" - } - }, - "chokidar-cli": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-2.1.0.tgz", - "integrity": "sha512-6n21AVpW6ywuEPoxJcLXMA2p4T+SLjWsXKny/9yTWFz0kKxESI3eUylpeV97LylING/27T/RVTY0f2/0QaWq9Q==", - "dev": true, - "requires": { - "chokidar": "^3.2.3", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "yargs": "^13.3.0" - }, - "dependencies": { - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "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" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true - }, - "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 - }, - "crocks": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/crocks/-/crocks-0.12.1.tgz", - "integrity": "sha512-2qCRJwBmPlRQXzd50k9gt9PaItultOP8lj/cKSH2Eai9aeBuNqAnDuyolAm9TGn6Pw/4BgbxtPJLU1S+tQ4WMQ==", - "dev": true - }, - "cross-spawn": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.0.1.tgz", - "integrity": "sha1-o7uzAtsil8vqPATt82lB9GE6o5k=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "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" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.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 - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "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" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "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" - } - }, - "elm-hot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/elm-hot/-/elm-hot-1.1.1.tgz", - "integrity": "sha512-ZHjoHd2Ev6riNXNQirj3J+GKKXXwedAUikfFBYzlVL/+3CdGs96cpZ7nhAk4c5l//Qa9ymltrqX36mOlr0pPFA==", - "dev": true - }, - "elm-live": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-4.0.1.tgz", - "integrity": "sha512-IlonaC1pO/QoXlOrwwrJaxyvpJAT8QDSfzenkChbhU1PC1fJetkj2TwZfki+y1ZxpSMTnMSomMraOdWA6DO3VQ==", - "dev": true, - "requires": { - "chalk": "^1.1.1", - "chokidar": "3.0.2", - "commander": "2.17.1", - "crocks": "0.12.1", - "cross-spawn": "5.0.1", - "elm-hot": "1.1.1", - "finalhandler": "1.1.2", - "http-proxy": "1.17.0", - "internal-ip": "4.3.0", - "mime": "2.4.3", - "open": "6.4.0", - "pem": "1.14.2", - "serve-static": "1.14.1", - "ws": "7.1.1" - } - }, - "elm-spa": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/elm-spa/-/elm-spa-3.0.0.tgz", - "integrity": "sha512-iHCv8EUUyKjYxKypfSMUC5mpPr4pRzSbhenWHh94zeQDJmnzW/JQMWvhdVSRfEGST5OK0vHtn5jcvNPQj5TCXA==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "es6-promisify": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.2.tgz", - "integrity": "sha512-eO6vFm0JvqGzjWIQA6QVKjxpmELfhWbDUWHm1rPfIbn55mhKPiAa5xpLmQWJrNa629ZIeQ8ZvMAi13kvrjK6Mg==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "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 - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", - "dev": true, - "requires": { - "debug": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "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" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "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" - } - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "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" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.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" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "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" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "dev": true, - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", - "dev": true - }, - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.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 - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "pem": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/pem/-/pem-1.14.2.tgz", - "integrity": "sha512-TOnPtq3ZFnCniOZ+rka4pk8UIze9xG1qI+wNE7EmkiR/cg+53uVvk5QbkWZ7M6RsuOxzz62FW1hlAobJr/lTOA==", - "dev": true, - "requires": { - "es6-promisify": "^6.0.0", - "md5": "^2.2.1", - "os-tmpdir": "^1.0.1", - "which": "^1.3.1" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.1.1.tgz", - "integrity": "sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "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 - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "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" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "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 - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "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" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "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 - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "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" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.1.tgz", - "integrity": "sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A==", - "dev": true, - "requires": { - "async-limiter": "^1.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/examples/docs/package.json b/examples/docs/package.json deleted file mode 100644 index 8f21684..0000000 --- a/examples/docs/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "my-elm-spa-project", - "version": "1.0.0", - "description": "learn more at https://elm-spa.dev", - "scripts": { - "start": "npm install && npm run dev", - "dev": "npm run elm:spa:build && npm run elm:spa:watch & npm run elm:live", - "build": "npm run elm:spa:build && npm run elm:compile", - "elm:compile": "elm make src/Main.elm --output=public/dist/elm.compiled.js --optimize", - "elm:live": "elm-live src/Main.elm --dir=public --start-page=index.html --open --pushstate --port=1234 -- --output=public/dist/elm.compiled.js --debug", - "elm:spa:build": "elm-spa build .", - "elm:spa:watch": "chokidar 'src/Pages' -c 'npm run elm:spa:build'" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": {}, - "devDependencies": { - "chokidar-cli": "2.1.0", - "elm": "0.19.1-3", - "elm-live": "4.0.1", - "elm-spa": "3.0.0" - } -} diff --git a/examples/docs/public/content/docs/components.md b/examples/docs/public/content/docs/components.md deleted file mode 100644 index 93c2461..0000000 --- a/examples/docs/public/content/docs/components.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -{ "title": "components" -, "description": "reusing ui in your app." -} ---- - - - -### starting simple - -Not every component in Elm needs to have it's own `Model`, `Msg`, `init`, -`update`, `view` defined. In fact, a lot of things can just be a function! - -Let's look at an examples of using creating a reusable button in Elm: - -```elm -module Pages.Top exposing ( Model, Msg, page ) - -import Element -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input - -type Msg = SignIn | SignOut - -view : Element Msg -view = - Input.button - [ Font.size 14 - , Font.semiBold - , Border.solid - , Border.width 2 - , Border.rounded 4 - , Element.paddingXY 24 8 - , Font.color colors.coral - , Border.color colors.coral - , Background.color colors.white - , Element.pointer - ] - { label = Element.text "SignIn" - , onPress = Just SignIn - } -``` - -Here, our homepage (at `src/Pages/Top.elm`) defines a __bunch__ of button styles. -If we wanted to reuse those styles, we can make a function like this: - -```elm -module Pages.Top exposing ( Model, Msg, page ) - -import Element -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input - -viewButton : { label : String, onPress : msg } -> Element msg -viewButton options = - Input.button - [ Font.size 14 - , Font.semiBold - , Border.solid - , Border.width 2 - , Border.rounded 4 - , Element.paddingXY 24 8 - , Font.color colors.coral - , Border.color colors.coral - , Background.color colors.white - , Element.pointer - ] - { label = Element.text options.label - , onPress = Just options.onPress - } - -type Msg = SignIn | SignOut - -view : Element Msg -view = - Element.column [] - [ viewButton - { label = "Sign in" - , onPress = SignIn - } - , viewButton - { label = "Sign out" - , onPress = SignOut - } - ] -``` - -By creating that `viewButton` function, we prevent the need to duplicate our code, -and reuse those styles again for the "Sign out" button! - - -### sharing between pages - -So we love our button so much that we want to reuse it on the "Share" page -(over at `src/Pages/Share.elm`). The only problem is that all the code we wrote -is in the `src/Pages/Top.elm` file. - -__So what should we do?__ - -Let's create a module called `Ui.elm` that has our `viewButton` function in it: - -```elm -module Ui exposing ( viewButton ) - -import Element -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Element.Input as Input - - -viewButton : { label : String, onPress : msg } -> Element msg -viewButton options = - Input.button - [ Font.size 14 - , Font.semiBold - , Border.solid - , Border.width 2 - , Border.rounded 4 - , Element.paddingXY 24 8 - , Font.color colors.coral - , Border.color colors.coral - , Background.color colors.white - , Element.pointer - ] - { label = Element.text options.label - , onPress = Just options.onPress - } -``` - -And update `src/Pages/Top.elm`: - -```elm -module Pages.Top exposing ( Model, Msg, page ) - -import Element -import Ui - -type Msg = SignIn | SignOut - -view : Element Msg -view = - Element.column [] - [ Ui.viewButton - { label = "Sign in" - , onPress = SignIn - } - , Ui.viewButton - { label = "Sign out" - , onPress = SignOut - } - ] -``` - -That makes our page a lot shorter, and using `Ui.viewButton` let's readers know -where that function is coming from! - -We can now reuse it on `src/Pages/Share.elm` easily! - -```elm -module Pages.Share exposing ( Model, Msg, page ) - -import Element -import Ui - -type Msg = ShareOnTwitter - -view : Element Msg -view = - Ui.viewButton - { label = "Share" - , onPress = ShareOnTwitter - } -``` - -### when to create a new module - -In Elm, we _usually_ make a module around data structures. The creator of the language, -Evan Czaplicki, has a [really great talk](https://www.youtube.com/watch?v=XpDsk374LDE) -about that idea here. - -For this site, I made the navbar into it's own file (at `src/Components/Navbar.elm`), -but I could have just as easily made a function in `src/Ui.elm` that exposed `viewNavbar`. - -Directly mapping ideas from JS frameworks like React may lead you down a frustrating path. -What makes sense for scaling a JavaScript app might not translate in Elm! - -If you find yourself creating components like this: - -```elm -module Components.Example exposing - ( Model - , Msg - , init - , update - , view - ) - --- code -``` - -You'll end up creating a verbosity problem for components (the same one -that __elm-spa__ was designed to fix for pages!) - -```elm -module Pages.Example exposing (Model, Msg, page) - -import Components.Foo as Foo -import Components.Bar as Bar -import Components.Baz as Baz - -type alias Model = - { foo : Foo.Model - , bar : Bar.Model - , baz : Baz.Model - } - -type Msg - = FromFoo Foo.Msg - | FromBar Bar.Msg - | FromBaz Baz.Msg - -view : Model -> Element Msg -view model = - Element.column [] - [ Element.map FromFoo (Foo.view model.foo) - , Element.map FromBar (Bar.view model.bar) - , Element.map FromBaz (Baz.view model.baz) - ] - -update : Msg -> Model -> Model -update msg model = - case msg of - FromFoo msg_ -> - { model | foo = Foo.update msg_ model.foo } - FromBar msg_ -> - { model | bar = Bar.update msg_ model.bar } - FromBaz msg_ -> - { model | baz = Baz.update msg_ model.baz } -``` - -There's nothing _wrong_ with the code in the example above! Maybe `Foo` needs to -be complex! - -But start with the simplest strategy first. Maybe `Bar` and `Baz` don't need to -follow that pattern: - -```elm -module Pages.Example exposing (Model, Msg, page) - -import Components.Foo as Foo -import Ui - -type alias Model = - { user : Maybe String - , foo : Foo.Model - } - -type Msg - = FromFoo Foo.Msg - | SignOut - -view : Model -> Element Msg -view model = - Element.column [] - [ Element.map FromFoo (Foo.view model.foo) - , Ui.viewBar model.username - , Ui.viewBaz { onClick = SignOut } - ] - -update : Msg -> Model -> Model -update msg model = - case msg of - FromFoo msg_ -> - { model | foo = Foo.update msg_ model.foo } - SignOut -> - { model | user = Nothing } -``` diff --git a/examples/docs/public/content/docs/deploying.md b/examples/docs/public/content/docs/deploying.md deleted file mode 100644 index 9f4778e..0000000 --- a/examples/docs/public/content/docs/deploying.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -{ "title": "deploying" -, "description": "sharing your app with the world!" -} ---- - - - -### using netlify - -Netlify is a free way to publish your app. - -When you run [elm-spa init](/docs/elm-spa/init), a file is automatically created -in your project named `netlify.toml`. - -Additionally, commands like `npm run build` have already been implemented to -make sharing your app easy! - -After you push your code up to Github, and create a free [Netlify account](https://netlify.com), -you should provide these details in your project's deploy settings: - -Setting | Value -:-- | :-- -__Build command__ | `npm run build` -__Publish directory__ | `public` - diff --git a/examples/docs/public/content/docs/elm-spa.md b/examples/docs/public/content/docs/elm-spa.md deleted file mode 100644 index 0bd4ec6..0000000 --- a/examples/docs/public/content/docs/elm-spa.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -{ "title": "elm-spa" -, "description": "a command line tool." -} ---- - - - -### using elm-spa - -If you have the latest [NodeJS](https://nodejs.org) installed, you already have the `npx` command. - -```bash -npx elm-spa help -``` - -The docs and guide use `npx`, because it has less issues than doing a global installation -with the `--global` flag. - -### installing elm-spa - -If you would rather just run `elm-spa` without the `npx` prefix, you can run this -command: - -```bash -npm install --global elm-spa -``` - -And if you don't receive any permissions issues, that's it! If you do, [this NPM article](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) -may be useful for you. - -Running `elm-spa help` should now work too! diff --git a/examples/docs/public/content/docs/elm-spa/add.md b/examples/docs/public/content/docs/elm-spa/add.md deleted file mode 100644 index 8e89f71..0000000 --- a/examples/docs/public/content/docs/elm-spa/add.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -{ "title": "elm-spa add" -, "description": "create new pages from the command line." -} ---- - - - -### typing is for nerds! - -Once you have created a new project with [elm-spa init](./init), you can use the `elm-spa add` command like this: - -```bash -npx elm-spa add static Example -``` - -That command will create a file at `src/Pages/Example.elm` that looks something like this: - -```elm -module Pages.Example exposing (Model, Msg, page) - --- imports - -page : Spa.Page Model Msg -page = - Page.static - { title = always "Example" - , view = always view - } - -view : Html Msg -view = - text "Example" -``` - -It's just a bit of boilerplate to get you started on creating your next thing. - -### creating more complex pages - -You can create any of the four page types with this command! - -```bash -npx elm-spa add static Example -``` - -```bash -npx elm-spa add sandbox Example -``` - -```bash -npx elm-spa add element Example -``` - -```bash -npx elm-spa add component Example -``` - - -### one more thing! - -there's one last perk when you use the `elm-spa add` command: - -it will automatically create layout files for you too! - -```bash -npx elm-spa add element Foo.Bar - -elm-spa created 2 files: - - /code/your-project/src/Layouts/Foo.elm - /code/your-project/src/Pages/Foo/Bar.elm - -``` diff --git a/examples/docs/public/content/docs/elm-spa/build.md b/examples/docs/public/content/docs/elm-spa/build.md deleted file mode 100644 index c133677..0000000 --- a/examples/docs/public/content/docs/elm-spa/build.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -{ "title": "elm-spa build" -, "description": "generate glue based on your pages." -} ---- - - - -### let's make a computer do it - -After you've created a new project with [elm-spa init](./init), you can use the -`elm-spa build` command in your project folder to generate routes, pages, and -url parameters: - -```bash -npx elm-spa build -``` - -Files will be created in the `elm-stuff/.elm-spa` folder - -The elm package, [ryannhg/elm-spa](https://package.elm-lang.org/packages/ryannhg/elm-spa/latest), -standardizes how pages are wired together, so we let the `build` command do all -the typing for you. - -As long as you follow the naming conventions in the pages folder, this command -will do all the work. \ No newline at end of file diff --git a/examples/docs/public/content/docs/elm-spa/init.md b/examples/docs/public/content/docs/elm-spa/init.md deleted file mode 100644 index 480992a..0000000 --- a/examples/docs/public/content/docs/elm-spa/init.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -{ "title": "elm-spa init" -, "description": "create a new project from the command line." -} ---- - - - -### the cli is your pal. - -If you want to create a new project, you can use the `elm-spa init` command like this: - -```bash -npx elm-spa init your-project -``` - -Here, you can replace `your-project` with whatever you like! - - -### choose your ui - -By default, running `elm-spa init` will use [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/), -a package that let's us build web UIs _without HTML or CSS_. - -It's pretty neat. - -If you'd like to use the standard [elm/html](https://package.elm-lang.org/packages/elm/html/latest/) -library, you can provide the `--ui` flag like this: - -```bash -npx elm-spa init --ui=Html your-project -``` - -This creates an `elm-spa.json` file with `Html` set as the `ui` option in your -project folder. - -That means `elm-spa add` will generate `import Html` instead of `import Element` -for all your new pages! \ No newline at end of file diff --git a/examples/docs/public/content/docs/faqs.md b/examples/docs/public/content/docs/faqs.md deleted file mode 100644 index 52b7e50..0000000 --- a/examples/docs/public/content/docs/faqs.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -{ "title": "faqs" -, "description": "common questions from the community!" -} ---- - -### how do i submit an faq? - -If you think a question is common enough to make it in here, you can submit it -to the `#elm-spa` channel in [the official Elm slack](https://elmlang.herokuapp.com/). - -If your question/answer combo gets 10 👍 emojis, it's officially considered "frequently asked" -and we'd be happy to add it in! - -It's a ridiculous system, and I'm very excited to see if it actually works. \ No newline at end of file diff --git a/examples/docs/public/content/docs/layouts.md b/examples/docs/public/content/docs/layouts.md deleted file mode 100644 index 4c26461..0000000 --- a/examples/docs/public/content/docs/layouts.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -{ "title": "layouts" -, "description": "shared views for your pages" -} ---- - - - -### what are layouts for? - -Take a look at the navbar on this site. That component is used on _every_ page. - -When you click a link up there, the __page__ content fades from view, but the -navbar itself stays persistent. - -That's because we render our navbar component in the `src/Layout.elm` file. - -Layouts enable elm-spa to transition changing pages, without transitioning out -stuff that should persist. - -### want another example? - -For users viewing this site on a wider screen (like a laptop or desktop), -this docs page has a sidebar on the left side of the page. - -If you navigate to `/` or `/guide` using the top navbar, notice that the sidebar -fades smoothly from view? - -Now click on a link in the sidebar itself. It doesn't fade away! - -Magic? Maybe– but really it's just because that sidebar component is rendered -in the `Layouts/Docs.elm` file. - -The next section will show you more details on how we can leverage that to make -the single page app transition in an expected way! diff --git a/examples/docs/public/content/docs/layouts/transitions.md b/examples/docs/public/content/docs/layouts/transitions.md deleted file mode 100644 index b5afb9b..0000000 --- a/examples/docs/public/content/docs/layouts/transitions.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -{ "title": "transitions" -, "description": "unlock client-side superpowers" -} ---- - - - -### client-side rendering with elm - -When you click a link on this app, Elm doesn't refetch the entire page back from -the server. Instead, it renders only the components that we want to change, and -updates the URL for you. - -This enables us to use __page transitions__ to improve user experience. - -### how does it work? - -Each elm-spa project looks for a file called `src/Transitions.elm` to describe -two things: - -- How the entire app should enter the screen. - -- How pages should exit/enter the screen. - -Here's the default when you create a project with [elm-spa init](/docs/elm-spa/init): - -```elm -module Transitions exposing (transitions) - -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition -import Utils.Spa as Spa - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeElmUi 300 - , page = Transition.fadeElmUi 300 - , pages = [] - } -``` - -When you refresh the page, the whole app will use the `layout` option -and fades it in over 300 milliseconds. - -When you click an internal link, pages will fade out and fade in over -300 milliseconds. - -### disabling transitions - -You can disable transitions with `Transition.none`: - -```elm -module Transitions exposing (transitions) - -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition -import Utils.Spa as Spa - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.none - , page = Transition.none - , pages = [] - } -``` - -### custom transitions - -Fading isn't your thing? Cool! You can use `Transition.custom` to define your own -transitions. - -Just provide three properties: - -- `duration` - how long the animation lasts - -- `invisible` - what the page looks like when invisible - -- `visible` - what the page looks like when visible - -Here's an example with Elm UI: - -```elm -import Element exposing (..) -import Ui -import Spa.Transition as Transition -import Utils.Spa as Spa - -scaleTransition : - Int - -> Spa.Transition (Element msg) -scaleTransition duration = - Transition.custom - { duration = duration - , invisible = - \page -> - el - [ scale 0, Ui.transition duration ] - page - , visible = - \page -> - el - [ scale 1, Ui.transition duration ] - page - } -``` - -And here's an example with HTML and CSS: - - -```elm -import Html exposing (..) -import Html.Attributes as Attr -import Spa.Transition as Transition -import Utils.Spa as Spa - -scaleTransition : - Int - -> Spa.Transition (Html msg) -scaleTransition duration = - Transition.custom - { duration = duration - , invisible = - \page -> - div [ Attr.style "transform" "scale(0)" - , Attr.style "transition" ("transform " ++ duration ++ "ms") - ] - [ page ] - , visible = - \page -> - div [ Attr.style "transform" "scale(1)" - , Attr.style "transition" ("transform " ++ duration ++ "ms") - ] - [ page ] - } -``` - -### only transitioning views that change - -You may have noticed a `pages` property from the first example: - -```elm -module Transitions exposing (transitions) - -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition -import Utils.Spa as Spa - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeElmUi 300 - , page = Transition.fadeElmUi 300 - , pages = [] - } -``` - -By default, this value is just an empty list. The `pages` property is a way to -prevent things like common sidebars from fading in and out when you click links -in the same view. - -Here's how this site uses the `pages` property to prevent the docs sidebar from -fading out when navigating between `/docs/*` routes: - -```elm -module Transitions exposing (transitions) - -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition -import Utils.Spa as Spa - - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeElmUi 500 - , page = Transition.fadeElmUi 300 - , pages = - [ { path = Docs.path - , transition = Transition.fadeElmUi 300 - } - ] - } -``` - -Because the sidebar is rendered in `src/Layouts/Docs.elm`, anytime we navigate -within the `Docs.path` (`/docs/*`) routes, it will stay in view. - -Here we chose to use the same transition that the other `page` property uses. \ No newline at end of file diff --git a/examples/docs/public/content/docs/pages.md b/examples/docs/public/content/docs/pages.md deleted file mode 100644 index 9c26129..0000000 --- a/examples/docs/public/content/docs/pages.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -{ "title": "pages" -, "description": "views rendered at some url." -} ---- - - - -### how routing works - -With elm-spa, creating a file in `src/Pages` automatically takes care of routing -and plugs your new page where it belongs in your layouts and single page app. - -If you have a folder structure like this: - -```elm -src/ - Pages/ - Top.elm - Guide.elm - NotFound.elm - Docs/ - Dynamic.elm -``` - -Will generate routes like these: - -Path | Elm file -:-- | :-- -`/` | `src/Pages/Top.elm` -`/guide` | `src/Pages/Guide.elm` -`/not-found` | `src/Pages/NotFound.elm` -`/docs/pages` | `src/Pages/Docs/Dynamic.elm` -`/docs/elm-spa` | `src/Pages/Docs/Dynamic.elm` -`/docs/deploying` | `src/Pages/Docs/Dynamic.elm` - -#### naming pages - -The names of your files correspond to the routes in your app. - -Creating an `Example.elm` file tells elm-spa to generate the route `/example` - -You can nest files in folders like `SomeFolder/Example.elm` and that will -result in `/some-folder/example`. - -Note that capitalization in module names creates a dash (`-`) in between the -URL. - -#### top-level routes - -To declare top-level routes (like for the homepage), elm-spa reserves the name -`Top` to allow you to do things like this: - -Path | Elm file -:-- | :-- -`/` | `src/Pages/Top.elm` -`/guide` | `src/Pages/Guide/Top.elm` -`/top` | `src/Pages/Top/Top.elm` - -Only the filename is reserved, so you can create `/top` if you like! - -#### dynamic routes - -If you want to use the same page to match different routes, you can use -`Dynamic` as the file or folder name. - -The dynamic parameters will be available to your `init` function. - -Here are some examples: - - -##### src/Pages/Dynamic.elm - -Path | Params -:-- | :-- -`/foo` | `{ param1 = "foo" }` -`/bar` | `{ param1 = "bar" }` -`/123` | `{ param1 = "123" }` - -##### src/Pages/Docs/Dynamic.elm - -Path | Params -:-- | :-- -`/docs/foo` | `{ param1 = "foo" }` -`/docs/bar` | `{ param1 = "bar" }` -`/docs/123` | `{ param1 = "123" }` - -##### src/Pages/Dynamic/Dynamic.elm - -Path | Params -:-- | :-- -`/hello/foo` | `{ param1 = "hello", param2 = "foo" }` -`/from/bar` | `{ param1 = "from", param2 = "bar" }` -`/anything/123` | `{ param1 = "anything", param2 = "123" }` - - -### choosing the right page - -the following sections show off the 4 types of pages you can -create with elm-spa! \ No newline at end of file diff --git a/examples/docs/public/content/docs/pages/component.md b/examples/docs/public/content/docs/pages/component.md deleted file mode 100644 index e177968..0000000 --- a/examples/docs/public/content/docs/pages/component.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -{ "title" : "Page.component" -, "description": "pages that make global updates." -} ---- - - - -### pages that make global updates. - -A "component" page is just a [Page.element](./element) that can update the global state -by sending messages! - -Both `init` and `update` now return something like this: - -```elm -( Model, Cmd Msg, Cmd Global.Msg ) -``` - -We can use `elm-spa add` to create a component page like this: - -```bash -npx elm-spa add component SignIn -``` - -A sign in page is a good example of when we would reach for a component instead -of an element. - -We can have the update function send out a `Global` message to update the logged -in user. - -It's also very common to omit the `always` to give our functions access -to the `Global.Model` from the page context. - -```elm -Page.component - { title = always title - , init = init -- *removed always - , update = update -- *removed always - , view = view -- *removed always - , subscriptions = always subscriptions - } -``` - -Maybe your `init` does something like this: - - -```elm -import Global - -type alias Model = - { user : Maybe User - } - -type Msg = NoOp - -init : - PageContext - -> Params.SignIn - -> ( Model, Cmd Msg, Cmd Global.Msg ) -init context _ = - ( { user = context.global.user } - , Cmd.none - , Global.SignIn "ryan@elm-spa.dev" - ) -``` diff --git a/examples/docs/public/content/docs/pages/element.md b/examples/docs/public/content/docs/pages/element.md deleted file mode 100644 index 94835a6..0000000 --- a/examples/docs/public/content/docs/pages/element.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -{ "title" : "Page.element" -, "description": "pages with state and side-effects." -} ---- - - - -### pages with state and side-effects. - -Okay so I lied about `/docs/pages/static` using [Page.static](./static). We actually -need to make an HTTP request on `init` to fetch content from [this markdown file](/content/docs/pages/element.md) that you're reading. - -So for that reason, we're going to upgrade to a `Page.element`– which is able to -send `Cmd Msg` and do cool things like making web requests. - -Let's use `elm-spa add` to create an element page like this: - -```bash -npx elm-spa add element Docs.Dynamic.Dynamic -``` - -Since we are using `Dynamic`, this route will match paths like these: - -- `/docs/pages/static` -- `/docs/pages/element` -- `/docs/elm-spa/add` - -For that first example, our `init` function receives `Params` that look like this: - -```elm -{ param1 = "pages" -, param2 = "static" -} -``` - -(You can read more about this in the [pages overview](/docs/pages)) \ No newline at end of file diff --git a/examples/docs/public/content/docs/pages/sandbox.md b/examples/docs/public/content/docs/pages/sandbox.md deleted file mode 100644 index 34ce0b6..0000000 --- a/examples/docs/public/content/docs/pages/sandbox.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -{ "title" : "Page.sandbox" -, "description": "pages with local state." -} ---- - - - -### pages with local state. - -If you want to keep track of something like tabs, an accordion, or something like -Elm's [classic counter example](https://elm-lang.org/examples/buttons), you'll need -to upgrade from [Page.static](./static) to `Page.sandbox` - -We can use `elm-spa add` to create a sandbox page like this: - -```bash -npx elm-spa add sandbox Counter -``` - -This will create a file called `src/Pages/Counter.elm` that looks like this: - -```elm -module Pages.Example exposing - ( Model - , Msg - , page - ) - -import Spa.Page -import Element exposing (..) -import Generated.Params as Params -import Utils.Spa exposing (Page) - - -page : Page Params.Counter Model Msg model msg appMsg -page = - Spa.Page.sandbox - { title = always "Counter" - , init = always init - , update = always update - , view = always view - } - - - --- INIT - - -type alias Model = - {} - - -init : Params.Counter -> Model -init _ = - {} - - - --- UPDATE - - -type Msg - = Msg - - -update : Msg -> Model -> Model -update msg model = - model - - - --- VIEW - - -view : Model -> Element Msg -view model = - text "Counter" -``` - -### adding in features - -From there, we can begin to implement something like a counter by updating the -individual pieces of the page: - -```elm -type Model = - { counter : Int - } -``` - -```elm -init : Params.Counter -> Model -init _ = - { counter = 0 - } -``` - -```elm -type Msg - = Increment - | Decrement -``` - -```elm -update : Msg -> Model -> Model -update msg model = - case msg of - Increment -> - { model | counter = model.counter + 1 } - Decrement -> - { model | counter = model.counter - 1 } -``` - -```elm -view : Model -> Html Msg -view model = - column [] - [ button [] - { onPress = Just Decrement - , label = text "-" - } - , text (String.fromInt model.counter) - , button [] - { onPress = Just Increment - , label = text "+" - } - ] -``` - -Next thing you know, we've got a page at `/counter` with our counter app! \ No newline at end of file diff --git a/examples/docs/public/content/docs/pages/static.md b/examples/docs/public/content/docs/pages/static.md deleted file mode 100644 index 026ff69..0000000 --- a/examples/docs/public/content/docs/pages/static.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -{ "title" : "Page.static" -, "description": "pages without any local state." -} ---- - - - -### pages without state. - -Look at this page for example, it's just content. We only care about providing a -`view` function that can't send any messages. - -We can use `elm-spa add` to create a page like this: - -```bash -npx elm-spa add static Docs.Pages.Static -``` - -By choosing the `static` keyword in the command above, elm-spa generates this for -us: - -```elm -module Pages.Docs.Pages.Static exposing - ( Model - , Msg - , page - ) - -import Spa.Page -import Element exposing (..) -import Generated.Docs.Pages.Params as Params -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Static Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "Docs.Pages.Static" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - text "Docs.Pages.Static" -``` - -From there, we change the `view` function to render whatever we like! diff --git a/examples/docs/public/content/guide/getting-started.md b/examples/docs/public/content/guide/getting-started.md deleted file mode 100644 index 103446f..0000000 --- a/examples/docs/public/content/guide/getting-started.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -{ "title": "getting started" -, "description": "easy peasy, web app... squeezy? 🤔" -} ---- - - - -## don't have elm installed? - -No worries, you can [get installation here](./installation) or click the link on the left. It'll bring you back when you're done! \ No newline at end of file diff --git a/examples/docs/public/content/guide/installation.md b/examples/docs/public/content/guide/installation.md deleted file mode 100644 index 917c2a0..0000000 --- a/examples/docs/public/content/guide/installation.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -{ "title": "installing things" -, "description": "getting your computer ready to roll." -} ---- - - - -### what's in this section? - -Together, we're going to: - -1. install node -1. install elm via npm -1. install your text editor - -### installing node - -Installing NodeJS from [nodejs.org](https://nodejs.org/) will allow us to run `npm` commands from our terminal or command prompt. - -Make sure to get the latest LTS release (at the time of writing this, that was `12.13.1 LTS`) - -#### did it work? - -You can confirm that node is installed by running this command - -```bash -npm -v -``` - -(that should spit out a number like `6.11.3`, though it doesn't need to that exact number) - -### installing all the elm things - -We can use `npm` to install `elm` and a few other tools. - -```bash -npm install -g elm elm-format elm-live elm-spa -``` - -These things will be useful for running commands in the terminal, and help us with the next section! - -### installing a text editor - -There's plenty of choices out there, but for this guide I'll be using: - -- [VS Code](https://code.visualstudio.com/) - -Once you have that installed, we can start getting our Elm dev environment -setup! - -Install the [Elm](https://marketplace.visualstudio.com/items?itemName=Elmtooling.elm-ls-vscode) extension so we get syntax highlighting and -magical format-on-save technology! - -You can run `Ctrl+Shift+P` in VS code to add these settings to your user settings: - -```js -{ - // ... - "[elm]": { - "editor.formatOnSave": true, - "editor.tabSize": 4 - } -} -``` - -That's it! You're ready for the next section! \ No newline at end of file diff --git a/examples/docs/public/favicon.png b/examples/docs/public/favicon.png deleted file mode 100644 index 9222eed..0000000 Binary files a/examples/docs/public/favicon.png and /dev/null differ diff --git a/examples/docs/public/index.html b/examples/docs/public/index.html deleted file mode 100644 index 89e7c82..0000000 --- a/examples/docs/public/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - elm-spa - - - - - - - - - - - - - diff --git a/examples/docs/public/ports.js b/examples/docs/public/ports.js deleted file mode 100644 index 476f44e..0000000 --- a/examples/docs/public/ports.js +++ /dev/null @@ -1,17 +0,0 @@ -const Ports = { - // Called by index.html - init: (app) => { - if (app.ports && app.ports.outgoing) { - app.ports.outgoing.subscribe(({ action, data }) => - Ports.actions[action] - ? Ports.actions[action](data) - : console.warn(`I didn't recognize action "${action}".`) - ) - } - }, - // Maps an action name to its handler - actions: { - 'SCROLL_TO_TOP': _ => - window.scroll({ top: 0, left: 0, behavior: 'smooth' }) - } -} \ No newline at end of file diff --git a/examples/docs/public/styles.css b/examples/docs/public/styles.css deleted file mode 100644 index bdf2e7a..0000000 --- a/examples/docs/public/styles.css +++ /dev/null @@ -1,133 +0,0 @@ -/* you can include CSS here */ -html, body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - height: 100%; - font-size: 16px; -} - -.markdown { - max-width: 40em; -} - -.markdown > * { - margin-bottom: 0; - margin-top: 1rem; -} - -.markdown > *:first-child { - margin-top: 0; - padding-top: 0; -} - -.markdown h3 { - margin-top: 1rem; - padding-top: 2rem; - line-height: 1.1; - font-size: 2rem; -} - -.markdown h4 { - margin-top: 0.25rem; - margin-bottom: -0.5rem; - padding-top: 1rem; - line-height: 1.2; - font-size: 1.375rem; -} - -.markdown h5 { - opacity: 0.6; - margin-top: 0.25rem; - padding-top: 1rem; - margin-bottom: -0.5rem; - line-height: 1.3; - font-size: 1.125rem; -} - -.markdown p, ul, ol { - line-height: 1.4; - font-size: 18px; -} - -.markdown p > code { - font-family: 'Source Code Pro', monospace; - padding: 0.025em 0.25em; - border: solid 1px #ccc; - border-radius: 4px; - font-size: 16px; - white-space: nowrap; -} - -.markdown h5 > code { - font-family: 'Source Code Pro', monospace; - padding: 0.025em 0.25em; -} - -.markdown pre > code { - display: block; - border: solid 1px #eee; - border-radius: 4px; - padding: 1rem; - margin: 0 calc(4px - 1rem); - margin-top: 1rem; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); - overflow: auto; -} - -.markdown pre > code.lang-bash { - background: #333; - color: white; -} - -.markdown pre > code.lang-bash::before { - content: '$ '; - user-select: none; - opacity: 0.5; -} - -.markdown a { - color: rgb(200, 75, 85); -} - -.markdown ul { - padding-left: 1.5rem; -} - -.markdown ol ol { - list-style: lower-alpha; -} - -.markdown ol ol ol { - list-style: lower-roman; -} - -.markdown iframe { - width: 100%; - height: calc(56.25vw - 2rem); - max-width: 560px; - max-height: 315px; - margin-top: 2em; - background: #eee; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); - border: solid 1px #ccc; - border-radius: 4px; -} - -.markdown table { - display: block; - max-width: 100%; - overflow-x: auto; - margin: 1rem 0; -} - -.markdown thead { - font-size: 18px; - font-weight: 600; -} - -.markdown th { - padding-bottom: 4px; -} - -.markdown td { - padding-right: 1em; -} \ No newline at end of file diff --git a/examples/docs/src/Components/Hero.elm b/examples/docs/src/Components/Hero.elm deleted file mode 100644 index acfc164..0000000 --- a/examples/docs/src/Components/Hero.elm +++ /dev/null @@ -1,57 +0,0 @@ -module Components.Hero exposing (Options, view) - -import Element exposing (..) -import Element.Font as Font -import Element.Region as Region -import Ui exposing (styles) - - -type alias Options = - { title : String - , subtitle : String - , links : - List - { label : String - , url : String - } - } - - -view : Options -> Element msg -view options = - column - [ paddingEach - { top = 120 - , left = 0 - , right = 0 - , bottom = 64 - } - , centerX - , spacing 24 - ] - [ column [ spacing 16, centerX ] - [ el - [ Font.size 64 - , Font.bold - , centerX - , Region.heading 1 - ] - (text options.title) - , el - [ Font.size 24 - , centerX - , alpha 0.5 - , Region.heading 2 - ] - (text options.subtitle) - ] - , wrappedRow [ spacing 12, centerX ] <| - List.map - (\{ label, url } -> - link styles.button - { label = text label - , url = url - } - ) - options.links - ] diff --git a/examples/docs/src/Components/Navbar.elm b/examples/docs/src/Components/Navbar.elm deleted file mode 100644 index c034887..0000000 --- a/examples/docs/src/Components/Navbar.elm +++ /dev/null @@ -1,40 +0,0 @@ -module Components.Navbar exposing (view) - -import Element exposing (..) -import Element.Font as Font -import Generated.Routes as Routes exposing (Route, routes) -import Ui exposing (colors, styles) - - -view : Route -> Element msg -view currentRoute = - row - [ spacing 16 - , centerX - , width fill - ] - [ row [ Font.color colors.coral, spacing 16 ] - [ el [ Font.semiBold, Font.size 20 ] - (viewLink currentRoute ( "elm-spa", routes.top )) - , viewLink currentRoute ( "docs", routes.docs_top ) - , viewLink currentRoute ( "guide", routes.guide ) - ] - , el [ alignRight ] <| - link styles.button - { label = text "get started" - , url = "/guide" - } - ] - - -viewLink : Route -> ( String, Route ) -> Element msg -viewLink currentRoute ( label, route ) = - if currentRoute == route then - el styles.link.disabled - (text label) - - else - link styles.link.enabled - { label = text label - , url = Routes.toPath route - } diff --git a/examples/docs/src/Components/README.md b/examples/docs/src/Components/README.md deleted file mode 100644 index edac699..0000000 --- a/examples/docs/src/Components/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Components -> views shared across the site \ No newline at end of file diff --git a/examples/docs/src/Components/Sidebar.elm b/examples/docs/src/Components/Sidebar.elm deleted file mode 100644 index 1b3ef8f..0000000 --- a/examples/docs/src/Components/Sidebar.elm +++ /dev/null @@ -1,195 +0,0 @@ -module Components.Sidebar exposing - ( viewDocLinks - , viewGuideLinks - , viewNextDocsArticle - , viewNextGuideArticle - ) - -import Element exposing (..) -import Element.Font as Font -import Generated.Routes as Routes exposing (Route, routes) -import Ui exposing (colors, styles) - - -type SideItem - = Heading String - | Link ( String, Route ) - - -guideLinks : List SideItem -guideLinks = - [ Link ( "intro", routes.guide ) - , Link ( "installation", routes.guide_dynamic "installation" ) - , Link ( "getting started", routes.guide_dynamic "getting-started" ) - ] - - -docsLinks : List SideItem -docsLinks = - [ Link ( "overview", routes.docs_top ) - , Heading "elm-spa" - , Link ( "overview", routes.docs_dynamic "elm-spa" ) - , Link ( "elm-spa init", routes.docs_dynamic_dynamic "elm-spa" "init" ) - , Link ( "elm-spa add", routes.docs_dynamic_dynamic "elm-spa" "add" ) - , Link ( "elm-spa build", routes.docs_dynamic_dynamic "elm-spa" "build" ) - , Heading "pages" - , Link ( "overview", routes.docs_dynamic "pages" ) - , Link ( "static", routes.docs_dynamic_dynamic "pages" "static" ) - , Link ( "sandbox", routes.docs_dynamic_dynamic "pages" "sandbox" ) - , Link ( "element", routes.docs_dynamic_dynamic "pages" "element" ) - , Link ( "component", routes.docs_dynamic_dynamic "pages" "component" ) - , Heading "layouts" - , Link ( "overview", routes.docs_dynamic "layouts" ) - , Link ( "transitions", routes.docs_dynamic_dynamic "layouts" "transitions" ) - , Heading "other things" - , Link ( "components", routes.docs_dynamic "components" ) - , Link ( "deploying", routes.docs_dynamic "deploying" ) - , Link ( "faqs", routes.docs_dynamic "faqs" ) - ] - - -type Match - = NoMatchFound - | FoundMatch - | HereItIs ( String, Route ) - - -nextArticle : Route -> List SideItem -> Maybe ( String, Route ) -nextArticle activeRoute items = - items - |> List.filterMap - (\item -> - case item of - Heading _ -> - Nothing - - Link tuple -> - Just tuple - ) - |> List.foldl - (\item match -> - case match of - NoMatchFound -> - if Tuple.second item == activeRoute then - FoundMatch - - else - NoMatchFound - - FoundMatch -> - HereItIs item - - HereItIs i -> - HereItIs i - ) - NoMatchFound - |> (\match -> - case match of - NoMatchFound -> - Nothing - - FoundMatch -> - Nothing - - HereItIs item -> - Just item - ) - - -viewNextDocsArticle = - viewNextArticle - { items = docsLinks - , nextUp = - { label = text "the guide" - , url = "/guide" - } - } - - -viewNextGuideArticle = - viewNextArticle - { items = guideLinks - , nextUp = - { label = text "the docs" - , url = "/docs" - } - } - - -viewNextArticle : - { nextUp : - { label : Element msg - , url : String - } - , items : List SideItem - } - -> Route - -> Element msg -viewNextArticle options route = - (\link -> paragraph [ Font.size 20 ] [ el [ Font.semiBold ] <| text "next up: ", link ]) <| - case nextArticle route options.items of - Just ( label, r ) -> - link (Font.color colors.coral :: styles.link.enabled) - { label = text label - , url = Routes.toPath r - } - - Nothing -> - link (Font.color colors.coral :: styles.link.enabled) - options.nextUp - - -viewDocLinks : Route -> Element msg -viewDocLinks = - view - { items = docsLinks - , heading = "docs" - } - - -viewGuideLinks : Route -> Element msg -viewGuideLinks = - view - { items = guideLinks - , heading = "guide" - } - - -view : { heading : String, items : List SideItem } -> Route -> Element msg -view { heading, items } activeRoute = - column - [ alignTop - , spacing 16 - , width (px 200) - , paddingEach { top = 84, left = 0, right = 0, bottom = 0 } - ] - [ el [ Font.size 24, Font.semiBold ] (text heading) - , column [ spacing 8 ] (List.map (viewSideLink activeRoute) items) - ] - - -viewSideLink : Route -> SideItem -> Element msg -viewSideLink activeRoute item = - case item of - Heading label -> - el - [ alpha 0.5 - , Font.size 18 - , Font.semiBold - , paddingEach { top = 8, left = 0, right = 0, bottom = 0 } - ] - (text label) - - Link ( label, route ) -> - let - linkStyles = - if route == activeRoute then - styles.link.disabled - - else - styles.link.enabled - in - link (Font.color colors.coral :: linkStyles) - { label = text label - , url = Routes.toPath route - } diff --git a/examples/docs/src/Global.elm b/examples/docs/src/Global.elm deleted file mode 100644 index 87fe441..0000000 --- a/examples/docs/src/Global.elm +++ /dev/null @@ -1,85 +0,0 @@ -module Global exposing - ( Device(..) - , Flags - , Model - , Msg(..) - , init - , subscriptions - , update - ) - -import Browser.Dom as Dom -import Browser.Events as Events -import Generated.Routes exposing (Route) -import Ports -import Task - - -type alias Flags = - () - - -type alias Model = - { device : Device - } - - -type Device - = Mobile - | Desktop - - -type Msg - = ScreenResized Int Int - | GotViewport Dom.Viewport - | AfterNavigate { old : Route, new : Route } - - -type alias GlobalContext msg = - { navigate : Route -> Cmd msg - } - - -init : GlobalContext msg -> Flags -> ( Model, Cmd Msg, Cmd msg ) -init _ _ = - ( { device = Desktop - } - , Task.perform GotViewport Dom.getViewport - , Cmd.none - ) - - -update : GlobalContext msg -> Msg -> Model -> ( Model, Cmd Msg, Cmd msg ) -update _ msg model = - case msg of - AfterNavigate _ -> - ( model - , Cmd.none - , Ports.scrollToTop - ) - - ScreenResized width _ -> - ( { model | device = deviceFrom width } - , Cmd.none - , Cmd.none - ) - - GotViewport { viewport } -> - ( { model | device = deviceFrom (floor viewport.width) } - , Cmd.none - , Cmd.none - ) - - -deviceFrom : Int -> Device -deviceFrom width = - if width > 715 then - Desktop - - else - Mobile - - -subscriptions : Model -> Sub Msg -subscriptions _ = - Events.onResize ScreenResized diff --git a/examples/docs/src/Layout.elm b/examples/docs/src/Layout.elm deleted file mode 100644 index 99ac579..0000000 --- a/examples/docs/src/Layout.elm +++ /dev/null @@ -1,75 +0,0 @@ -module Layout exposing (view) - -import Components.Navbar as Navbar -import Element exposing (..) -import Element.Font as Font -import Element.Region as Region -import Global -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, route, global } = - column - [ height fill - , width (fill |> maximum 780) - , centerX - , Font.size 16 - , Font.family - [ Font.external - { name = "Source Sans Pro" - , url = "https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" - } - , Font.sansSerif - ] - , paddingEach - { top = 32 - , left = 20 - , right = 20 - , bottom = 0 - } - ] - [ el [ Region.navigation, width fill ] (Navbar.view route) - , el [ Region.mainContent, width fill, height fill ] page - , el [ Region.footer, width fill ] (viewFooter global) - ] - - -viewFooter : Global.Model -> Element msg -viewFooter { device } = - let - message = - text "this site was built with elm-spa!" - - externalLinks = - [ newTabLink [] - { label = text "the elm package" - , url = "https://package.elm-lang.org/packages/ryannhg/elm-spa/latest" - } - , newTabLink [] - { label = text "github" - , url = "https://github.com/ryannhg/elm-spa" - } - ] - in - case device of - Global.Mobile -> - column - [ width fill - , paddingEach { top = 96, left = 0, right = 0, bottom = 48 } - , spacing 16 - , alpha 0.5 - ] - [ message - , row [ spacing 8, Font.underline ] externalLinks - ] - - Global.Desktop -> - row - [ width fill - , paddingEach { top = 96, left = 0, right = 0, bottom = 48 } - , alpha 0.5 - ] - [ message - , row [ spacing 8, alignRight, Font.underline ] externalLinks - ] diff --git a/examples/docs/src/Layouts/Docs.elm b/examples/docs/src/Layouts/Docs.elm deleted file mode 100644 index f933088..0000000 --- a/examples/docs/src/Layouts/Docs.elm +++ /dev/null @@ -1,22 +0,0 @@ -module Layouts.Docs exposing (view) - -import Components.Sidebar as Sidebar -import Element exposing (..) -import Global -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, route, global } = - case global.device of - Global.Mobile -> - column [ width fill ] - [ page - , Sidebar.viewDocLinks route - ] - - Global.Desktop -> - row [ width fill ] - [ Sidebar.viewDocLinks route - , el [ width fill, alignTop ] page - ] diff --git a/examples/docs/src/Layouts/Docs/Dynamic.elm b/examples/docs/src/Layouts/Docs/Dynamic.elm deleted file mode 100644 index 02b15c9..0000000 --- a/examples/docs/src/Layouts/Docs/Dynamic.elm +++ /dev/null @@ -1,9 +0,0 @@ -module Layouts.Docs.Dynamic exposing (view) - -import Element exposing (..) -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page } = - page \ No newline at end of file diff --git a/examples/docs/src/Layouts/Guide.elm b/examples/docs/src/Layouts/Guide.elm deleted file mode 100644 index 01ee7bc..0000000 --- a/examples/docs/src/Layouts/Guide.elm +++ /dev/null @@ -1,22 +0,0 @@ -module Layouts.Guide exposing (view) - -import Components.Sidebar as Sidebar -import Element exposing (..) -import Global -import Utils.Spa as Spa - - -view : Spa.LayoutContext msg -> Element msg -view { page, route, global } = - case global.device of - Global.Mobile -> - column [ width fill ] - [ page - , Sidebar.viewGuideLinks route - ] - - Global.Desktop -> - row [ width fill ] - [ Sidebar.viewGuideLinks route - , el [ width fill, alignTop ] page - ] diff --git a/examples/docs/src/Layouts/README.md b/examples/docs/src/Layouts/README.md deleted file mode 100644 index b8e461e..0000000 --- a/examples/docs/src/Layouts/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Layouts -> where all your pages go \ No newline at end of file diff --git a/examples/docs/src/Main.elm b/examples/docs/src/Main.elm deleted file mode 100644 index cda6caa..0000000 --- a/examples/docs/src/Main.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Main exposing (main) - -import Generated.Pages as Pages -import Generated.Routes as Routes exposing (routes) -import Global -import Spa -import Transitions - - -main : Spa.Program Global.Flags Global.Model Global.Msg Pages.Model Pages.Msg -main = - Spa.create - { ui = Spa.usingElmUi - , transitions = Transitions.transitions - , routing = - { routes = Routes.parsers - , toPath = Routes.toPath - , notFound = routes.notFound - , afterNavigate = Just Global.AfterNavigate - } - , global = - { init = Global.init - , update = Global.update - , subscriptions = Global.subscriptions - } - , page = Pages.page - } diff --git a/examples/docs/src/Pages/Docs/Dynamic.elm b/examples/docs/src/Pages/Docs/Dynamic.elm deleted file mode 100644 index 47f6217..0000000 --- a/examples/docs/src/Pages/Docs/Dynamic.elm +++ /dev/null @@ -1,104 +0,0 @@ -module Pages.Docs.Dynamic exposing (Model, Msg, page) - -import Components.Sidebar as Sidebar -import Element exposing (..) -import Generated.Docs.Params as Params -import Generated.Routes exposing (Route) -import Http -import Spa.Page -import Ui -import Utils.Markdown as Markdown exposing (Markdown) -import Utils.Spa exposing (Page, PageContext) -import Utils.WebData as WebData exposing (WebData(..)) - - -page : Page Params.Dynamic Model Msg model msg appMsg -page = - Spa.Page.element - { title = \{ model } -> String.join " | " [ model.slug, "docs", "elm-spa" ] - , init = init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -type alias Model = - { slug : String - , markdown : WebData (Markdown Markdown.Frontmatter) - , route : Route - } - - -init : PageContext -> Params.Dynamic -> ( Model, Cmd Msg ) -init { route } { param1 } = - ( { slug = param1 - , markdown = Loading - , route = route - } - , Http.get - { url = "/" ++ String.join "/" [ "content", "docs", param1 ++ ".md" ] - , expect = WebData.expectMarkdown Loaded - } - ) - - - --- UPDATE - - -type Msg - = Loaded (WebData (Markdown Markdown.Frontmatter)) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Loaded data -> - ( { model | markdown = data } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - , spacing 32 - , alpha - (if model.markdown == WebData.Loading then - 0 - - else - 1 - ) - , Ui.transition { props = [ "opacity" ], duration = 300 } - ] - [ Ui.webDataMarkdownArticle model.markdown - , case model.markdown of - WebData.Success _ -> - Sidebar.viewNextDocsArticle model.route - - WebData.Failure _ -> - Sidebar.viewNextDocsArticle model.route - - _ -> - text "" - ] diff --git a/examples/docs/src/Pages/Docs/Dynamic/Dynamic.elm b/examples/docs/src/Pages/Docs/Dynamic/Dynamic.elm deleted file mode 100644 index ddd24c1..0000000 --- a/examples/docs/src/Pages/Docs/Dynamic/Dynamic.elm +++ /dev/null @@ -1,111 +0,0 @@ -module Pages.Docs.Dynamic.Dynamic exposing (Model, Msg, page) - -import Components.Sidebar as Sidebar -import Element exposing (..) -import Generated.Docs.Dynamic.Params as Params -import Generated.Routes exposing (Route) -import Http -import Spa.Page -import Ui -import Utils.Markdown as Markdown exposing (Markdown) -import Utils.Spa exposing (Page, PageContext) -import Utils.WebData as WebData exposing (WebData) - - -page : Page Params.Dynamic Model Msg model msg appMsg -page = - Spa.Page.element - { title = \{ model } -> String.join " | " [ model.slug, model.section, "docs", "elm-spa" ] - , init = init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -type alias Model = - { section : String - , slug : String - , markdown : WebData (Markdown Markdown.Frontmatter) - , route : Route - } - - -init : PageContext -> Params.Dynamic -> ( Model, Cmd Msg ) -init context { param1, param2 } = - ( { section = param1 - , slug = param2 - , markdown = WebData.Loading - , route = context.route - } - , Http.get - { url = "/" ++ String.join "/" [ "content", "docs", param1, param2 ++ ".md" ] - , expect = WebData.expectMarkdown Loaded - } - ) - - - --- UPDATE - - -type Msg - = Loaded (WebData (Markdown Markdown.Frontmatter)) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Loaded data -> - ( { model | markdown = data } - , Cmd.none - ) - - - --- SUSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - , spacing 32 - , alpha - (if model.markdown == WebData.Loading then - 0 - - else - 1 - ) - , Ui.transition { props = [ "opacity" ], duration = 300 } - ] - [ Ui.webDataMarkdownArticle model.markdown - , case model.markdown of - WebData.Success _ -> - Sidebar.viewNextDocsArticle model.route - - WebData.Failure _ -> - Sidebar.viewNextDocsArticle model.route - - _ -> - text "" - ] - - -viewTitle : Model -> String -viewTitle model = - model.section ++ " " ++ model.slug diff --git a/examples/docs/src/Pages/Docs/Top.elm b/examples/docs/src/Pages/Docs/Top.elm deleted file mode 100644 index 89e22f7..0000000 --- a/examples/docs/src/Pages/Docs/Top.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Pages.Docs.Top exposing (Model, Msg, page) - -import Element exposing (..) -import Generated.Docs.Params as Params -import Spa.Page -import Ui -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "docs | elm-spa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - Ui.markdownArticle - { title = "docs" - , subtitle = Just "it's not done until the docs are great!" - , content = """ - - -### oh hi there! - -Each section of the docs focus on a single command, concept, or idea in elm-spa. - -If you're new to elm-spa, [the guide](/guide) has a complete video tutorial on how to build this site from scratch! - """ - } diff --git a/examples/docs/src/Pages/Guide.elm b/examples/docs/src/Pages/Guide.elm deleted file mode 100644 index 7e7eb5e..0000000 --- a/examples/docs/src/Pages/Guide.elm +++ /dev/null @@ -1,52 +0,0 @@ -module Pages.Guide exposing (Model, Msg, page) - -import Components.Hero as Hero -import Element exposing (..) -import Generated.Params as Params -import Spa.Page -import Ui -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Guide Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "guide | elm-spa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - Ui.sections - [ Hero.view - { title = "guide" - , subtitle = "(videos coming soon)" - , links = [] - } - , el [ centerX, width (fill |> maximum 512) ] <| - Ui.markdown """ -### what can i build with elm-spa? - -__This entire site!__ And in this guide we'll build it together, from scratch. -(Step-by-step, with short videos) - - - """ - , link ([ centerX ] ++ Ui.styles.button) - { label = text "let's gooo" - , url = "/guide/getting-started" - } - ] diff --git a/examples/docs/src/Pages/Guide/Dynamic.elm b/examples/docs/src/Pages/Guide/Dynamic.elm deleted file mode 100644 index ba8dfb8..0000000 --- a/examples/docs/src/Pages/Guide/Dynamic.elm +++ /dev/null @@ -1,104 +0,0 @@ -module Pages.Guide.Dynamic exposing (Model, Msg, page) - -import Components.Sidebar as Sidebar -import Element exposing (..) -import Generated.Guide.Params as Params -import Generated.Routes exposing (Route) -import Http -import Spa.Page -import Ui -import Utils.Markdown as Markdown exposing (Markdown) -import Utils.Spa exposing (Page, PageContext) -import Utils.WebData as WebData exposing (WebData(..)) - - -page : Page Params.Dynamic Model Msg model msg appMsg -page = - Spa.Page.element - { title = \{ model } -> String.join " | " [ model.slug, "guide", "elm-spa" ] - , init = init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - - --- INIT - - -type alias Model = - { slug : String - , markdown : WebData (Markdown Markdown.Frontmatter) - , route : Route - } - - -init : PageContext -> Params.Dynamic -> ( Model, Cmd Msg ) -init { route } { param1 } = - ( { slug = param1 - , markdown = Loading - , route = route - } - , Http.get - { url = "/" ++ String.join "/" [ "content", "guide", param1 ++ ".md" ] - , expect = WebData.expectMarkdown Loaded - } - ) - - - --- UPDATE - - -type Msg - = Loaded (WebData (Markdown Markdown.Frontmatter)) - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Loaded data -> - ( { model | markdown = data } - , Cmd.none - ) - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - Sub.none - - - --- VIEW - - -view : Model -> Element Msg -view model = - column - [ width fill - , spacing 32 - , alpha - (if model.markdown == WebData.Loading then - 0 - - else - 1 - ) - , Ui.transition { props = [ "opacity" ], duration = 300 } - ] - [ Ui.webDataMarkdownArticle model.markdown - , case model.markdown of - WebData.Success _ -> - Sidebar.viewNextGuideArticle model.route - - WebData.Failure _ -> - Sidebar.viewNextGuideArticle model.route - - _ -> - text "" - ] diff --git a/examples/docs/src/Pages/NotFound.elm b/examples/docs/src/Pages/NotFound.elm deleted file mode 100644 index 7a961c4..0000000 --- a/examples/docs/src/Pages/NotFound.elm +++ /dev/null @@ -1,43 +0,0 @@ -module Pages.NotFound exposing (Model, Msg, page) - -import Components.Hero as Hero -import Element exposing (..) -import Element.Font as Font -import Generated.Params as Params -import Generated.Routes as Routes exposing (routes) -import Spa.Page -import Ui -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.NotFound Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "not found | elm-spa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - Hero.view - { title = "that's a 404" - , subtitle = "it's not you, it's me." - , links = - [ { label = "but this link works!" - , url = "/" - } - ] - } diff --git a/examples/docs/src/Pages/README.md b/examples/docs/src/Pages/README.md deleted file mode 100644 index fb9be14..0000000 --- a/examples/docs/src/Pages/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# src/Pages -> where all your pages go - diff --git a/examples/docs/src/Pages/Top.elm b/examples/docs/src/Pages/Top.elm deleted file mode 100644 index 1c95a0c..0000000 --- a/examples/docs/src/Pages/Top.elm +++ /dev/null @@ -1,55 +0,0 @@ -module Pages.Top exposing (Model, Msg, page) - -import Components.Hero as Hero -import Element exposing (..) -import Generated.Params as Params -import Spa.Page -import Ui -import Utils.Spa exposing (Page) - - -type alias Model = - () - - -type alias Msg = - Never - - -page : Page Params.Top Model Msg model msg appMsg -page = - Spa.Page.static - { title = always "elm-spa" - , view = always view - } - - - --- VIEW - - -view : Element Msg -view = - Ui.sections - [ Hero.view - { title = "elm-spa" - , subtitle = "single page apps made easy" - , links = [ { label = "learn more", url = "/docs" } ] - } - , el - [ width (fill |> maximum 512) - , centerX - ] - <| - Ui.markdown """ -### does elm _need_ a framework? - -__nope, not really–__ it's kinda got one built in! so building something like React, VueJS, or Angular wouldn't really make sense. - -#### ...but even frameworks need frameworks! - -that's why projects like VueJS also have awesome tools like NuxtJS that bring together the best tools in the ecosystem (and a set of shared best practices!) - -welcome to __elm-spa__, a framework for Elm! - """ - ] diff --git a/examples/docs/src/Ports.elm b/examples/docs/src/Ports.elm deleted file mode 100644 index 70c1c98..0000000 --- a/examples/docs/src/Ports.elm +++ /dev/null @@ -1,14 +0,0 @@ -port module Ports exposing (scrollToTop) - -import Json.Encode as Json - - -port outgoing : { action : String, data : Json.Value } -> Cmd msg - - -scrollToTop : Cmd msg -scrollToTop = - outgoing - { action = "SCROLL_TO_TOP" - , data = Json.null - } diff --git a/examples/docs/src/Transitions.elm b/examples/docs/src/Transitions.elm deleted file mode 100644 index 39c35ae..0000000 --- a/examples/docs/src/Transitions.elm +++ /dev/null @@ -1,17 +0,0 @@ -module Transitions exposing (transitions) - -import Generated.Docs.Pages as Docs -import Spa.Transition as Transition -import Utils.Spa as Spa - - -transitions : Spa.Transitions msg -transitions = - { layout = Transition.fadeElmUi 500 - , page = Transition.fadeElmUi 300 - , pages = - [ { path = Docs.path - , transition = Transition.fadeElmUi 300 - } - ] - } diff --git a/examples/docs/src/Ui.elm b/examples/docs/src/Ui.elm deleted file mode 100644 index b20424f..0000000 --- a/examples/docs/src/Ui.elm +++ /dev/null @@ -1,281 +0,0 @@ -module Ui exposing - ( colors - , markdown - , markdownArticle - , sections - , styles - , transition - , viewNextArticle - , viewSidebar - , webDataMarkdownArticle - ) - -import Element exposing (..) -import Element.Background as Background -import Element.Border as Border -import Element.Font as Font -import Html.Attributes as Attr -import Markdown -import Utils.Markdown as Markdown exposing (Markdown) -import Utils.WebData as WebData exposing (WebData(..)) - - -colors : { coral : Color, white : Color } -colors = - { coral = rgb255 200 75 85 - , white = rgb255 255 255 255 - } - - -styles : - { button : List (Attribute msg) - , link : - { enabled : List (Attribute msg) - , disabled : List (Attribute msg) - } - } -styles = - { link = - { enabled = - [ Font.underline - , transition - { duration = 200 - , props = [ "opacity" ] - } - , mouseOver [ alpha 0.5 ] - ] - , disabled = - [ alpha 0.5 - ] - } - , button = - [ centerX - , Font.size 14 - , Font.semiBold - , Border.solid - , Border.width 2 - , Border.rounded 4 - , paddingXY 24 8 - , Font.color colors.coral - , Border.color colors.coral - , Background.color colors.white - , pointer - , transition - { duration = 200 - , props = [ "background", "color" ] - } - , mouseOver - [ Background.color colors.coral - , Font.color colors.white - ] - ] - } - - -sections : List (Element msg) -> Element msg -sections = - column [ spacing 32, width fill ] - - -markdown : String -> Element msg -markdown = - let - defaults = - Markdown.defaultOptions - in - Markdown.toHtmlWith - { defaults - | sanitize = False - , githubFlavored = Just { tables = True, breaks = False } - } - [ Attr.class "markdown" ] - >> Element.html - >> List.singleton - >> paragraph [] - - -markdownArticle : - { title : String - , subtitle : Maybe String - , content : String - } - -> Element msg -markdownArticle options = - column [ width fill, height fill ] - [ column [ paddingXY 0 64, spacing 8 ] <| - List.concat - [ [ el [ Font.size 48, Font.semiBold ] (text options.title) ] - , options.subtitle - |> Maybe.map (text >> List.singleton >> paragraph [ alpha 0.5, Font.size 20 ]) - |> Maybe.map List.singleton - |> Maybe.withDefault [] - ] - , markdown options.content - ] - - -webDataMarkdownArticle : - WebData (Markdown { title : String, description : Maybe String }) - -> Element msg -webDataMarkdownArticle markdown_ = - case markdown_ of - Loading -> - text "" - - Success { frontmatter, content } -> - markdownArticle - { title = frontmatter.title - , subtitle = frontmatter.description - , content = content - } - - Failure _ -> - markdownArticle - { title = "huh." - , subtitle = Just "i couldn't find that article." - , content = "" - } - - -transition : { props : List String, duration : Int } -> Attribute msg -transition options = - options.props - |> List.map - (\prop -> - String.join " " - [ prop - , String.fromInt options.duration ++ "ms" - , "ease-in-out" - ] - ) - |> String.join ", " - |> Attr.style "transition" - |> Element.htmlAttribute - - - --- DOCS SIDEBAR - - -type alias SidebarOptions route = - { current : route - , links : List (SideItem route) - , toPath : route -> String - } - - -type SideItem route - = Heading String - | Link ( String, route ) - - -type Match route - = NoMatchFound - | FoundMatch - | HereItIs ( String, route ) - - -nextArticle : route -> List (SideItem route) -> Maybe ( String, route ) -nextArticle activeRoute items = - items - |> List.filterMap - (\item -> - case item of - Heading _ -> - Nothing - - Link tuple -> - Just tuple - ) - |> List.foldl - (\item match -> - case match of - NoMatchFound -> - if Tuple.second item == activeRoute then - FoundMatch - - else - NoMatchFound - - FoundMatch -> - HereItIs item - - HereItIs i -> - HereItIs i - ) - NoMatchFound - |> (\match -> - case match of - NoMatchFound -> - Nothing - - FoundMatch -> - Nothing - - HereItIs item -> - Just item - ) - - -viewNextArticle : - { current : route - , links : List (SideItem route) - , toPath : route -> String - } - -> Element msg -viewNextArticle options = - (\link -> paragraph [ Font.size 20 ] [ el [ Font.semiBold ] <| text "next up: ", link ]) <| - case nextArticle options.current options.links of - Just ( label, r ) -> - link (Font.color colors.coral :: styles.link.enabled) - { label = text label - , url = options.toPath r - } - - Nothing -> - link (Font.color colors.coral :: styles.link.enabled) - { label = text "the guide" - , url = "/guide" - } - - -viewSidebar : - SidebarOptions route - -> Element msg -viewSidebar options = - column - [ alignTop - , spacing 16 - , width (px 180) - , paddingEach { top = 84, left = 0, right = 0, bottom = 0 } - ] - [ el [ Font.size 24, Font.semiBold ] (text "docs") - , column [ spacing 8 ] (List.map (viewSideLink options) options.links) - ] - - -viewSideLink : SidebarOptions route -> SideItem route -> Element msg -viewSideLink options item = - case item of - Heading label -> - el - [ alpha 0.5 - , Font.size 18 - , Font.semiBold - , paddingEach { top = 8, left = 0, right = 0, bottom = 0 } - ] - (text label) - - Link ( label, route ) -> - let - linkStyles = - if route == options.current then - styles.link.disabled - - else - styles.link.enabled - in - link (Font.color colors.coral :: linkStyles) - { label = text label - , url = options.toPath route - } diff --git a/examples/docs/src/Utils/Markdown.elm b/examples/docs/src/Utils/Markdown.elm deleted file mode 100644 index 9537e3f..0000000 --- a/examples/docs/src/Utils/Markdown.elm +++ /dev/null @@ -1,78 +0,0 @@ -module Utils.Markdown exposing - ( Frontmatter - , Markdown - , frontmatterDecoder - , parse - , parser - ) - -import Json.Decode as Json exposing (Decoder) -import Json.Encode -import Parser exposing ((|.), (|=), Parser) - - -type alias Markdown frontmatter = - { frontmatter : frontmatter - , content : String - } - - -parse : Json.Decoder a -> String -> Result Json.Error (Markdown a) -parse decoder value = - value - |> Parser.run parser - |> Result.mapError (\_ -> Json.Failure "Could not parse markdown." Json.Encode.null) - |> Result.andThen - (\raw -> - raw.frontmatter - |> Json.decodeString decoder - |> Result.map - (\frontmatter -> - { frontmatter = frontmatter - , content = raw.content - } - ) - ) - - -type alias RawMarkdown = - { frontmatter : String - , content : String - } - - -parser : Parser RawMarkdown -parser = - let - frontmatterParser : Parser String - frontmatterParser = - Parser.getChompedString <| - Parser.chompUntil "---" - - contentParser : Parser String - contentParser = - Parser.getChompedString <| - Parser.chompWhile (always True) - in - Parser.succeed RawMarkdown - |. Parser.symbol "---" - |= frontmatterParser - |. Parser.symbol "---" - |= contentParser - - - --- FRONTMATTER - - -type alias Frontmatter = - { title : String - , description : Maybe String - } - - -frontmatterDecoder : Decoder Frontmatter -frontmatterDecoder = - Json.map2 Frontmatter - (Json.field "title" Json.string) - (Json.maybe (Json.field "description" Json.string)) diff --git a/examples/docs/src/Utils/README.md b/examples/docs/src/Utils/README.md deleted file mode 100644 index 05c12de..0000000 --- a/examples/docs/src/Utils/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src/Utils -> a place for helper functions diff --git a/examples/docs/src/Utils/Spa.elm b/examples/docs/src/Utils/Spa.elm deleted file mode 100644 index ef4747d..0000000 --- a/examples/docs/src/Utils/Spa.elm +++ /dev/null @@ -1,72 +0,0 @@ -module Utils.Spa exposing - ( Bundle - , Init - , LayoutContext - , Page - , PageContext - , Recipe - , Transitions - , Update - , layout - , recipe - ) - -import Element exposing (Element) -import Generated.Routes as Routes exposing (Route) -import Global -import Spa.Page -import Spa.Types - - -type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias Init model msg = - Spa.Types.Init Route model msg Global.Model Global.Msg - - -type alias Update model msg = - Spa.Types.Update Route model msg Global.Model Global.Msg - - -type alias Bundle msg appMsg = - Spa.Types.Bundle Route msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -type alias LayoutContext msg = - Spa.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg - - -type alias PageContext = - Spa.Types.PageContext Route Global.Model - - -type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -layout : - Layout params model msg appMsg - -> Page params model msg layoutModel layoutMsg appMsg -layout = - Spa.Page.layout Element.map - - -type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -recipe : - Upgrade params model msg layoutModel layoutMsg appMsg - -> Recipe params model msg layoutModel layoutMsg appMsg -recipe = - Spa.Page.recipe Element.map - - -type alias Transitions msg = - Spa.Types.Transitions (Element msg) diff --git a/examples/docs/src/Utils/WebData.elm b/examples/docs/src/Utils/WebData.elm deleted file mode 100644 index ba5945c..0000000 --- a/examples/docs/src/Utils/WebData.elm +++ /dev/null @@ -1,35 +0,0 @@ -module Utils.WebData exposing - ( WebData(..) - , expectMarkdown - , fromResult - ) - -import Http -import Utils.Markdown as Markdown exposing (Markdown) - - -type WebData a - = Loading - | Success a - | Failure Http.Error - - -fromResult : Result Http.Error a -> WebData a -fromResult result = - case result of - Ok value -> - Success value - - Err reason -> - Failure reason - - -expectMarkdown : - (WebData (Markdown Markdown.Frontmatter) -> msg) - -> Http.Expect msg -expectMarkdown msg = - Http.expectString - (Result.andThen (Markdown.parse Markdown.frontmatterDecoder >> Result.mapError (\_ -> Http.BadBody "")) - >> fromResult - >> msg - ) diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index cab849f..0000000 --- a/netlify.toml +++ /dev/null @@ -1,6 +0,0 @@ -# sends all routes to /index.html -# (so you can handle 404s there!) -[[redirects]] - from = "/*" - to = "/index.html" - status = 200 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 22b167e..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2896 +0,0 @@ -{ - "name": "elm-spa", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "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" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "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 - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "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 - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "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" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", - "dev": true, - "requires": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", - "dev": true - } - } - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "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" - } - }, - "commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.2.tgz", - "integrity": "sha512-bUTfqFWtNKWp73oNIfRkqwYZJeNT3lstzZcAkhhiuvDraRSgOH1/+F9ZklbpR4zpdKuo4cpXN8tKP7s61yjX+g==", - "dev": true - }, - "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 - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "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" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "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 - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "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" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "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" - } - }, - "elm-doc-preview": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/elm-doc-preview/-/elm-doc-preview-3.0.4.tgz", - "integrity": "sha512-tLobB4Kv4X/T+mkL4Tc5G1fqnBzvCqV7Pqx/f2sJIQtSTsJcktwI01U8poiBPoo8VwE7ZRuBmApSkl6XTzrymA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "commander": "^3.0.2", - "core-js": "^3.3.3", - "cross-spawn": "^6.0.5", - "express": "^4.17.1", - "express-ws": "^4.0.0", - "glob": "^7.1.5", - "latest-version": "^4.0.0", - "opn": "^5.5.0", - "sane": "^4.1.0", - "serve-index": "^1.9.1", - "serve-static": "^1.14.1", - "tmp": "0.1.0", - "util.promisify": "^1.0.0", - "ws": "^6.2.1" - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "express-ws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-4.0.0.tgz", - "integrity": "sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw==", - "dev": true, - "requires": { - "ws": "^5.2.0" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.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 - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "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 - }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "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" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "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" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "got": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" - } - }, - "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" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "dev": true, - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.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" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "into-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", - "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", - "dev": true, - "requires": { - "from2": "^2.1.1", - "p-is-promise": "^1.1.0" - } - }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "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 - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "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" - } - }, - "keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "latest-version": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-4.0.0.tgz", - "integrity": "sha512-b4Myk7aQiQJvgssw2O8yITjELdqKRX4JQJUF1IUplgLaA8unv7s+UsAOwH6Q0/a09czSvlxEm306it2LBXrCzg==", - "dev": true, - "requires": { - "package-json": "^5.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "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" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.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 - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", - "dev": true - }, - "p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "package-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-5.0.0.tgz", - "integrity": "sha512-EeHQFFTlEmLrkIQoxbE9w0FuAWHoc1XpthDqnZ/i9keOt701cteyXwAxQFLpVqVjj3feh2TodkihjLaRUtIgLg==", - "dev": true, - "requires": { - "got": "^8.3.1", - "registry-auth-token": "^3.3.2", - "registry-url": "^3.1.0", - "semver": "^5.5.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "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 - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - } - }, - "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "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.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "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" - }, - "dependencies": { - "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 - } - } - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "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" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "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 - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "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" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "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 - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "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" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 4534abf..0000000 --- a/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "elm-spa", - "version": "1.0.0", - "description": "a package for building single-page applications!", - "main": "cli/src/index.js", - "bin": "./cli/src/index.js", - "scripts": { - "start": "npm install && npm run dev", - "build": "npm link && (cd cli && npm run build)", - "dev": "npm run build && npm run example", - "docs": "elm-doc-preview", - "build:example": "npm run build && (cd examples/docs && npm install && npm run build)", - "example": "npm run examples:complex", - "examples:docs": "(cd examples/docs && npm install && npm run dev)", - "examples:complex": "(cd examples/complex && npm install && npm run dev)" - }, - "dependencies": {}, - "devDependencies": { - "elm": "0.19.1-3", - "elm-doc-preview": "3.0.4" - }, - "keywords": [] -} diff --git a/src/Internals/Page.elm b/src/Internals/Page.elm deleted file mode 100644 index fc8f65c..0000000 --- a/src/Internals/Page.elm +++ /dev/null @@ -1,110 +0,0 @@ -module Internals.Page exposing - ( Bundle - , Init - , Layout - , LayoutContext - , Page(..) - , PageContext - , Recipe - , Update - , Upgrade - , upgrade - ) - -import Dict exposing (Dict) -import Internals.Path exposing (Path) -import Internals.Transition as Transition exposing (Transition) - - -type alias PageContext route globalModel = - { global : globalModel - , route : route - , queryParameters : Dict String String - } - - -type Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - = Page (Page_ route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg) - - -type alias Page_ route 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 - } - -> Recipe route pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - - -type alias Recipe route pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - { init : pageParams -> Init route layoutModel layoutMsg globalModel globalMsg - , update : pageMsg -> pageModel -> Update route layoutModel layoutMsg globalModel globalMsg - , bundle : pageModel -> Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - } - - -type alias Upgrade route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - { page : Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - , toModel : pageModel -> layoutModel - , toMsg : pageMsg -> layoutMsg - } - - -upgrade : - ((pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg) - -> Upgrade route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - -> Recipe route pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -upgrade map config = - let - (Page page) = - config.page - in - page - { toModel = config.toModel - , toMsg = config.toMsg - , map = map - } - - -type alias Init route layoutModel layoutMsg globalModel globalMsg = - PageContext route globalModel - -> ( layoutModel, Cmd layoutMsg, Cmd globalMsg ) - - -type alias Update route layoutModel layoutMsg globalModel globalMsg = - PageContext route globalModel - -> ( layoutModel, Cmd layoutMsg, Cmd globalMsg ) - - -type alias Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - { fromGlobalMsg : globalMsg -> msg - , fromPageMsg : layoutMsg -> msg - , map : (layoutMsg -> msg) -> ui_layoutMsg -> ui_msg - , visibility : Transition.Visibility - , path : Path - , transitions : - List - { path : Path - , transition : Transition ui_msg - } - } - -> PageContext route globalModel - -> - { title : String - , view : ui_msg - , subscriptions : Sub msg - } - - -type alias LayoutContext route msg ui_msg globalModel globalMsg = - { page : ui_msg - , route : route - , global : globalModel - , fromGlobalMsg : globalMsg -> msg - } - - -type alias Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg = - { path : Path - , view : LayoutContext route msg ui_msg globalModel globalMsg -> ui_msg - , recipe : Recipe route pageParams pageModel pageMsg pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg - } diff --git a/src/Internals/Path.elm b/src/Internals/Path.elm deleted file mode 100644 index c368553..0000000 --- a/src/Internals/Path.elm +++ /dev/null @@ -1,48 +0,0 @@ -module Internals.Path exposing - ( Path - , Piece - , dynamic - , static - , within - ) - - -type alias Path = - List Piece - - -type Piece - = Static String - | Dynamic - - -static : String -> Piece -static = - Static - - -dynamic : Piece -dynamic = - Dynamic - - -within : List String -> List Piece -> Bool -within strings pieces = - List.length pieces - <= List.length strings - && (List.map2 - matches - strings - pieces - |> List.all ((==) True) - ) - - -matches : String -> Piece -> Bool -matches str piece = - case piece of - Static value -> - str == value - - Dynamic -> - True diff --git a/src/Internals/Transition.elm b/src/Internals/Transition.elm deleted file mode 100644 index 78b8175..0000000 --- a/src/Internals/Transition.elm +++ /dev/null @@ -1,165 +0,0 @@ -module Internals.Transition exposing - ( Transition - , duration, view - , optOut, none, fadeHtml, fadeElmUi - , Visibility - , visible, invisible - , custom - ) - -{-| - -@docs Transition -@docs duration, view, chooseFrom -@docs optOut, none, fadeHtml, fadeElmUi - -@docs Visibility -@docs visible, invisible - --} - -import Element exposing (Element) -import Html exposing (Html) -import Html.Attributes as Attr -import Url exposing (Url) - - -type Visibility - = Invisible - | Visible - - -visible : Visibility -visible = - Visible - - -invisible : Visibility -invisible = - Invisible - - -type Transition ui_msg - = OptOut - | None - | Transition (Options ui_msg) - - -type alias Options ui_msg = - { duration : Int - , invisible : View ui_msg - , visible : View ui_msg - } - - -type alias View ui_msg = - ui_msg - -> ui_msg - - -duration : Transition ui_msg -> Int -duration transition = - case transition of - OptOut -> - 0 - - None -> - 0 - - Transition t -> - t.duration - - -view : - Transition ui_msg - -> Visibility - -> ui_msg - -> ui_msg -view transition visibility page = - case transition of - OptOut -> - page - - None -> - page - - Transition t -> - case visibility of - Visible -> - t.visible page - - Invisible -> - t.invisible page - - - --- TRANSITIONS - - -optOut : Transition ui_msg -optOut = - OptOut - - -none : Transition ui_msg -none = - None - - -fadeHtml : Int -> Transition (Html msg) -fadeHtml duration_ = - let - withOpacity : Int -> View (Html msg) - withOpacity opacity page = - Html.div - [ Attr.style "opacity" (String.fromInt opacity) - , Attr.style "transition" <| - String.concat - [ "opacity " - , String.fromInt duration_ - , "ms ease-in-out" - ] - ] - [ page ] - in - Transition <| - { duration = duration_ - , invisible = withOpacity 0 - , visible = withOpacity 1 - } - - -fadeElmUi : Int -> Transition (Element msg) -fadeElmUi duration_ = - let - withOpacity : Float -> View (Element msg) - withOpacity opacity page = - Element.el - [ Element.width Element.fill - , Element.height Element.fill - , Element.alpha opacity - , Element.htmlAttribute <| - Attr.style "transition" <| - String.concat - [ "opacity " - , String.fromInt duration_ - , "ms ease-in-out" - ] - ] - page - in - Transition <| - { duration = duration_ - , invisible = withOpacity 0 - , visible = withOpacity 1 - } - - -custom : - { duration : Int - , invisible : View ui_msg - , visible : View ui_msg - } - -> Transition ui_msg -custom = - Transition diff --git a/src/Internals/Utils.elm b/src/Internals/Utils.elm deleted file mode 100644 index 9c53b47..0000000 --- a/src/Internals/Utils.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Internals.Utils exposing - ( delay - , send - ) - -import Process -import Task - - -delay : Int -> msg -> Cmd msg -delay ms msg = - Process.sleep (toFloat ms) - |> Task.perform (\_ -> msg) - - -send : msg -> Cmd msg -send = - Task.succeed >> Task.perform identity diff --git a/src/README.md b/src/README.md deleted file mode 100644 index e9f7507..0000000 --- a/src/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# elm-spa -a way to build single page apps with Elm! - - -## getting started - -``` -npx elm-spa init your-project -``` - -_"Wait– that command didn't work!"_ (you might need the latest [NodeJS](https://nodejs.org/)) - - -## just installing the elm package? - -``` -elm install ryannhg/elm-spa -``` - - -## want to learn more? - -you should check out [elm-spa.dev](https://elm-spa.dev)! diff --git a/src/Spa.elm b/src/Spa.elm index 58a6f50..a281e79 100644 --- a/src/Spa.elm +++ b/src/Spa.elm @@ -1,629 +1,395 @@ module Spa exposing - ( create, Program - , usingElmUi - , usingHtml + ( static + , sandbox + , element + , component + , Page + , upgrade + , Bundle ) +{-| When you create an app with the [elm/browser](/packages/elm/browser/latest) package, +you can build anything from a static `Html msg` page to a fully-fledged web `Browser.application`. + +`elm-spa` uses the existing design at the page-level, so you can quickly add new pages to your Elm application! + + +## the four kinds of pages: + +1. [static](#static-pages) – a page that only renders HTML. +2. [sandbox](#sandbox-pages) – a page with state. +3. [element](#element-pages) – a page with side-effects. +4. [component](#component-pages) – a page with global state. + + +# static pages + +Just like [Elm's intro "Hello!" example](https://elm-lang.org/examples/hello), +sometimes you just want a page that renders some HTML, without having any state. + +@docs static + +**Note:** Static pages don't store data, so Model is always an empty tuple: () + + +# sandbox pages + +When you're ready to keep track of state, like in +[Elm's "Counter" example](https://elm-lang.org/examples/buttons), +you can use a sandbox page. + +Similar to `Browser.sandbox`, this allows you to init your model, and update it with messages! + +@docs sandbox + + +# element pages + +If you're ready to [send HTTP requests](https://elm-lang.org/examples/cat-gifs) or [listen to events](https://elm-lang.org/examples/time) for updates, it's time to upgrade to an element page! + +Additionally, an element will give you access to Flags, so you can access things like URL parameters. + +@docs element + +**New to Cmd or Sub?** I recommend checking out Elm's official guide , that's a great place to wrap your head around these two concepts. + + +# component pages + +If you need to access shared state across all pages, or need to send a global commands like signing in/signing out a user, a component page is what you'll need. + +Component pages gain access to the `Global.Model`, and return an additional `Cmd Global.Msg` so you can read/update the global state of your application. + +@docs component + +**Cool trick:** Don't need access to Global.Model in all of your functions? Use always to skip the first argument. + +In the following example, only the view function receives Global.Model: + + import Global + import Spa exposing (Page) + + page : Page Flags Model Msg Global.Model Global.Msg + page = + Spa.component + { init = always init + , update = always update + , view = view -- not using "always"! + , subscriptions = always subscriptions + } + + +# putting pages together + +**Note:** The [elm-spa cli tool](https://www.npmjs.com/package/elm-spa) will generate all the upcoming code +for you. You can continue reading to understand what it's doing under the hood! + +@docs Page + + +## learning with an example + +Let's imagine we are building a website for a restaurant! This website has the following routes: + +1. `/home` - the homepage +2. `/menu` - the menu +3. `/faqs` - frequently asked questions + +With `elm-spa` we'd create a module for each page, and import them together in one file. + + module Pages exposing (Model, Msg, init, update, view, subscriptions) + + import Pages.Home + import Pages.Menu + import Pages.Faqs + +If we want to implement the `Pages.init` function, we'll need to return the same type of value. This means we need to make a shared model and a shared msg type to handle all the different pages in our application: + + type Model + = Home_Model Pages.Home.Model + | Menu_Model Pages.Menu.Model + | Faqs_Model Pages.Faqs.Model + + type Msg + = Home_Msg Pages.Home.Msg + | Menu_Msg Pages.Menu.Msg + | Faqs_Msg Pages.Faqs.Msg + +These two types allow `init` to return `( Model, Cmd Msg, Cmd Global.Msg )` for any page that the user is looking at! + + +## upgrading pages + +With the custom types above: + +1. `Home_Model` can upgrade a `Pages.Home.Model` to a `Model` +2. `Home_Msg` can upgrade a `Pages.Home.Msg` to a `Msg` + +We use the upgrade function to return a record that makes writing the combined `init`/`update`/`view`/`subscriptions` functions clean and easy! + +@docs upgrade + + import Pages.Faqs + import Pages.Home + import Pages.Menu + import Spa + + type alias UpgradedPage pageFlags pageModel pageMsg = + { init : pageFlags -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + , update : pageMsg -> pageModel -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + , bundle : pageModel -> Global.Model -> Spa.Bundle Msg + } + + type alias UpgradedPages = + { home : UpgradedPage Pages.Home.Flags Pages.Home.Model Pages.Home.Msg + , menu : UpgradedPage Pages.Menu.Flags Pages.Menu.Model Pages.Menu.Msg + , faqs : UpgradedPage Pages.Faqs.Flags Pages.Faqs.Model Pages.Faqs.Msg + } + + pages : UpgradedPages + pages = + { home = Pages.Home.page |> Spa.upgrade Home_Model Home_Msg + , menu = Pages.Menu.page |> Spa.upgrade Menu_Model Menu_Msg + , faqs = Pages.Faqs.page |> Spa.upgrade Faqs_Model Faqs_Msg + } + +Now when we write `init`, we can use the upgraded `pages` variable to keep things easy to read. + + +## implementing `init` + + import Route exposing (Route) + + init : Route -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + init route = + case route of + Route.Home -> + pages.home.init () + + Route.Menu -> + pages.menu.init () + + Route.Faqs -> + pages.faqs.init () + + +## implementing `update` + + update : Msg -> Model -> Global.Model -> ( Model, Cmd Msg, Cmd Global.Msg ) + update bigMsg bigModel = + case ( bigMsg, bigModel ) of + ( Home_Msg msg, Home_Model model ) -> + pages.home.update msg model + + ( Menu_Msg msg, Menu_Model model ) -> + pages.menu.update msg model + + ( Faqs_Msg msg, Faqs_Model model ) -> + pages.faqs.update msg model + + _ -> + always ( bigModel, Cmd.none, Cmd.none ) + + +## implementing `bundle` + +With `elm-spa`, we don't need to write out a case expression for both `view` and `subscriptions`. +We can just write one that bundles them together. + +We can create `view` and `subscriptions` from this single function: + +@docs Bundle + + +# that's it! + +You can check out or join #elm-spa-users on the official Elm slack channel for any questions ❤️ + +-} + +import Browser exposing (Document) +import Html + + + +-- PAGE + + +{-| What was the point of using the functions above? They all return the `Page` type, +and we can use the `Spa.upgrade` function on any of them! + +(The following example will illustrate why that's a good thing) + +-} +type alias Page flags model msg globalModel globalMsg = + { init : globalModel -> flags -> ( model, Cmd msg, Cmd globalMsg ) + , update : globalModel -> msg -> model -> ( model, Cmd msg, Cmd globalMsg ) + , view : globalModel -> model -> Document msg + , subscriptions : globalModel -> model -> Sub msg + } + + {-| + page : Page Flags Model Msg globalModel globalMsg + page = + Spa.static + { view = view + } -## Let's build some single page applications! +-} +static : + { view : Document msg + } + -> Page flags () msg globalModel globalMsg +static options = + { init = \_ _ -> ( (), Cmd.none, Cmd.none ) + , update = \_ _ model -> ( model, Cmd.none, Cmd.none ) + , view = \_ _ -> options.view + , subscriptions = \_ _ -> Sub.none + } -`Spa.create` replaces [Browser.application](https://package.elm-lang.org/packages/elm/browser/latest/Browser#application) -as the entrypoint to your app. + +{-| + + import Spa exposing (Page) + + page : Page Flags Model Msg globalModel globalMsg + page = + Spa.sandbox + { init = init + , update = update + , view = view + } + +-} +sandbox : + { init : model + , update : msg -> model -> model + , view : model -> Document msg + } + -> Page flags model msg globalModel globalMsg +sandbox options = + { init = \_ _ -> ( options.init, Cmd.none, Cmd.none ) + , update = \_ msg model -> ( options.update msg model, Cmd.none, Cmd.none ) + , view = always options.view + , subscriptions = \_ _ -> Sub.none + } + + +{-| + + page : Page Flags Model Msg globalModel globalMsg + page = + Spa.element + { init = init + , update = update + , view = view + , subscriptions = subscriptions + } + +-} +element : + { init : flags -> ( model, Cmd msg ) + , update : msg -> model -> ( model, Cmd msg ) + , view : model -> Document msg + , subscriptions : model -> Sub msg + } + -> Page flags model msg globalModel globalMsg +element page = + { init = \_ flags -> page.init flags |> (\( model, cmd ) -> ( model, cmd, Cmd.none )) + , update = \_ msg model -> page.update msg model |> (\( model_, cmd ) -> ( model_, cmd, Cmd.none )) + , subscriptions = always page.subscriptions + , view = always page.view + } + + +{-| import Global - import Pages - import Routes exposing (routes) + import Spa exposing (Page) + + page : Page Flags Model Msg Global.Model Global.Msg + page = + Spa.component + { init = init + , update = update + , view = view + , subscriptions = subscriptions + } + +-} +component : + { init : globalModel -> flags -> ( model, Cmd msg, Cmd globalMsg ) + , update : globalModel -> msg -> model -> ( model, Cmd msg, Cmd globalMsg ) + , view : globalModel -> model -> Document msg + , subscriptions : globalModel -> model -> Sub msg + } + -> Page flags model msg globalModel globalMsg +component = + identity + + +{-| For each page we export from our `Pages.*` modules, we should call the `upgrade` function with the corresponding `Model` and `Msg` variants, like this: + + pages : UpgradedPages + pages = + { home = Pages.Home.page |> Spa.upgrade Home_Model Home_Msg + , menu = Pages.Menu.page |> Spa.upgrade Menu_Model Menu_Msg + , faqs = Pages.Faqs.page |> Spa.upgrade Faqs_Model Faqs_Msg + } + +-} +upgrade : + (pageModel -> model) + -> (pageMsg -> msg) + -> Page pageFlags pageModel pageMsg globalModel globalMsg + -> + { init : pageFlags -> globalModel -> ( model, Cmd msg, Cmd globalMsg ) + , update : pageMsg -> pageModel -> globalModel -> ( model, Cmd msg, Cmd globalMsg ) + , bundle : pageModel -> globalModel -> Bundle msg + } +upgrade toModel toMsg page = + { init = + \flags global -> + page.init global flags |> (\( model, cmd, globalCmd ) -> ( toModel model, Cmd.map toMsg cmd, globalCmd )) + , update = + \msg model global -> + page.update global msg model |> (\( model_, cmd, globalCmd ) -> ( toModel model_, Cmd.map toMsg cmd, globalCmd )) + , bundle = + \model global -> + { view = page.view global model |> (\doc -> { title = doc.title, body = List.map (Html.map toMsg) doc.body }) + , subscriptions = page.subscriptions global model |> Sub.map toMsg + } + } + + +{-| + import Spa - import Transitions - import Utils.Spa - main : Utils.Spa.Program Pages.Model Pages.Msg - main = - Spa.create - { global = - { init = Global.init - , update = Global.update - , subscriptions = Global.subscriptions - } - , page = Pages.page - , routing = - { routes = Routes.parsers - , toPath = Routes.toPath - , notFound = routes.notFound - , afterNavigate = Nothing - } - , transitions = Transitions.transitions - , ui = Spa.usingElmUi - } + bundle : Model -> Global.Model -> Spa.Bundle Msg + bundle bigModel = + case bigModel of + Home_Model model -> + pages.home.bundle model -@docs create, Program + Menu_Model model -> + pages.menu.bundle model + Faqs_Model model -> + pages.faqs.bundle model -# using elm-ui? + view : Model -> Global.Model -> Document Msg + view model = + bundle model >> .view -If you're a big fan of [mdgriffith/elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) (or a "not-so-big-fan of CSS"), -this package supports using `Element msg` instead of `Html msg` for your pages and components. - -@docs usingElmUi - - -## using html? - -@docs usingHtml + subscriptions : Model -> Global.Model -> Sub Msg + subscriptions model = + bundle model >> .subscriptions -} - -import Browser -import Browser.Navigation as Nav -import Dict exposing (Dict) -import Element exposing (Element) -import Html exposing (Html) -import Internals.Page as Page -import Internals.Path as Path exposing (Path) -import Internals.Transition as Transition exposing (Transition) -import Internals.Utils as Utils -import Url exposing (Url) -import Url.Parser as Parser exposing (Parser) - - - --- APPLICATION - - -{-| An alias for `Platform.Program` to make annotations a little more clear. --} -type alias Program flags globalModel globalMsg layoutModel layoutMsg = - Platform.Program flags (Model flags globalModel layoutModel) (Msg globalMsg layoutMsg) - - -{-| If you're just using `elm/html`, you can pass this into `Spa.create` - - main = - Spa.create - { ui = Spa.usingHtml - , -- ... - } - --} -usingHtml : - { map : - (layoutMsg -> Msg globalMsg layoutMsg) - -> Html layoutMsg - -> Html (Msg globalMsg layoutMsg) - , toHtml : ui_msg -> ui_msg +type alias Bundle msg = + { view : Document msg + , subscriptions : Sub msg } -usingHtml = - { toHtml = identity - , map = Html.map - } - - -{-| If you're just using `mdgriffith/elm-ui`, you can pass this into `Spa.create` - - main = - Spa.create - { ui = Spa.usingElmUi - , -- ... - } - --} -usingElmUi : - { map : - (layoutMsg -> Msg globalMsg layoutMsg) - -> Element layoutMsg - -> Element (Msg globalMsg layoutMsg) - , toHtml : Element msg -> Html msg - } -usingElmUi = - { toHtml = Element.layout [] - , map = Element.map - } - - -{-| Creates a new `Program` given some one-time configuration: - - - `ui` - How do we convert our views into `Html msg`? - - `routing` - What are the app's routes? - - `transitions` - How should we transition between routes? - - `global` - How do we share state between pages? - - `page` - What page should we render? - --} -create : - { global : - { init : - { navigate : route -> Cmd (Msg globalMsg layoutMsg) - } - -> flags - -> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) ) - , update : - { navigate : route -> Cmd (Msg globalMsg layoutMsg) - } - -> globalMsg - -> globalModel - -> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) ) - , subscriptions : globalModel -> Sub globalMsg - } - , page : Page.Page route route layoutModel layoutMsg ui_layoutMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg - , routing : - { routes : List (Parser (route -> route) route) - , toPath : route -> String - , notFound : route - , afterNavigate : Maybe ({ old : route, new : route } -> globalMsg) - } - , transitions : - { layout : Transition ui_msg - , page : Transition ui_msg - , pages : - List - { path : Path - , transition : Transition ui_msg - } - } - , ui : - { toHtml : ui_msg -> Html (Msg globalMsg layoutMsg) - , map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg - } - } - -> Program flags globalModel globalMsg layoutModel layoutMsg -create config = - let - page = - Page.upgrade (always identity) - { toModel = identity - , toMsg = identity - , page = config.page - } - in - Browser.application - { init = - init - { init = - { global = config.global.init - , pages = page.init - } - , routing = - { fromUrl = fromUrl config.routing - , toPath = config.routing.toPath - , transition = config.transitions.layout - } - } - , update = - update - { routing = - { fromUrl = fromUrl config.routing - , toPath = config.routing.toPath - , routes = config.routing.routes - , afterNavigate = config.routing.afterNavigate - , transitions = pageTransitions config.transitions - } - , init = page.init - , update = - { global = config.global.update - , pages = page.update - } - } - , subscriptions = - subscriptions - { bundle = page.bundle - , map = config.ui.map - , global = config.global.subscriptions - , transition = config.transitions.layout - , fromUrl = fromUrl config.routing - } - , view = - view - { toHtml = config.ui.toHtml - , bundle = page.bundle - , map = config.ui.map - , transitions = config.transitions - , fromUrl = fromUrl config.routing - } - , onUrlChange = ChangedUrl - , onUrlRequest = ClickedLink - } - - - --- ROUTING - - -type alias Routes route a = - List (Parser (route -> a) a) - - -fromUrl : { a | routes : Routes route route, notFound : route } -> Url -> route -fromUrl config = - Parser.parse (Parser.oneOf config.routes) - >> Maybe.withDefault config.notFound - - - --- INIT - - -type alias Model flags globalModel model = - { url : Url - , flags : flags - , key : Nav.Key - , global : globalModel - , page : model - , path : Path - , visibilities : - { layout : Transition.Visibility - , page : Transition.Visibility - } - } - - -init : - { routing : - { fromUrl : Url -> route - , toPath : route -> String - , transition : Transition ui_msg - } - , init : - { global : - { navigate : route -> Cmd (Msg globalMsg layoutMsg) } - -> flags - -> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) ) - , pages : route -> Page.Init route layoutModel layoutMsg globalModel globalMsg - } - } - -> flags - -> Url - -> Nav.Key - -> ( Model flags globalModel layoutModel, Cmd (Msg globalMsg layoutMsg) ) -init config flags url key = - url - |> config.routing.fromUrl - |> (\route -> - let - ( globalModel, globalCmd, cmd ) = - config.init.global - { navigate = navigate config.routing.toPath url - } - flags - - ( pageModel, pageCmd, pageGlobalCmd ) = - config.init.pages route - { global = globalModel - , queryParameters = queryParameters url - , route = route - } - in - ( { flags = flags - , url = url - , key = key - , global = globalModel - , page = pageModel - , path = [] - , visibilities = - { layout = Transition.invisible - , page = Transition.visible - } - } - , Cmd.batch - [ Cmd.map Page pageCmd - , Cmd.map Global pageGlobalCmd - , Cmd.map Global globalCmd - , Utils.delay (Transition.duration config.routing.transition) FadeInLayout - , cmd - ] - ) - ) - - - --- UPDATE - - -type Msg globalMsg msg - = ChangedUrl Url - | ClickedLink Browser.UrlRequest - | Global globalMsg - | Page msg - | FadeInLayout - | FadeInPage Url - - -update : - { routing : - { fromUrl : Url -> route - , toPath : route -> String - , routes : Routes route a - , afterNavigate : Maybe ({ old : route, new : route } -> globalMsg) - , transitions : - List - { path : Path - , transition : Transition ui_msg - } - } - , init : route -> Page.Init route layoutModel layoutMsg globalModel globalMsg - , update : - { global : - { navigate : route -> Cmd (Msg globalMsg layoutMsg) } - -> globalMsg - -> globalModel - -> ( globalModel, Cmd globalMsg, Cmd (Msg globalMsg layoutMsg) ) - , pages : - layoutMsg - -> layoutModel - -> Page.Update route layoutModel layoutMsg globalModel globalMsg - } - } - -> Msg globalMsg layoutMsg - -> Model flags globalModel layoutModel - -> ( Model flags globalModel layoutModel, Cmd (Msg globalMsg layoutMsg) ) -update config msg model = - case msg of - FadeInLayout -> - ( { model - | visibilities = - { layout = Transition.visible - , page = model.visibilities.page - } - } - , Cmd.none - ) - - FadeInPage url -> - url - |> config.routing.fromUrl - |> (\route -> - config.init route - { global = model.global - , queryParameters = queryParameters model.url - , route = route - } - ) - |> (\( pageModel, pageCmd, globalCmd ) -> - ( { model - | visibilities = { layout = Transition.visible, page = Transition.visible } - , page = pageModel - } - , Cmd.batch - [ Cmd.map Page pageCmd - , Cmd.map Global globalCmd - ] - ) - ) - - ClickedLink (Browser.Internal url) -> - if url == model.url then - ( model, Cmd.none ) - - else - ( model - , Nav.pushUrl model.key (Url.toString url) - ) - - ClickedLink (Browser.External url) -> - ( model - , Nav.load url - ) - - ChangedUrl url -> - let - ( path, duration ) = - chooseFrom - { transitions = config.routing.transitions - , from = model.url - , to = url - } - |> Just - |> Maybe.withDefault (List.head config.routing.transitions) - |> Maybe.map (\item -> ( item.path, Transition.duration item.transition )) - |> Maybe.withDefault ( [], 0 ) - in - ( { model - | url = url - , visibilities = - { layout = Transition.visible - , page = Transition.invisible - } - , path = path - } - , Cmd.batch - [ Utils.delay - duration - (FadeInPage url) - , case config.routing.afterNavigate of - Just toMsg -> - (Utils.send >> Cmd.map Global) - (toMsg - { old = config.routing.fromUrl model.url - , new = config.routing.fromUrl url - } - ) - - Nothing -> - Cmd.none - ] - ) - - Global globalMsg -> - config.update.global - { navigate = navigate config.routing.toPath model.url - } - globalMsg - model.global - |> (\( global, globalCmd, cmd ) -> - ( { model | global = global } - , Cmd.batch - [ Cmd.map Global globalCmd - , cmd - ] - ) - ) - - Page pageMsg -> - config.update.pages pageMsg - model.page - { global = model.global - , queryParameters = queryParameters model.url - , route = config.routing.fromUrl model.url - } - |> (\( page, pageCmd, globalCmd ) -> - ( { model | page = page } - , Cmd.batch - [ Cmd.map Page pageCmd - , Cmd.map Global globalCmd - ] - ) - ) - - -navigate : (route -> String) -> Url -> route -> Cmd (Msg globalMsg layoutMsg) -navigate toPath url route = - Utils.send <| - ClickedLink (Browser.Internal { url | path = toPath route }) - - - --- SUBSCRIPTIONS - - -subscriptions : - { map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg - , bundle : - layoutModel - -> Page.Bundle route layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg - , global : globalModel -> Sub globalMsg - , transition : Transition ui_msg - , fromUrl : Url -> route - } - -> Model flags globalModel layoutModel - -> Sub (Msg globalMsg layoutMsg) -subscriptions config model = - Sub.batch - [ (config.bundle - model.page - { fromGlobalMsg = Global - , fromPageMsg = Page - , map = config.map - , path = model.path - , transitions = [] - , visibility = model.visibilities.page - } - { global = model.global - , route = config.fromUrl model.url - , queryParameters = queryParameters model.url - } - ).subscriptions - , Sub.map Global (config.global model.global) - ] - - - --- VIEW - - -type alias Transitions ui_msg = - { layout : Transition ui_msg - , page : Transition ui_msg - , pages : - List - { path : Path - , transition : Transition ui_msg - } - } - - -view : - { map : (layoutMsg -> Msg globalMsg layoutMsg) -> ui_layoutMsg -> ui_msg - , toHtml : ui_msg -> Html (Msg globalMsg layoutMsg) - , bundle : - layoutModel - -> Page.Bundle route layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg - , fromUrl : Url -> route - , transitions : Transitions ui_msg - } - -> Model flags globalModel layoutModel - -> Browser.Document (Msg globalMsg layoutMsg) -view config model = - let - bundle = - config.bundle - model.page - { fromGlobalMsg = Global - , fromPageMsg = Page - , map = config.map - , path = model.path - , visibility = model.visibilities.page - , transitions = pageTransitions config.transitions - } - { global = model.global - , route = config.fromUrl model.url - , queryParameters = queryParameters model.url - } - in - { title = bundle.title - , body = - [ config.toHtml <| - Transition.view - config.transitions.layout - model.visibilities.layout - bundle.view - ] - } - - - --- Transition magic - - -chooseFrom : - { transitions : List { path : Path, transition : Transition ui_msg } - , from : Url - , to : Url - } - -> Maybe { path : Path, transition : Transition ui_msg } -chooseFrom options = - let - ( fromPath, toPath ) = - ( options.from, options.to ) - |> Tuple.mapBoth urlPath urlPath - in - options.transitions - |> List.reverse - |> List.filter - (\{ path, transition } -> - Path.within fromPath path - && Path.within toPath path - && (transition /= Transition.optOut) - ) - |> List.head - - -urlPath : Url -> List String -urlPath url = - url.path |> String.dropLeft 1 |> String.split "/" - - -pageTransitions : Transitions ui_msg -> List { path : Path, transition : Transition ui_msg } -pageTransitions transitions = - ({ path = [], transition = transitions.page } :: transitions.pages) - |> List.sortBy (.path >> List.length) - - - --- QUERY PARAMETERS - - -queryParameters : { a | query : Maybe String } -> Dict String String -queryParameters url = - let - toDict : String -> Dict String String - toDict query = - query - |> String.split "&" - |> List.map (String.split "=") - |> List.map (\pieces -> ( List.head pieces, List.drop 1 pieces |> List.head )) - |> List.map (Tuple.mapBoth (Maybe.withDefault "") (Maybe.withDefault "")) - |> List.filter (\( key, _ ) -> not (String.isEmpty key)) - |> Dict.fromList - in - Maybe.map toDict url.query |> Maybe.withDefault Dict.empty diff --git a/src/Spa/Page.elm b/src/Spa/Page.elm deleted file mode 100644 index f37c4ce..0000000 --- a/src/Spa/Page.elm +++ /dev/null @@ -1,643 +0,0 @@ -module Spa.Page exposing - ( static - , sandbox - , element - , component, send - , layout - , recipe - , keep - ) - -{-| - - -## Pick the simplest page for the job! - -1. [`static`](#static) - a page without state - -2. [`sandbox`](#sandbox) - a page without side-effects - -3. [`element`](#element) - a page _with_ side-effects - -4. [`component`](#component) - a page that can change the global state - - -### **heads up:** `always` incoming! - -You may notice the examples below use the function `always`. - - Page.static - { title = always "Hello" - , view = always view - } - -This is to **opt-out** each function from accessing data like [`PageContext`](./Spa-Types#PageContext) - -If you decide you need access to the `Route`, query parameters, or `Global.Model`: -Remove the `always` from `title`, `init`, `update`, `view`, or -`subscriptions` functions. - - -# static - -@docs static - - -# sandbox - -@docs sandbox - - -# element - -@docs element - - -# component - -@docs component, send - - -# manually composing pages? - -The rest of this module contains types and functions that -are automatically generated with the [CLI companion tool](https://github.com/ryannhg/elm-spa/tree/master/cli)! - -If you'd rather type this stuff manually, these docs are for you! - - -## layout - -@docs layout - - -## recipe - -@docs recipe - - -## wait... what's a "bundle"? - -We can "bundle" the `title`,`view`, and `subscriptions` functions together, -because they only need access to the current `model`. - -So _instead_ of typing out all this: - - title bigModel = - case bigModel of - FooModel model -> - foo.title model - - BarModel model -> - bar.title model - - BazModel model -> - baz.title model - - view bigModel = - case bigModel of - FooModel model -> - foo.view model - - BarModel model -> - bar.view model - - BazModel model -> - baz.view model - - subscriptions bigModel = - case bigModel of - FooModel model -> - foo.subscriptions model - - BarModel model -> - bar.subscriptions model - - BazModel model -> - baz.subscriptions model - -You only create **one** case expression: (woohoo, less typing!) - - bundle bigModel = - case bigModel of - FooModel model -> - foo.bundle model - - BarModel model -> - bar.bundle model - - BazModel model -> - baz.bundle model - - -## update helpers - -@docs keep - --} - -import Internals.Page exposing (..) -import Internals.Path as Path exposing (Path) -import Internals.Transition as Transition exposing (Transition) -import Internals.Utils as Utils - - -type alias PageContext route globalModel = - Internals.Page.PageContext route globalModel - - -type alias Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - Internals.Page.Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - - -{-| Implementing the `init`, `update` and `bundle` functions is much easier -when you turn a `Page` type into a `Recipe`. - -A `Recipe` is just an Elm record waiting for its page specific data. - - - `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 : - ((pageMsg -> layoutMsg) -> ui_pageMsg -> ui_layoutMsg) - -> Upgrade route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - -> Recipe route pageParams pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -recipe = - Internals.Page.upgrade - - -{-| If the `update` function receives a `msg` that doesn't -match up its `model`, we use `keep` to leave the page as-is. - - update : Msg -> Model -> Spa.Update Model Msg - update bigMsg bigModel = - case ( bigMsg, bigModel ) of - ( TopMsg msg, TopModel model ) -> - top.update msg model - - ( CounterMsg msg, CounterModel model ) -> - counter.update msg model - - ( NotFoundMsg msg, NotFoundModel model ) -> - notFound.update msg model - - _ -> - Page.keep bigModel - --} -keep : - layoutModel - -> Update route layoutModel layoutMsg globalModel globalMsg -keep model = - always ( model, Cmd.none, Cmd.none ) - - -{-| - - -## an example - - page = - Page.static - { title = always title - , view = always view - } - - title : String - title = - "Example" - - view : Html Never - view = - h1 [ class "title" ] [ text "Example" ] - --} -static : - { title : { global : globalModel } -> String - , view : PageContext route globalModel -> ui_pageMsg - } - -> Page route pageParams () Never ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -static page = - Page - (\{ toModel, toMsg, map } -> - { init = \_ _ -> ( toModel (), Cmd.none, Cmd.none ) - , update = \_ model _ -> ( toModel model, Cmd.none, Cmd.none ) - , bundle = - \_ private context -> - { title = - page.title - { global = context.global - } - , view = - page.view - context - |> map toMsg - |> private.map private.fromPageMsg - , subscriptions = Sub.none - } - } - ) - - - --- SANDBOX - - -{-| - - -## an example - - page = - Page.sandbox - { title = always title - , init = always init - , update = always update - , view = always view - } - - title : String - title = - "Counter" - - type alias Model = - Int - - init : Model - init = - 0 - - type Msg - = Increment - | Decrement - - update : Msg -> Model -> Model - update msg model = - case msg of - Increment -> - model + 1 - - Decrement -> - model - 1 - - view : Model -> Html Msg - view model = - div [] - [ button [ Events.onClick Increment ] [ text "+" ] - , text (String.fromInt model) - , button [ Events.onClick Decrement ] [ text "-" ] - ] - --} -sandbox : - { title : { global : globalModel, model : pageModel } -> String - , init : PageContext route globalModel -> pageParams -> pageModel - , update : PageContext route globalModel -> pageMsg -> pageModel -> pageModel - , view : PageContext route globalModel -> pageModel -> ui_pageMsg - } - -> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -sandbox page = - Page - (\{ toModel, toMsg, map } -> - { init = - \pageParams context -> - ( toModel (page.init context pageParams) - , Cmd.none - , Cmd.none - ) - , update = - \msg model context -> - ( page.update context msg model - |> toModel - , Cmd.none - , Cmd.none - ) - , bundle = - \model private context -> - { title = - page.title - { global = context.global - , model = model - } - , view = - page.view context model - |> map toMsg - |> private.map private.fromPageMsg - , subscriptions = Sub.none - } - } - ) - - - --- ELEMENT - - -{-| - - -## an example - - page = - Page.element - { title = always title - , init = always init - , update = always update - , subscriptions = always subscriptions - , view = always view - } - - title : String - title = - "Cat Gifs" - - init : ( Model, Cmd.none ) - init = - -- ... - - update : Msg -> Model -> ( Model, Cmd Msg ) - update msg model = - -- ... - - subscriptions : Model -> Sub Msg - subscriptions model = - -- ... - - view : Model -> Html Msg - view model = - -- ... - --} -element : - { title : { global : globalModel, model : pageModel } -> String - , init : PageContext route globalModel -> pageParams -> ( pageModel, Cmd pageMsg ) - , update : PageContext route globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg ) - , view : PageContext route globalModel -> pageModel -> ui_pageMsg - , subscriptions : PageContext route globalModel -> pageModel -> Sub pageMsg - } - -> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -element page = - Page - (\{ toModel, toMsg, map } -> - { init = - \pageParams context -> - page.init context pageParams - |> tuple toModel toMsg - , update = - \msg model context -> - page.update context msg model - |> tuple toModel toMsg - , bundle = - \model private context -> - { title = - page.title - { global = context.global - , model = model - } - , view = - page.view context model - |> map toMsg - |> private.map private.fromPageMsg - , subscriptions = - page.subscriptions context model - |> Sub.map (toMsg >> private.fromPageMsg) - } - } - ) - - - --- COMPONENT - - -{-| - - -## an example - - page = - Page.component - { title = always title - , init = always init - , update = always update - , subscriptions = always subscriptions - , view = view -- no always used here, so view - -- has access to `PageContext` - } - - title : String - title = - "Sign in" - - init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg ) - init params = - -- ... - - update : Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) - update msg model = - -- ... - - subscriptions : Model -> Sub Msg - subscriptions model = - -- ... - - view : Spa.PageContext -> Model -> Html Msg - view { global } model = - case global.user of - SignedIn user -> - viewSignOutForm user model - - SignedOut -> - viewSignInForm model - --} -component : - { title : { global : globalModel, model : pageModel } -> String - , init : PageContext route globalModel -> pageParams -> ( pageModel, Cmd pageMsg, Cmd globalMsg ) - , update : PageContext route globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg, Cmd globalMsg ) - , view : PageContext route globalModel -> pageModel -> ui_pageMsg - , subscriptions : PageContext route globalModel -> pageModel -> Sub pageMsg - } - -> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -component page = - Page - (\{ toModel, toMsg, map } -> - { init = - \pageParams context -> - page.init context pageParams - |> truple toModel toMsg - , update = - \msg model context -> - page.update context msg model - |> truple toModel toMsg - , bundle = - \model private context -> - { title = - page.title - { global = context.global - , model = model - } - , view = - page.view context model - |> map toMsg - |> private.map private.fromPageMsg - , subscriptions = - page.subscriptions context model - |> Sub.map (toMsg >> private.fromPageMsg) - } - } - ) - - -{-| A utility for sending `Global.Msg` commands from your `Page.component` - - init : Params.SignIn -> ( Model, Cmd Msg, Cmd Global.Msg ) - init params = - ( model - , Cmd.none - , Page.send (Global.NavigateTo routes.dashboard) - ) - --} -send : msg -> Cmd msg -send = - Utils.send - - - --- LAYOUT - - -{-| In practice, we wrap `layout` in `Utils/Spa.elm` so we only have to provide `Html.map` or `Element.map` once) - - import Utils.Spa as Spa - - page = - Spa.layout - { layout = Layout.view - , pages = - { init = init - , update = update - , bundle = bundle - } - } - --} -layout : - ((pageMsg -> msg) -> ui_pageMsg -> ui_msg) - -> Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg - -> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg -layout map options = - Page - (\{ toModel, toMsg } -> - { init = - \pageParams global -> - options.recipe.init pageParams global - |> truple toModel toMsg - , update = - \msg model global -> - options.recipe.update msg model global - |> truple toModel toMsg - , bundle = - \model private context -> - let - viewLayout page = - options.view - { page = page - , global = context.global - , fromGlobalMsg = private.fromGlobalMsg - , route = context.route - } - - myLayoutsVisibility : Transition.Visibility - myLayoutsVisibility = - if private.path == options.path then - private.visibility - - else - Transition.visible - - bundle : { title : String, view : ui_msg, subscriptions : Sub msg } - bundle = - options.recipe.bundle - model - { fromGlobalMsg = private.fromGlobalMsg - , fromPageMsg = toMsg >> private.fromPageMsg - , map = map - , path = private.path - , transitions = private.transitions - , visibility = private.visibility - } - context - - lookupTransitionFrom : - Path - -> List { path : Path, transition : Transition ui_msg } - -> Transition ui_msg - lookupTransitionFrom path list = - list - |> List.filter (.path >> (==) path) - |> List.map .transition - |> List.head - |> Maybe.withDefault Transition.optOut - in - { title = bundle.title - , view = - viewLayout <| - Transition.view - (lookupTransitionFrom options.path private.transitions) - myLayoutsVisibility - bundle.view - , subscriptions = bundle.subscriptions - } - } - ) - - - --- UTILS - - -tuple : - (model -> bigModel) - -> (msg -> bigMsg) - -> ( model, Cmd msg ) - -> ( bigModel, Cmd bigMsg, Cmd a ) -tuple toModel toMsg ( model, cmd ) = - ( toModel model - , Cmd.map toMsg cmd - , Cmd.none - ) - - -truple : - (model -> bigModel) - -> (msg -> bigMsg) - -> ( model, Cmd msg, Cmd a ) - -> ( bigModel, Cmd bigMsg, Cmd a ) -truple toModel toMsg ( a, b, c ) = - ( toModel a, Cmd.map toMsg b, c ) diff --git a/src/Spa/Path.elm b/src/Spa/Path.elm deleted file mode 100644 index a9d74cf..0000000 --- a/src/Spa/Path.elm +++ /dev/null @@ -1,75 +0,0 @@ -module Spa.Path exposing - ( Path - , static, dynamic - ) - -{-| - - -## Modify transitions at different routes! - -If you're using the [CLI companion tool](https://github.com/ryannhg/elm-spa/tree/master/cli), -these are **automatically generated**. - -(So feel free to ignore these docs!) - -If you're doing things by hand, this documentation might be helpful! - -@docs Path - -@docs static, dynamic - --} - -import Internals.Path as Internals - - -{-| a `List` of path segments that you use with `Spa.Transition` - - transitions : Spa.Transitions (Element msg) - transitions = - { layout = Transition.none - , page = Transition.none - , pages = - [ -- applies fade to all pages under `/guide/*` - { path = [ static "guide" ] - , transition = Transition.fadeElmUi 300 - } - ] - } - --} -type alias Path = - List Internals.Piece - - -{-| A static segment of a path. - - [ static "docs" ] - -- /docs - - [ static "docs", static "intro" ] - -- /docs/intro - --} -static : String -> Internals.Piece -static = - Internals.static - - -{-| A dynamic segment of a path. - - [ static "docs", dynamic ] - -- /docs/welcome - -- /docs/hello - -- /docs/hooray - - [ static "docs", dynamic, static "intro" ] - -- /docs/welcome/intro - -- /docs/hello/intro - -- /docs/hooray/intro - --} -dynamic : Internals.Piece -dynamic = - Internals.dynamic diff --git a/src/Spa/Transition.elm b/src/Spa/Transition.elm deleted file mode 100644 index f817c82..0000000 --- a/src/Spa/Transition.elm +++ /dev/null @@ -1,173 +0,0 @@ -module Spa.Transition exposing - ( Transition - , none, fadeElmUi, fadeHtml - , custom - ) - -{-| - - -## Create transitions from page to page! - -A huge benefit to doing client-side rendering is the ability to -seamlessly navigate from one page to another! - -This package is designed to make creating page transitions a breeze! - -@docs Transition - - -## Use one of these transitions - -@docs none, fadeElmUi, fadeHtml - - -## Or create your own - -@docs custom - --} - -import Element exposing (Element) -import Html exposing (Html) -import Internals.Transition - - -{-| Describes how to move from one page to another. - - transition : Transition (Html msg) - transition = - Transition.none - - otherTransition : Transition (Element msg) - otherTransition = - Transition.fadeElmUi 300 - --} -type alias Transition ui_msg = - Internals.Transition.Transition ui_msg - - - --- TRANSITIONS - - -{-| Don't transition from one page to another. - -Can be used with `Html msg` or `Element msg` (or another view library) - - transitions : Transitions (Html msg) - transitions = - { layout = Transition.none -- page loads instantly - , page = Transition.fadeHtml 300 - , pages = [] - } - - otherTransitions : Transitions (Element msg) - otherTransitions = - { layout = Transition.none -- page loads instantly - , page = Transition.fadeElmUi 300 - , pages = [] - } - --} -none : Transition ui_msg -none = - Internals.Transition.none - - -{-| Fade one page out and another one in. (For use with `elm/html`) - -Animation duration is represented in **milliseconds** - - transitions : Spa.Types.Transitions (Html msg) - transitions = - { layout = Transition.none - , page = Transition.fadeHtml 300 -- 300 milliseconds - , pages = [] - } - --} -fadeHtml : Int -> Transition (Html msg) -fadeHtml = - Internals.Transition.fadeHtml - - -{-| Fade one page out and another one in. (For use with `mdgriffith/elm-ui`) - -Animation duration is represented in **milliseconds** - - transitions : Spa.Types.Transitions (Element msg) - transitions = - { layout = Transition.none - , page = Transition.fadeElmUi 300 -- 300 milliseconds - , pages = [] - } - --} -fadeElmUi : Int -> Transition (Element msg) -fadeElmUi = - Internals.Transition.fadeElmUi - - -{-| Create your own custom transition! - -Just provide three things: - - - `duration` – how long (in milliseconds) the transition should last. - - - `invisible` – what the page looks like when **invisible**. - - - `visible` – what the page looks like when **visible**. - -``` -batmanNewspaper : Int -> Transition (Element msg) -batmanNewspaper duration = - Transition.custom - { duration = duration - , invisible = - \page -> - el - [ alpha 0 - , width fill - , rotate (4 * pi) - , scale 0 - , Styles.transition - { property = "all" - , duration = duration - } - ] - page - , visible = - \page -> - el - [ alpha 1 - , width fill - , Styles.transition - { property = "all" - , duration = duration - } - ] - page - } - --- --- using it later on --- -transitions : Spa.Types.Transitions (Element msg) -transitions = - { layout = batmanNewspaper 500 -- 🦇 - , page = Transition.none - , pages = [] - } -``` - --} -custom : - { duration : Int - , invisible : ui_msg -> ui_msg - , visible : ui_msg -> ui_msg - } - -> Transition ui_msg -custom = - Internals.Transition.custom diff --git a/src/Spa/Types.elm b/src/Spa/Types.elm deleted file mode 100644 index f10b80f..0000000 --- a/src/Spa/Types.elm +++ /dev/null @@ -1,430 +0,0 @@ -module Spa.Types exposing - ( Page - , Recipe - , Init - , Update - , Bundle - , Layout, Upgrade - , Transitions - , LayoutContext, PageContext - ) - -{-| - - -## Types so spooky, they got their own module! - -This module is all about exposing the types that `ryannhg/elm-spa` uses -under the hood. - -You might notice that there are a **lot of generic types**. - -In practice, we can avoid the messy types with a single -[`Utils/Spa.elm`](https://github.com/ryannhg/elm-spa/blob/master/example/src/Utils/Spa.elm) file that -makes your types easier to understand! - -`elm-spa init` will generate that file for you, but I've added examples below if you're -doing things by hand. - - -# page - -@docs Page - - -# recipe - -@docs Recipe - - -# init - -@docs Init - - -# update - -@docs Update - - -# bundle - -@docs Bundle - - -# layouts and recipes - -@docs Layout, Upgrade - - -# transitions - -@docs Transitions - - -# context - -@docs LayoutContext, PageContext - --} - -import Dict exposing (Dict) -import Internals.Page as Page -import Internals.Path exposing (Path) -import Internals.Transition exposing (Transition) - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - -- if using mdgriffith/elm-ui - - import Spa.Types - import Element exposing (Element) - - type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -- if using elm/html - - import Spa.Types - import Html exposing (Html) - - type alias Page params model msg layoutModel layoutMsg appMsg = - Spa.Types.Page Route params model msg (Html msg) layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -## using your alias - -**`src/Pages/Example.elm`** - - import Utils.Spa as Spa - - page : Spa.Page Params.Example Model Msg model msg appMsg - page = - Spa.Page.static { ... } - --} -type alias Page route params pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - Page.Page route params pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - -- if using mdgriffith/elm-ui - - import Spa.Types - import Element exposing (Element) - - type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -- if using elm/html - - import Spa.Types - import Html exposing (Html) - - type alias Recipe params model msg layoutModel layoutMsg appMsg = - Spa.Types.Recipe Route params model msg layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -## using your alias - -**`.elm-spa/Generated/Pages.elm`** - - import Utils.Spa as Spa - - type alias Recipes appMsg = - { top : Spa.Recipe Params.Top Top.Model Top.Msg Model Msg appMsg - , example : Spa.Recipe Params.Example Example.Model Example.Msg Model Msg appMsg - , notFound : Spa.Recipe Params.NotFound NotFound.Model NotFound.Msg Model Msg appMsg - } - --} -type alias Recipe route params pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - Page.Recipe route params pageModel pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - type alias Init model msg = - Spa.Types.Init Route model msg Global.Model Global.Msg - - -## using your alias - -**`.elm-spa/Generated/Pages.elm`** - - import Utils.Spa as Spa - - init : Route -> Spa.Init Model Msg - init route_ = - case route_ of - -- ... - --} -type alias Init route layoutModel layoutMsg globalModel globalMsg = - Page.Init route layoutModel layoutMsg globalModel globalMsg - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - type alias Update model msg = - Spa.Types.Update Route model msg Global.Model Global.Msg - - -## using your alias - -**`.elm-spa/Generated/Pages.elm`** - - import Utils.Spa as Spa - - update : Msg -> Model -> Spa.Update Model Msg - update msg_ model_ = - case ( msg_, model_ ) of - -- ... - --} -type alias Update route layoutModel layoutMsg globalModel globalMsg = - Page.Update route layoutModel layoutMsg globalModel globalMsg - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - -- if using mdgriffith/elm-ui - - import Spa.Types - import Element exposing (Element) - - type alias Bundle msg appMsg = - Spa.Types.Bundle Route msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - -- if using elm/html - - import Spa.Types - import Html exposing (Html) - - type alias Bundle msg appMsg = - Spa.Types.Bundle Route 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 Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - Page.Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - -- if using mdgriffith/elm-ui - - import Spa.Types - import Element exposing (Element) - - type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Element msg) Global.Model Global.Msg appMsg (Element appMsg) - - - -- if using elm/html - - import Spa.Types - import Html exposing (Html) - - type alias Layout params model msg appMsg = - Spa.Types.Layout Route params model msg (Html msg) Global.Model Global.Msg appMsg (Html appMsg) - - -## using your alias - -**`src/Utils/Spa.elm`** - - layout : - Layout params model msg appMsg - -> Page params model msg layoutModel layoutMsg appMsg - layout = - Spa.Page.layout Element.map - --} -type alias Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg = - Page.Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg - - -{-| Describes how to transition between layouts and pages. - - transitions : Transitions (Html msg) - transitions = - { layout = Transition.none -- page loads instantly - , page = Transition.fadeHtml 300 - , pages = [] - } - --} -type alias Transitions ui_msg = - { layout : Transition ui_msg - , page : Transition ui_msg - , pages : - List - { path : Path - , transition : Transition ui_msg - } - } - - -{-| This is what your `src/Pages/*.elm` files can access! - - -## creating your alias - -**`src/Utils/Spa.elm`** - - type alias PageContext = - Spa.Types.PageContext Route Global.Model - - -## using your alias - -**`src/Pages/Top.elm`** - - import Utils.Spa as Spa - - page = - Page.static - { title = always "Homepage" - , view = view -- leaving off always here! - } - - view : PageContext -> Html Msg - view context = - case context.global.user of - SignedIn user -> - viewUser user - - SignedOut -> - text "Who dis?" - --} -type alias PageContext route globalModel = - { global : globalModel - , route : route - , queryParameters : Dict String String - } - - -{-| This is what your `src/Layouts/*.elm` files can access! - - -## creating your alias - -**`src/Utils/Spa.elm`** - - type alias LayoutContext msg = - Spa.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg - - -## using your alias - -**`src/Layout.elm`** - - import Utils.Spa as Spa - - view : Spa.LayoutContext msg -> Html msg - view { page, fromGlobalMsg, global } = - div [ class "app" ] - [ Html.map fromGlobalMsg (viewNavbar global) - , page - , viewFooter - ] - - viewNavbar : Global.Model -> Html Global.Msg - viewNavbar = - -- ... - - viewFooter : Html msg - viewFooter = - -- ... - --} -type alias LayoutContext route msg ui_msg globalModel globalMsg = - { page : ui_msg - , route : route - , global : globalModel - , fromGlobalMsg : globalMsg -> msg - } - - -{-| - - -## creating your alias - -**`src/Utils/Spa.elm`** - - -- if using mdgriffith/elm-ui - - import Spa.Types - import Element exposing (Element) - - type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg) - - -- if using elm/html - - import Spa.Types - import Html exposing (Html) - - type alias Upgrade params model msg layoutModel layoutMsg appMsg = - Spa.Types.Upgrade Route params model msg (Html msg) layoutModel layoutMsg (Html layoutMsg) Global.Model Global.Msg appMsg (Html appMsg) - - -## using your alias - -**`src/Utils/Spa.elm`** - - recipe : - Upgrade params model msg layoutModel layoutMsg appMsg - -> Recipe params model msg layoutModel layoutMsg appMsg - recipe = - Spa.Page.recipe Element.map - --} -type alias Upgrade route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg = - Page.Upgrade route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg