├── .gitignore ├── LICENSE ├── README.md ├── docs ├── README.md ├── elm.json ├── netlify.toml ├── public │ ├── content │ │ ├── examples.md │ │ ├── examples │ │ │ ├── 01-hello-world.md │ │ │ ├── 02-pages.md │ │ │ ├── 03-storage.md │ │ │ └── 04-authentication.md │ │ ├── guide.md │ │ ├── guide │ │ │ ├── 01-cli.md │ │ │ ├── 02-routing.md │ │ │ ├── 03-pages.md │ │ │ ├── 04-requests.md │ │ │ ├── 05-shared-state.md │ │ │ └── 06-views.md │ │ └── images │ │ │ ├── 01-hello-world.png │ │ │ ├── 02-pages.png │ │ │ ├── 03-storage.png │ │ │ ├── 04-authentication.png │ │ │ ├── community │ │ │ └── 01-elmcss-patterns.png │ │ │ ├── realworld.png │ │ │ └── this-site.png │ ├── favicon.png │ ├── images │ │ ├── icons │ │ │ ├── brain.svg │ │ │ ├── laptop.svg │ │ │ ├── lock.svg │ │ │ └── magic.svg │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── outlined-to-edge.png │ │ ├── rounded-logo-bg.png │ │ └── screenshot.png │ ├── index.html │ ├── main.js │ ├── robots.txt │ ├── sitemap.xml │ ├── style.css │ └── vendor │ │ ├── prism.css │ │ └── prism.js ├── scripts │ └── generate-index.js └── src │ ├── Domain │ └── Index.elm │ ├── Main.elm │ ├── Pages │ ├── Examples.elm │ ├── Examples │ │ └── Section_.elm │ ├── Guide.elm │ ├── Guide │ │ └── Section_.elm │ ├── Home_.elm │ └── NotFound.elm │ ├── Ports.elm │ ├── Shared.elm │ ├── UI.elm │ ├── UI │ ├── Docs.elm │ ├── Layout.elm │ ├── Searchbar.elm │ └── Sidebar.elm │ └── Utils │ └── String.elm ├── elm.json ├── examples ├── 01-hello-world │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ │ └── index.html │ └── src │ │ └── Pages │ │ └── Home_.elm ├── 02-pages │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ │ ├── index.html │ │ └── style.css │ └── src │ │ ├── Pages │ │ ├── Advanced.elm │ │ ├── Dynamic │ │ │ └── Name_.elm │ │ ├── Element.elm │ │ ├── Home_.elm │ │ ├── Sandbox.elm │ │ └── Static.elm │ │ ├── Shared.elm │ │ └── UI.elm ├── 03-local-storage │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ │ ├── index.html │ │ └── main.js │ └── src │ │ ├── Pages │ │ └── Home_.elm │ │ ├── Shared.elm │ │ └── Storage.elm ├── 04-authentication │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ │ ├── index.html │ │ └── main.js │ └── src │ │ ├── Auth.elm │ │ ├── Domain │ │ └── User.elm │ │ ├── Pages │ │ ├── Home_.elm │ │ └── SignIn.elm │ │ ├── Shared.elm │ │ ├── Storage.elm │ │ └── UI.elm ├── 05-vite │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── main.js │ ├── src │ │ └── Pages │ │ │ └── Home_.elm │ └── vite.config.js ├── 06-testing │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ │ └── index.html │ ├── src │ │ ├── Pages │ │ │ └── Home_.elm │ │ └── Utils │ │ │ └── String.elm │ └── tests │ │ ├── ProgramTests │ │ └── Homepage.elm │ │ └── UnitTests │ │ └── Utils │ │ └── StringTest.elm └── 07-elm-ui │ ├── .gitignore │ ├── README.md │ ├── elm.json │ ├── public │ └── index.html │ └── src │ ├── Pages │ └── Home_.elm │ └── View.elm └── src ├── ElmSpa ├── Page.elm └── Request.elm └── cli ├── .npmignore ├── README.md ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── cli.ts ├── cli │ ├── _common.ts │ ├── add.ts │ ├── build.ts │ ├── help.ts │ ├── init.ts │ ├── server.ts │ └── watch.ts ├── config.ts ├── defaults │ ├── Auth.elm │ ├── Effect.elm │ ├── Main.elm │ ├── Pages │ │ └── NotFound.elm │ ├── Shared.elm │ └── View.elm ├── file.ts ├── index.ts ├── new │ ├── README.md │ ├── _gitignore │ ├── elm.json │ ├── public │ │ └── index.html │ └── src │ │ └── Pages │ │ └── Home_.elm ├── templates │ ├── add.ts │ ├── add │ │ ├── advanced.elm │ │ ├── element.elm │ │ ├── sandbox.elm │ │ └── static.elm │ ├── model.ts │ ├── msg.ts │ ├── page.ts │ ├── pages.ts │ ├── params.ts │ ├── request.ts │ ├── routes.ts │ └── utils.ts ├── terminal.ts └── types.ts ├── tests ├── file.spec.ts └── templates │ └── utils.spec.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .elm-spa 3 | elm-stuff 4 | node_modules 5 | dist 6 | 7 | # Local Netlify folder 8 | .netlify -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present, Ryan Haskell-Glatz 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Ryan Haskell-Glatz nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [](https://elm-spa.dev) 2 | 3 | # **Installation** 4 | 5 | ```bash 6 | npm install -g elm-spa@latest 7 | ``` 8 | 9 | # **Quick start** 10 | 11 | ## **1. Create a new project** 12 | 13 | ```bash 14 | npx elm-spa new 15 | ``` 16 | 17 | ## **2. Check out the new files** 18 | 19 | ```bash 20 | your-new-project/ 21 | - elm.json 22 | - src/Pages/Home_.elm 23 | - public/index.html 24 | ``` 25 | 26 | ## **3. Run it in your browser** 27 | 28 | ```bash 29 | npx elm-spa server # Ready at http://localhost:1234 30 | ``` 31 | 32 | # **Learn more** 33 | 34 | __Visit the official site__ at [elm-spa.dev](https://elm-spa.dev) for more examples, guides, and other documentation. 35 | 36 | ### **Do I need the Elm package?** 37 | 38 | If you are using elm-spa, there's no need to read the [ryan-haskell/elm-spa](https://package.elm-lang.org/packages/ryan-haskell/elm-spa/latest/) package documentation. The package only exists to constrain the CLI, and provides a few basic internal helper functions. 39 | 40 | Check out [the official website](https://elm-spa.dev) instead! 41 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # elm-spa.dev 2 | > 🌳 built with [elm-spa](https://elm-spa.dev) 3 | 4 |  5 | 6 | ## dependencies 7 | 8 | This project requires the latest LTS version of [Node.js](https://nodejs.org/) 9 | 10 | ```bash 11 | npm install -g elm-spa 12 | ``` 13 | 14 | ## running locally 15 | 16 | ```bash 17 | elm-spa server # starts this app at http:/localhost:1234 18 | ``` 19 | 20 | ### other commands 21 | 22 | ```bash 23 | elm-spa add # add a new page to the application 24 | elm-spa build # one-time production build 25 | elm-spa watch # builds code as you go (without the server) 26 | ``` 27 | 28 | ## learn more 29 | 30 | You can learn more at [elm-spa.dev](https://elm-spa.dev) 31 | 32 | -------------------------------------------------------------------------------- /docs/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | ".elm-spa/defaults", 6 | ".elm-spa/generated" 7 | ], 8 | "elm-version": "0.19.1", 9 | "dependencies": { 10 | "direct": { 11 | "dillonkearns/elm-markdown": "5.1.1", 12 | "elm/browser": "1.0.2", 13 | "elm/core": "1.0.5", 14 | "elm/html": "1.0.0", 15 | "elm/http": "2.0.0", 16 | "elm/json": "1.1.3", 17 | "elm/url": "1.0.0", 18 | "ryan-haskell/elm-spa": "1.0.0" 19 | }, 20 | "indirect": { 21 | "elm/bytes": "1.0.8", 22 | "elm/file": "1.0.5", 23 | "elm/parser": "1.1.0", 24 | "elm/regex": "1.0.0", 25 | "elm/time": "1.0.0", 26 | "elm/virtual-dom": "1.0.2", 27 | "rtfeldman/elm-hex": "1.0.0" 28 | } 29 | }, 30 | "test-dependencies": { 31 | "direct": {}, 32 | "indirect": {} 33 | } 34 | } -------------------------------------------------------------------------------- /docs/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "npm i elm@latest && node scripts/generate-index.js && npx elm-spa build" 4 | 5 | # Prevents missing markdown files from redirecting to index.html 6 | [[redirects]] 7 | from = "/content/*" 8 | to = "/content/:splat" 9 | status = 200 10 | force = true 11 | 12 | [[redirects]] 13 | from = "/*" 14 | to = "/index.html" 15 | status = 200 16 | -------------------------------------------------------------------------------- /docs/public/content/examples.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Prefer to learn by example? Wonderful! The source code for all of the examples on this site can be found in the GitHub repo's [examples](https://github.com/ryan-haskell/elm-spa/tree/main/examples) folder. 4 | 5 | ### Hello, world! 6 | 7 | Get an introduction to the framework with a simple app. 8 | 9 | [](/examples/01-hello-world) 10 | 11 | ### Pages 12 | 13 | Learn how pages and URL routing work together. 14 | 15 | [](/examples/02-pages) 16 | 17 | ### Local storage 18 | 19 | Use ports and local storage to persist data on refresh. 20 | 21 | [](/examples/03-storage) 22 | 23 | ### User authentication 24 | 25 | Explore the elm-spa's user authentication API. 26 | 27 | [](/examples/04-authentication) 28 | 29 | ## Real world examples 30 | 31 | ### elm-css patterns 32 | 33 | _Author_: [@bigardone](https://github.com/bigardone) 34 | 35 | [](https://elmcsspatterns.io/) 36 | 37 | Source code: [GitHub](https://github.com/bigardone/elm-css-patterns) 38 | 39 | ### RealWorld Conduit App 40 | 41 | _Author_: [@ryan-haskell](https://github.com/ryan-haskell) 42 | 43 | Implements the [RealWorld app](https://github.com/gothinkster/realworld), inspired by Richard Feldman's "elm-spa-example" project. 44 | 45 | [](https://realworld.elm-spa.dev) 46 | 47 | Source code: [GitHub](https://github.com/ryan-haskell/elm-spa-realworld) 48 | 49 | ### This website 50 | 51 | _Author_: [@ryan-haskell](https://github.com/ryan-haskell) 52 | 53 | The website you are looking at _right now_ was built with __elm-spa__. Mindbending, right? 54 | 55 | [](https://elm-spa.dev) 56 | 57 | Source code: [GitHub](https://github.com/ryan-haskell/elm-spa/tree/main/docs) 58 | 59 | ## More examples 60 | 61 | There are more examples available on the official repo: 62 | 63 | __[Working with NPM 🔗](https://github.com/ryan-haskell/elm-spa/tree/main/examples/05-vite)__ 64 | 65 | Use [Vite](https://vitejs.dev/) instead of the default __elm-spa server__ command. This gives you access to NPM, reading environment variables, and fancier JS ecosystem stuff. 66 | 67 | __[Testing 🔗](https://github.com/ryan-haskell/elm-spa/tree/main/examples/06-testing)__ 68 | 69 | Use [elm-test](https://github.com/elm-explorations/test) and [elm-program-test](https://elm-program-test.netlify.app/) to write unit and end-to-end tests for your single page application. 70 | 71 | __[Using Elm UI 🔗](https://github.com/ryan-haskell/elm-spa/tree/main/examples/07-elm-ui)__ 72 | 73 | Use the wonderful [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest) package to create web UIs without the need for HTML or CSS. This example can also be applied to [elm-css](https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/) or any other custom UI of your choice. -------------------------------------------------------------------------------- /docs/public/content/examples/01-hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello, world! 2 | 3 | __Source code__: [GitHub](https://github.com/ryan-haskell/elm-spa/tree/main/examples/01-hello-world) 4 | 5 | Welcome to __elm-spa__! This guide is a breakdown of the simplest project you can make: the "Hello, world!" example. 6 | 7 | ### Installation 8 | 9 | In case you are starting from scratch, you can install __elm-spa__ via NPM: 10 | 11 | ```terminal 12 | npm install -g elm-spa@latest 13 | ``` 14 | 15 | ### Creating a project 16 | 17 | This will allow you to create a new project using the following commands: 18 | 19 | ```terminal 20 | elm-spa new 21 | ``` 22 | 23 | 24 | 25 | 26 | When we ran `elm-spa new`, only __three__ files were created: 27 | 28 | - __public/index.html__ - the entrypoint for our web app. 29 | - __src/Pages/Home\_.elm__ - the homepage. 30 | - __elm.json__ - our project dependencies. 31 | 32 | ### Running the server 33 | 34 | With only these files, we can get an application up-and-running: 35 | 36 | ```terminal 37 | elm-spa server 38 | ``` 39 | 40 | This runs a server at [http://localhost:1234](http://localhost:1234). If everything worked, you should see this in your browser: 41 | 42 |  43 | 44 | 45 | ### The entrypoint 46 | 47 | Earlier, I mentioned that `public/index.html` was the "entrypoint" to our web app. Let's take a look at that file: 48 | 49 | ```html 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | This HTML file defines some standard tags, and then runs our Elm application. Because our Elm compiles to JavaScript, the `elm-spa server` command generates a `/dist/elm.js` file anytime we make changes. 64 | 65 | Once we import that with a ` 26 | 27 | 28 | 29 |